Commit 56e64d9a

stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
2024-09-27 01:25:28
feat(api): add omni-moderation model (#1750)
1 parent 9feadd8
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 .._response import to_streamed_response_wrapper, async_to_streamed_response
 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 @@ class Moderations(SyncAPIResource):
     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 @@ class Moderations(SyncAPIResource):
         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 @@ class AsyncModerations(AsyncAPIResource):
     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 @@ class AsyncModerations(AsyncAPIResource):
         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
 
src/openai/types/__init__.py
@@ -46,4 +46,7 @@ from .completion_create_params import CompletionCreateParams as CompletionCreate
 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
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."""
 
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).
     """
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`."""
src/openai/types/moderation_model.py
@@ -4,4 +4,6 @@ from typing_extensions import Literal, TypeAlias
 
 __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"
+]
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]
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`."""
tests/api_resources/test_moderations.py
@@ -28,7 +28,7 @@ class TestModerations:
     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 @@ class TestAsyncModerations:
     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"])
 
.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
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: