diff --git a/.castiron.stats.yml b/.castiron.stats.yml index 327037823..516b8e5e0 100644 --- a/.castiron.stats.yml +++ b/.castiron.stats.yml @@ -1,8 +1,8 @@ schema_version: 1 -generation_id: 8e5ebfdb-d6fd-41d4-9173-a575e6ba13fc -openapi_spec_hash: 8c73dea75f8570b7ea967542dafa0b0f -openapi_transformed_spec_hash: 19d647b4e088bfa2499083e65eacd54e -config_hash: ad0fbf2fbcf7981fe7a9910664285991 -codegen_sha: 9244e44f047f4d2a30e58bf5423011673623ce81 -codegen_hash: 391329cd5b43dc65c96676993a33fa22103908dd8b8e2f3cf461a837b31df84b -public_codegen_sha: 1252578bddc8b8637004578d7a16f3c8ee7ee22e +generation_id: cf94c82f-0604-4dfc-aff0-cec482e84953 +openapi_spec_hash: ce912f79d7f8cb19a7a515513c214b8a +openapi_transformed_spec_hash: 352b5a7a028f990b6475b6593e381eee +config_hash: dde98d9c41cd6f4163320f92c70f83e6 +codegen_sha: 326831af0c0fdf859335a189df2b6716e4b605d4 +codegen_hash: 00e7f6a53e1a6a1adf02385bc368fce999fdd775d1f1c28615f790c7a4109ad0 +public_codegen_sha: c2abe7c282c83c8fb9a7330254235c20b67a2b10 diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml index f8d2c4599..30f3e6581 100644 --- a/api_reference/openapi.transformed.yml +++ b/api_reference/openapi.transformed.yml @@ -601,64 +601,11 @@ paths: schema: type: string format: binary - audio/mpeg: - schema: - type: string - format: binary - audio/aac: - schema: - type: string - format: binary - audio/opus: - schema: - type: string - format: binary - audio/flac: - schema: - type: string - format: binary - audio/pcm: - schema: - type: string - format: binary - audio/wav: - schema: - type: string - format: binary text/event-stream: schema: $ref: '#/components/schemas/CreateSpeechResponseStreamEvent' - '400': - description: Invalid speech request, input, output format, or voice. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - text/plain: - schema: - type: string - description: JSON-encoded error text containing an error object and the HTTP status. - '403': - description: Access to a personal API organization is blocked by the organization policy. - content: - text/plain: - schema: - type: string - description: JSON-encoded error text containing an error object and the HTTP status. '429': $ref: '#/components/responses/InferenceRateLimited' - '500': - description: The voice or speech audio could not be processed. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' '503': $ref: '#/components/responses/InferenceServiceUnavailable' x-oaiMeta: @@ -747,8 +694,7 @@ paths: Transcribes audio into the input language. Returns a transcription object in `json`, `diarized_json`, or `verbose_json` - format, plain text in `text`, `srt`, or `vtt` format, or a stream of transcript - events. Supported formats depend on the model. + format, or a stream of transcript events. requestBody: required: true content: @@ -764,58 +710,16 @@ paths: anyOf: - $ref: '#/components/schemas/CreateTranscriptionResponseJson' - $ref: '#/components/schemas/CreateTranscriptionResponseDiarizedJson' + x-stainless-skip: + - go - $ref: '#/components/schemas/CreateTranscriptionResponseVerboseJson' discriminator: propertyName: task - text/plain: - schema: - type: string text/event-stream: schema: $ref: '#/components/schemas/CreateTranscriptionResponseStreamEvent' - '400': - description: Invalid audio input or request parameters. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - text/plain: - schema: - type: string - description: JSON-encoded error text containing an error object and the HTTP status. - '403': - description: Access to a personal API organization is blocked by the organization policy. - content: - text/plain: - schema: - type: string - description: JSON-encoded error text containing an error object and the HTTP status. - '413': - description: The audio request exceeds the supported size limit. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' '429': $ref: '#/components/responses/InferenceRateLimited' - '500': - description: The audio could not be processed. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '502': - description: The upstream audio service connection failed. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' '503': $ref: '#/components/responses/InferenceServiceUnavailable' x-oaiMeta: @@ -1367,52 +1271,8 @@ paths: - $ref: '#/components/schemas/CreateTranslationResponseVerboseJson' x-stainless-skip: - go - text/plain: - schema: - type: string - '400': - description: Invalid audio input or request parameters. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Authentication failed because the API key is missing or revoked, or the client IP is not authorized. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - text/plain: - schema: - type: string - description: JSON-encoded error text containing an error object and the HTTP status. - '403': - description: Access to a personal API organization is blocked by the organization policy. - content: - text/plain: - schema: - type: string - description: JSON-encoded error text containing an error object and the HTTP status. - '413': - description: The audio request exceeds the supported size limit. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' '429': $ref: '#/components/responses/InferenceRateLimited' - '500': - description: The audio could not be processed. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '502': - description: The upstream audio service connection failed. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' '503': $ref: '#/components/responses/InferenceServiceUnavailable' x-oaiMeta: @@ -66870,36 +66730,6 @@ components: "type": "safety.warning_issued", "data": {"id": "C-abc123"} } - _MisalignmentErrorType: - anyOf: - - type: string - - type: string - enum: - - potentially_unintended_data_transfer - - potentially_unintended_data_access - - potentially_unintended_destructive_activity - - other - _MisalignmentSteer: - properties: - message: - type: string - description: The public continuation instruction. - type: object - required: - - message - MisalignmentErrorDetailsResource: - properties: - error_type: - $ref: '#/components/schemas/_MisalignmentErrorType' - description: An optional classification; clients must accept additional values. - detailed_explanation: - type: string - description: The public explanation for this block. - steer: - $ref: '#/components/schemas/_MisalignmentSteer' - description: An optional public continuation instruction. - type: object - required: [] ModerationInputType: type: string enum: @@ -70376,6 +70206,36 @@ components: - object - metadata - created_at + _MisalignmentErrorType: + anyOf: + - type: string + - type: string + enum: + - potentially_unintended_data_transfer + - potentially_unintended_data_access + - potentially_unintended_destructive_activity + - other + _MisalignmentSteer: + properties: + message: + type: string + description: The public continuation instruction. + type: object + required: + - message + MisalignmentErrorDetailsResource: + properties: + error_type: + $ref: '#/components/schemas/_MisalignmentErrorType' + description: An optional classification; clients must accept additional values. + detailed_explanation: + type: string + description: The public explanation for this block. + steer: + $ref: '#/components/schemas/_MisalignmentSteer' + description: An optional public continuation instruction. + type: object + required: [] ImageGenOutputTokensDetails: properties: image_tokens: diff --git a/examples/audio/record_and_play.rb b/examples/audio/record_and_play.rb index a9fc3d56b..4974cf42e 100755 --- a/examples/audio/record_and_play.rb +++ b/examples/audio/record_and_play.rb @@ -14,6 +14,6 @@ client = OpenAI::Client.new clip = OpenAI::LocalAudio.record(duration: 5, timeout: 10) transcription = client.audio.transcriptions.create(model: TRANSCRIPTION_MODEL, file: clip) -puts(transcription.is_a?(StringIO) ? transcription.read : transcription.text) +puts(transcription.text) speech = client.audio.speech.create(model: SPEECH_MODEL, voice: VOICE, input: "Recording complete.") OpenAI::LocalAudio.play(speech, timeout: 30) diff --git a/lib/openai/resources/admin/organization.rb b/lib/openai/resources/admin/organization.rb index 6f76d6133..9325a4af9 100644 --- a/lib/openai/resources/admin/organization.rb +++ b/lib/openai/resources/admin/organization.rb @@ -4,6 +4,9 @@ module OpenAI module Resources class Admin class Organization + # @return [OpenAI::Resources::Admin::Organization::ExternalStorage] + attr_reader :external_storage + # List user actions and configuration changes within this organization. # @return [OpenAI::Resources::Admin::Organization::AuditLogs] attr_reader :audit_logs @@ -29,9 +32,6 @@ class Organization # @return [OpenAI::Resources::Admin::Organization::DataRetention] attr_reader :data_retention - # @return [OpenAI::Resources::Admin::Organization::ExternalStorage] - attr_reader :external_storage - # @return [OpenAI::Resources::Admin::Organization::SpendLimit] attr_reader :spend_limit @@ -49,6 +49,7 @@ class Organization # @param client [OpenAI::Client] def initialize(client:) @client = client + @external_storage = OpenAI::Resources::Admin::Organization::ExternalStorage.new(client: client) @audit_logs = OpenAI::Resources::Admin::Organization::AuditLogs.new(client: client) @admin_api_keys = OpenAI::Resources::Admin::Organization::AdminAPIKeys.new(client: client) @usage = OpenAI::Resources::Admin::Organization::Usage.new(client: client) @@ -57,7 +58,6 @@ def initialize(client:) @groups = OpenAI::Resources::Admin::Organization::Groups.new(client: client) @roles = OpenAI::Resources::Admin::Organization::Roles.new(client: client) @data_retention = OpenAI::Resources::Admin::Organization::DataRetention.new(client: client) - @external_storage = OpenAI::Resources::Admin::Organization::ExternalStorage.new(client: client) @spend_limit = OpenAI::Resources::Admin::Organization::SpendLimit.new(client: client) @spend_alerts = OpenAI::Resources::Admin::Organization::SpendAlerts.new(client: client) @certificates = OpenAI::Resources::Admin::Organization::Certificates.new(client: client) diff --git a/lib/openai/resources/audio/transcriptions.rb b/lib/openai/resources/audio/transcriptions.rb index 92c9258e5..50bdb1bba 100644 --- a/lib/openai/resources/audio/transcriptions.rb +++ b/lib/openai/resources/audio/transcriptions.rb @@ -11,8 +11,7 @@ class Transcriptions # Transcribes audio into the input language. # # Returns a transcription object in `json`, `diarized_json`, or `verbose_json` - # format, plain text in `text`, `srt`, or `vtt` format, or a stream of transcript - # events. Supported formats depend on the model. + # format, or a stream of transcript events. # # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload # metadata. Use `OpenAI::FilePart` when you need to override the filename or @@ -109,7 +108,7 @@ class Transcriptions # # @param request_options [OpenAI::RequestOptions, Hash{Symbol=>Object}, nil] # - # @return [OpenAI::Models::Audio::Transcription, OpenAI::Models::Audio::TranscriptionDiarized, OpenAI::Models::Audio::TranscriptionVerbose, StringIO] + # @return [OpenAI::Models::Audio::Transcription, OpenAI::Models::Audio::TranscriptionDiarized, OpenAI::Models::Audio::TranscriptionVerbose] # # @see OpenAI::Models::Audio::TranscriptionCreateParams def create(params) @@ -119,22 +118,12 @@ def create(params) raise ArgumentError.new(message) end - body = OpenAI::Internal::Transport::RequestBodyMerge.merge(parsed, options[:extra_body]) - model = case body.fetch(:response_format) { body["response_format"] } - in :verbose_json | "verbose_json" - OpenAI::UnionOf[OpenAI::Audio::TranscriptionVerbose, OpenAI::Audio::TranscriptionCreateResponse] - in :diarized_json | "diarized_json" - OpenAI::UnionOf[OpenAI::Audio::TranscriptionDiarized, OpenAI::Audio::TranscriptionCreateResponse] - else - OpenAI::Audio::TranscriptionCreateResponse - end - @client.request( method: :post, path: "audio/transcriptions", headers: {"content-type" => "multipart/form-data"}, body: parsed, - model: model, + model: OpenAI::Models::Audio::TranscriptionCreateResponse, security: {bearer_auth: true}, options: options ) @@ -146,8 +135,7 @@ def create(params) # Transcribes audio into the input language. # # Returns a transcription object in `json`, `diarized_json`, or `verbose_json` - # format, plain text in `text`, `srt`, or `vtt` format, or a stream of transcript - # events. Supported formats depend on the model. + # format, or a stream of transcript events. # # `String`, `StringIO`, and pathless `IO` inputs are sent with generic upload # metadata. Use `OpenAI::FilePart` when you need to override the filename or diff --git a/lib/openai/resources/audio/translations.rb b/lib/openai/resources/audio/translations.rb index 4b6dffe89..3f579cdd1 100644 --- a/lib/openai/resources/audio/translations.rb +++ b/lib/openai/resources/audio/translations.rb @@ -46,25 +46,17 @@ class Translations # # @param request_options [OpenAI::RequestOptions, Hash{Symbol=>Object}, nil] # - # @return [OpenAI::Models::Audio::Translation, OpenAI::Models::Audio::TranslationVerbose, StringIO] + # @return [OpenAI::Models::Audio::Translation, OpenAI::Models::Audio::TranslationVerbose] # # @see OpenAI::Models::Audio::TranslationCreateParams def create(params) parsed, options = OpenAI::Audio::TranslationCreateParams.dump_request(params) - body = OpenAI::Internal::Transport::RequestBodyMerge.merge(parsed, options[:extra_body]) - model = case body.fetch(:response_format) { body["response_format"] } - in :verbose_json | "verbose_json" - OpenAI::UnionOf[OpenAI::Audio::TranslationVerbose, OpenAI::Audio::TranslationCreateResponse] - else - OpenAI::Audio::TranslationCreateResponse - end - @client.request( method: :post, path: "audio/translations", headers: {"content-type" => "multipart/form-data"}, body: parsed, - model: model, + model: OpenAI::Models::Audio::TranslationCreateResponse, security: {bearer_auth: true}, options: options ) diff --git a/lib/openai/resources/safety.rb b/lib/openai/resources/safety.rb index 0086eacf0..901e5d385 100644 --- a/lib/openai/resources/safety.rb +++ b/lib/openai/resources/safety.rb @@ -3,19 +3,19 @@ module OpenAI module Resources class Safety - # @return [OpenAI::Resources::Safety::Cases] - attr_reader :cases - # @return [OpenAI::Resources::Safety::Alerts] attr_reader :alerts + # @return [OpenAI::Resources::Safety::Cases] + attr_reader :cases + # @api private # # @param client [OpenAI::Client] def initialize(client:) @client = client - @cases = OpenAI::Resources::Safety::Cases.new(client: client) @alerts = OpenAI::Resources::Safety::Alerts.new(client: client) + @cases = OpenAI::Resources::Safety::Cases.new(client: client) end end end diff --git a/rbi/openai/resources/admin/organization.rbi b/rbi/openai/resources/admin/organization.rbi index d26de70ba..c5b58b4ff 100644 --- a/rbi/openai/resources/admin/organization.rbi +++ b/rbi/openai/resources/admin/organization.rbi @@ -7,6 +7,9 @@ module OpenAI class Organization + sig { returns(OpenAI::Resources::Admin::Organization::ExternalStorage) } + attr_reader :external_storage + # List user actions and configuration changes within this organization. sig { returns(OpenAI::Resources::Admin::Organization::AuditLogs) } attr_reader :audit_logs @@ -32,9 +35,6 @@ module OpenAI sig { returns(OpenAI::Resources::Admin::Organization::DataRetention) } attr_reader :data_retention - sig { returns(OpenAI::Resources::Admin::Organization::ExternalStorage) } - attr_reader :external_storage - sig { returns(OpenAI::Resources::Admin::Organization::SpendLimit) } attr_reader :spend_limit diff --git a/rbi/openai/resources/audio/transcriptions.rbi b/rbi/openai/resources/audio/transcriptions.rbi index fe1ae4d85..bd0ed6dc0 100644 --- a/rbi/openai/resources/audio/transcriptions.rbi +++ b/rbi/openai/resources/audio/transcriptions.rbi @@ -14,8 +14,7 @@ module OpenAI # Transcribes audio into the input language. # # Returns a transcription object in `json`, `diarized_json`, or `verbose_json` - # format, plain text in `text`, `srt`, or `vtt` format, or a stream of transcript - # events. Supported formats depend on the model. + # format, or a stream of transcript events. sig { params( file: OpenAI::Internal::FileInput, @@ -36,7 +35,7 @@ module OpenAI stream: T.noreturn, request_options: OpenAI::RequestOptions::OrHash ) - .returns(T.any(OpenAI::Models::Audio::TranscriptionCreateResponse::Variants, StringIO)) + .returns(OpenAI::Models::Audio::TranscriptionCreateResponse::Variants) } def create( # The audio file object (not file name) to transcribe, in one of these formats: @@ -126,8 +125,7 @@ module OpenAI # Transcribes audio into the input language. # # Returns a transcription object in `json`, `diarized_json`, or `verbose_json` - # format, plain text in `text`, `srt`, or `vtt` format, or a stream of transcript - # events. Supported formats depend on the model. + # format, or a stream of transcript events. sig { params( file: OpenAI::Internal::FileInput, diff --git a/rbi/openai/resources/audio/translations.rbi b/rbi/openai/resources/audio/translations.rbi index 681559837..332cf184a 100644 --- a/rbi/openai/resources/audio/translations.rbi +++ b/rbi/openai/resources/audio/translations.rbi @@ -18,7 +18,7 @@ module OpenAI temperature: Float, request_options: OpenAI::RequestOptions::OrHash ) - .returns(T.any(OpenAI::Models::Audio::TranslationCreateResponse::Variants, StringIO)) + .returns(OpenAI::Models::Audio::TranslationCreateResponse::Variants) } def create( # The audio file object (not file name) translate, in one of these formats: flac, diff --git a/rbi/openai/resources/safety.rbi b/rbi/openai/resources/safety.rbi index fe85088bc..b8efbfae6 100644 --- a/rbi/openai/resources/safety.rbi +++ b/rbi/openai/resources/safety.rbi @@ -5,12 +5,12 @@ module OpenAI class Safety - sig { returns(OpenAI::Resources::Safety::Cases) } - attr_reader :cases - sig { returns(OpenAI::Resources::Safety::Alerts) } attr_reader :alerts + sig { returns(OpenAI::Resources::Safety::Cases) } + attr_reader :cases + # @api private sig { params(client: OpenAI::Client).returns(T.attached_class) } def self.new(client:) diff --git a/sig/openai/resources/admin/organization.rbs b/sig/openai/resources/admin/organization.rbs index 589bd92cb..fcedc6219 100644 --- a/sig/openai/resources/admin/organization.rbs +++ b/sig/openai/resources/admin/organization.rbs @@ -2,6 +2,8 @@ module OpenAI module Resources class Admin class Organization + attr_reader external_storage: OpenAI::Resources::Admin::Organization::ExternalStorage + attr_reader audit_logs: OpenAI::Resources::Admin::Organization::AuditLogs attr_reader admin_api_keys: OpenAI::Resources::Admin::Organization::AdminAPIKeys @@ -18,8 +20,6 @@ module OpenAI attr_reader data_retention: OpenAI::Resources::Admin::Organization::DataRetention - attr_reader external_storage: OpenAI::Resources::Admin::Organization::ExternalStorage - attr_reader spend_limit: OpenAI::Resources::Admin::Organization::SpendLimit attr_reader spend_alerts: OpenAI::Resources::Admin::Organization::SpendAlerts diff --git a/sig/openai/resources/audio/transcriptions.rbs b/sig/openai/resources/audio/transcriptions.rbs index 77b8a6233..4ced1012e 100644 --- a/sig/openai/resources/audio/transcriptions.rbs +++ b/sig/openai/resources/audio/transcriptions.rbs @@ -17,7 +17,7 @@ module OpenAI ?temperature: Float, ?timestamp_granularities: ::Array[OpenAI::Models::Audio::TranscriptionCreateParams::timestamp_granularity], ?request_options: OpenAI::request_opts - ) -> (OpenAI::Models::Audio::transcription_create_response | StringIO) + ) -> OpenAI::Models::Audio::transcription_create_response def create_streaming: ( file: OpenAI::Internal::file_input, diff --git a/sig/openai/resources/audio/translations.rbs b/sig/openai/resources/audio/translations.rbs index 504e6d563..bd3560b75 100644 --- a/sig/openai/resources/audio/translations.rbs +++ b/sig/openai/resources/audio/translations.rbs @@ -9,7 +9,7 @@ module OpenAI ?response_format: OpenAI::Models::Audio::TranslationCreateParams::response_format, ?temperature: Float, ?request_options: OpenAI::request_opts - ) -> (OpenAI::Models::Audio::translation_create_response | StringIO) + ) -> OpenAI::Models::Audio::translation_create_response def initialize: (client: OpenAI::Client) -> void end diff --git a/sig/openai/resources/safety.rbs b/sig/openai/resources/safety.rbs index 9b9257931..8569b7e90 100644 --- a/sig/openai/resources/safety.rbs +++ b/sig/openai/resources/safety.rbs @@ -1,10 +1,10 @@ module OpenAI module Resources class Safety - attr_reader cases: OpenAI::Resources::Safety::Cases - attr_reader alerts: OpenAI::Resources::Safety::Alerts + attr_reader cases: OpenAI::Resources::Safety::Cases + def initialize: (client: OpenAI::Client) -> void end end diff --git a/test/openai/audio_response_format_test.rb b/test/openai/audio_response_format_test.rb deleted file mode 100644 index 2d384141a..000000000 --- a/test/openai/audio_response_format_test.rb +++ /dev/null @@ -1,178 +0,0 @@ -# frozen_string_literal: true - -require_relative "test_helper" - -class OpenAI::Test::AudioResponseFormatTest < Minitest::Test - class Capture < OpenAI::HTTPClient - attr_reader :requests - - def initialize(body, content_type: "application/json") - super() - @body = body - @content_type = content_type - @requests = [] - end - - def execute(request) - @requests << request - OpenAI::HTTPClient::Response.new( - status: 200, - headers: {"content-type" => @content_type, "x-request-id" => "req_audio_format"}, - body: [@body] - ) - end - end - - def test_verbose_translation_uses_the_effective_format - body = JSON.generate( - duration: 1.25, - language: "english", - text: "hello", - segments: [verbose_segment], - future: {value: 1} - ) - [ - {response_format: :verbose_json}, - {response_format: "verbose_json"}, - {response_format: :json, request_options: {extra_body: {response_format: :verbose_json}}}, - {response_format: :json, request_options: {extra_body: {"response_format" => "verbose_json"}}}, - {request_options: OpenAI::RequestOptions.new(extra_body: {"response_format" => "verbose_json"})} - ].each do |params| - client, transport = client_for(body) - response = client.audio.translations.create(**audio_params, **params) - - assert_instance_of(OpenAI::Audio::TranslationVerbose, response) - assert_instance_of(OpenAI::Audio::TranscriptionSegment, response.segments.fetch(0)) - assert_equal(7, response.segments.fetch(0).id) - assert_equal({value: 1}, response[:future]) - assert_equal("req_audio_format", response._request_id) - assert_match(/name="response_format".*?\r\n\r\nverbose_json\r\n/m, transport.requests.last.body.to_a.join) - end - end - - def test_empty_transcription_segments_follow_the_effective_rich_format - body = JSON.generate(duration: 1.25, language: "en", task: "transcribe", text: "hello", segments: []) - [ - [:diarized_json, {}, OpenAI::Audio::TranscriptionDiarized], - ["diarized_json", {}, OpenAI::Audio::TranscriptionDiarized], - [:verbose_json, {extra_body: {response_format: :diarized_json}}, OpenAI::Audio::TranscriptionDiarized], - [:verbose_json, {extra_body: {"response_format" => "diarized_json"}}, OpenAI::Audio::TranscriptionDiarized], - [:diarized_json, {extra_body: {response_format: :verbose_json}}, OpenAI::Audio::TranscriptionVerbose], - [:diarized_json, {extra_body: {"response_format" => "verbose_json"}}, OpenAI::Audio::TranscriptionVerbose] - ].each do |format, options, expected| - client, = client_for(body) - response = client.audio.transcriptions.create( - **audio_params, - response_format: format, - request_options: {**options, include_raw_body: true} - ) - - assert_instance_of(expected, response) - assert_empty(response.segments) - assert_equal(body, response.last_response.body) - assert_equal("req_audio_format", response.last_response.request_id) - end - end - - def test_diarized_override_preserves_typed_segments_and_future_fields - body = JSON.generate( - duration: 1.25, - task: "transcribe", - text: "hello", - segments: [diarized_segment], - future: {value: 1} - ) - client, transport = client_for(body) - response = client.audio.transcriptions.create( - **audio_params, - request_options: OpenAI::RequestOptions.new(extra_body: {"response_format" => "diarized_json"}) - ) - - assert_instance_of(OpenAI::Audio::TranscriptionDiarized, response) - segment = response.segments.fetch(0) - assert_instance_of(OpenAI::Audio::TranscriptionDiarizedSegment, segment) - assert_equal("seg_1", segment.id) - assert_equal("A", segment.speaker) - assert_equal({value: 1}, response[:future]) - assert_match(/name="response_format".*?\r\n\r\ndiarized_json\r\n/m, transport.requests.last.body.to_a.join) - end - - def test_missing_speaker_falls_back_to_basic_with_overrides - body = JSON.generate( - duration: 1.25, - task: "transcribe", - text: "hello", - segments: [diarized_segment.except(:speaker)] - ) - [{response_format: :diarized_json}, {"response_format" => "diarized_json"}].each do |extra_body| - client, = client_for(body) - response = client.audio.transcriptions.create( - **audio_params, - response_format: :json, - request_options: {extra_body: extra_body} - ) - - assert_instance_of(OpenAI::Audio::Transcription, response) - assert_instance_of(Hash, response[:segments].fetch(0)) - assert_equal("seg_1", response[:segments].fetch(0).fetch(:id)) - end - end - - def test_translation_default_json_and_future_formats_keep_aggregate_fallback - body = JSON.generate(duration: 1.25, language: "english", text: "hello", segments: []) - [ - {}, - {response_format: :json}, - {response_format: "future_format"}, - {response_format: :verbose_json, request_options: {extra_body: {response_format: :json}}}, - {response_format: :verbose_json, request_options: {extra_body: {"response_format" => "json"}}} - ].each do |params| - client, = client_for(body) - response = client.audio.translations.create(**audio_params, **params) - - assert_instance_of(OpenAI::Audio::Translation, response) - assert_equal(1.25, response[:duration]) - end - end - - def test_plaintext_overrides_keep_string_io - ["text", "srt", "vtt"].each do |format| - client, = client_for("synthetic plaintext", content_type: "text/plain") - options = {extra_body: {"response_format" => format}} - [client.audio.transcriptions, client.audio.translations].each do |resource| - response = resource.create(**audio_params, response_format: :verbose_json, request_options: options) - - assert_instance_of(StringIO, response) - assert_equal("synthetic plaintext", response.read) - refute_respond_to(response, :last_response) - end - end - end - - private def client_for(body, content_type: "application/json") - transport = Capture.new(body, content_type: content_type) - client = OpenAI::Client.new(base_url: "http://localhost", api_key: "fake-api-key", http_client: transport) - [client, transport] - end - - private def audio_params = {file: StringIO.new("synthetic audio"), model: :"whisper-1"} - - private def verbose_segment - { - id: 7, - avg_logprob: -0.1, - compression_ratio: 1.0, - end: 1.25, - no_speech_prob: 0.0, - seek: 0, - start: 0.0, - temperature: 0.0, - text: "hello", - tokens: [1] - } - end - - private def diarized_segment - {id: "seg_1", end: 1.25, speaker: "A", start: 0.0, text: "hello", type: "transcript.text.segment"} - end -end