openai 0.78.0 → 0.80.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +99 -0
- data/README.md +241 -8
- data/SECURITY.md +6 -22
- data/azure.md +110 -0
- data/bedrock.md +197 -0
- data/examples/mtls_custom_http_client.rb +61 -0
- data/lib/openai/auth/workload_identity.rb +13 -2
- data/lib/openai/auth/workload_identity_auth.rb +2 -1
- data/lib/openai/client.rb +29 -6
- data/lib/openai/errors.rb +11 -1
- data/lib/openai/helpers/streaming/chat_completion_stream.rb +3 -3
- data/lib/openai/helpers/streaming/response_stream.rb +5 -6
- data/lib/openai/helpers/structured_output/base_model.rb +20 -19
- data/lib/openai/helpers/structured_output/json_schema_converter.rb +12 -5
- data/lib/openai/http_client.rb +99 -7
- data/lib/openai/internal/conversation_cursor_page.rb +5 -6
- data/lib/openai/internal/cursor_page.rb +3 -3
- data/lib/openai/internal/logging.rb +419 -0
- data/lib/openai/internal/next_cursor_page.rb +5 -6
- data/lib/openai/internal/page.rb +2 -2
- data/lib/openai/internal/provider.rb +7 -1
- data/lib/openai/internal/read_io_adapter.rb +42 -18
- data/lib/openai/internal/stream.rb +2 -2
- data/lib/openai/internal/transport/base_client.rb +137 -37
- data/lib/openai/internal/type/array_of.rb +6 -4
- data/lib/openai/internal/type/base_model.rb +39 -1
- data/lib/openai/internal/type/base_page.rb +19 -2
- data/lib/openai/internal/type/base_stream.rb +39 -7
- data/lib/openai/internal/type/hash_of.rb +3 -3
- data/lib/openai/internal/util.rb +52 -27
- data/lib/openai/models/admin/organization/audit_log_list_params.rb +3 -1
- data/lib/openai/models/admin/organization/audit_log_list_response.rb +33 -11
- data/lib/openai/models/admin/organization/groups/role_list_response.rb +5 -1
- data/lib/openai/models/admin/organization/groups/role_retrieve_response.rb +5 -1
- data/lib/openai/models/admin/organization/projects/groups/role_list_response.rb +6 -1
- data/lib/openai/models/admin/organization/projects/groups/role_retrieve_response.rb +6 -1
- data/lib/openai/models/admin/organization/projects/service_account_create_response.rb +3 -1
- data/lib/openai/models/admin/organization/projects/users/role_list_response.rb +6 -1
- data/lib/openai/models/admin/organization/projects/users/role_retrieve_response.rb +6 -1
- data/lib/openai/models/admin/organization/users/role_list_response.rb +5 -1
- data/lib/openai/models/admin/organization/users/role_retrieve_response.rb +5 -1
- data/lib/openai/models/all_models.rb +5 -0
- data/lib/openai/models/audio/transcription_create_params.rb +7 -1
- data/lib/openai/models/audio/translation_create_params.rb +7 -1
- data/lib/openai/models/beta/beta_mcp_tool_call_error.rb +84 -0
- data/lib/openai/models/beta/beta_response.rb +27 -0
- data/lib/openai/models/beta/beta_response_input_item.rb +3 -3
- data/lib/openai/models/beta/beta_response_item.rb +3 -3
- data/lib/openai/models/beta/beta_response_output_item.rb +3 -3
- data/lib/openai/models/beta/beta_response_output_item_added_event.rb +5 -2
- data/lib/openai/models/beta/beta_response_output_text_annotation_added_event.rb +201 -4
- data/lib/openai/models/beta/beta_response_reasoning_item.rb +5 -0
- data/lib/openai/models/beta/beta_response_stream_event.rb +1 -1
- data/lib/openai/models/beta/beta_responses_client_event.rb +46 -3
- data/lib/openai/models/beta/beta_responses_server_event.rb +1143 -63
- data/lib/openai/models/beta/beta_web_search_tool.rb +11 -1
- data/lib/openai/models/beta/response_compact_params.rb +18 -0
- data/lib/openai/models/beta/response_create_params.rb +27 -0
- data/lib/openai/models/chat_model.rb +1 -0
- data/lib/openai/models/completion_usage.rb +26 -2
- data/lib/openai/models/containers/file_create_params.rb +4 -0
- data/lib/openai/models/content_provenance_check_create_params.rb +4 -0
- data/lib/openai/models/conversations/conversation_item.rb +3 -3
- data/lib/openai/models/file_create_params.rb +4 -0
- data/lib/openai/models/image_create_variation_params.rb +4 -0
- data/lib/openai/models/image_edit_params.rb +8 -0
- data/lib/openai/models/model.rb +9 -1
- data/lib/openai/models/responses/mcp_tool_call_error.rb +82 -0
- data/lib/openai/models/responses/response.rb +9 -0
- data/lib/openai/models/responses/response_compact_params.rb +18 -0
- data/lib/openai/models/responses/response_create_params.rb +9 -0
- data/lib/openai/models/responses/response_input_item.rb +3 -3
- data/lib/openai/models/responses/response_item.rb +3 -3
- data/lib/openai/models/responses/response_output_item.rb +3 -3
- data/lib/openai/models/responses/response_output_item_added_event.rb +5 -2
- data/lib/openai/models/responses/response_output_text_annotation_added_event.rb +201 -4
- data/lib/openai/models/responses/response_reasoning_item.rb +5 -0
- data/lib/openai/models/responses/response_stream_event.rb +1 -1
- data/lib/openai/models/responses/responses_client_event.rb +22 -1
- data/lib/openai/models/responses/responses_server_event.rb +1107 -56
- data/lib/openai/models/responses/web_search_tool.rb +11 -1
- data/lib/openai/models/responses_model.rb +5 -0
- data/lib/openai/models/skill_create_params.rb +4 -0
- data/lib/openai/models/skills/version_create_params.rb +4 -0
- data/lib/openai/models/uploads/part_create_params.rb +4 -0
- data/lib/openai/models/vector_store_search_response.rb +3 -1
- data/lib/openai/models/video_create_character_params.rb +4 -0
- data/lib/openai/models/video_create_params.rb +4 -0
- data/lib/openai/models/video_edit_params.rb +4 -0
- data/lib/openai/models/video_extend_params.rb +4 -0
- data/lib/openai/models.rb +9 -1
- data/lib/openai/net_http_client.rb +20 -6
- data/lib/openai/providers/azure.rb +216 -0
- data/lib/openai/providers/bedrock.rb +130 -17
- data/lib/openai/resources/admin/organization/projects/groups/roles.rb +1 -1
- data/lib/openai/resources/admin/organization/projects/groups.rb +1 -1
- data/lib/openai/resources/admin/organization/projects/users/roles.rb +1 -1
- data/lib/openai/resources/admin/organization/projects.rb +4 -2
- data/lib/openai/resources/audio/transcriptions.rb +8 -0
- data/lib/openai/resources/audio/translations.rb +4 -0
- data/lib/openai/resources/beta/responses.rb +1 -1
- data/lib/openai/resources/beta/threads/runs/steps.rb +2 -2
- data/lib/openai/resources/containers/files.rb +4 -0
- data/lib/openai/resources/content_provenance_checks.rb +4 -0
- data/lib/openai/resources/conversations/items.rb +1 -1
- data/lib/openai/resources/evals/runs/output_items.rb +1 -1
- data/lib/openai/resources/files.rb +5 -1
- data/lib/openai/resources/images.rb +12 -0
- data/lib/openai/resources/responses.rb +3 -2
- data/lib/openai/resources/skills/versions.rb +4 -0
- data/lib/openai/resources/skills.rb +4 -0
- data/lib/openai/resources/uploads/parts.rb +4 -0
- data/lib/openai/resources/vector_stores/file_batches.rb +1 -1
- data/lib/openai/resources/videos.rb +38 -0
- data/lib/openai/resources/webhooks.rb +18 -6
- data/lib/openai/version.rb +1 -1
- data/lib/openai.rb +111 -105
- data/rbi/openai/auth.rbi +4 -4
- data/rbi/openai/client.rbi +13 -3
- data/rbi/openai/errors.rbi +7 -0
- data/rbi/openai/helpers/streaming/events.rbi +12 -0
- data/rbi/openai/http_client.rbi +62 -2
- data/rbi/openai/internal/logging.rbi +192 -0
- data/rbi/openai/internal/provider.rbi +3 -0
- data/rbi/openai/internal/read_io_adapter.rbi +6 -1
- data/rbi/openai/internal/transport/base_client.rbi +61 -7
- data/rbi/openai/internal/type/base_model.rbi +24 -1
- data/rbi/openai/internal/type/base_page.rbi +13 -2
- data/rbi/openai/internal/type/base_stream.rbi +15 -4
- data/rbi/openai/internal/util.rbi +26 -2
- data/rbi/openai/models/admin/organization/audit_log_list_params.rbi +5 -0
- data/rbi/openai/models/admin/organization/audit_log_list_response.rbi +5 -0
- data/rbi/openai/models/all_models.rbi +25 -0
- data/rbi/openai/models/audio/transcription_create_params.rbi +14 -2
- data/rbi/openai/models/audio/translation_create_params.rbi +14 -2
- data/rbi/openai/models/beta/beta_mcp_tool_call_error.rbi +122 -0
- data/rbi/openai/models/beta/beta_response.rbi +47 -0
- data/rbi/openai/models/beta/beta_response_input_item.rbi +27 -3
- data/rbi/openai/models/beta/beta_response_item.rbi +27 -3
- data/rbi/openai/models/beta/beta_response_output_item.rbi +27 -3
- data/rbi/openai/models/beta/beta_response_output_item_added_event.rbi +8 -2
- data/rbi/openai/models/beta/beta_response_output_text_annotation_added_event.rbi +297 -5
- data/rbi/openai/models/beta/beta_response_reasoning_item.rbi +10 -0
- data/rbi/openai/models/beta/beta_response_stream_event.rbi +1 -1
- data/rbi/openai/models/beta/beta_responses_client_event.rbi +69 -1
- data/rbi/openai/models/beta/beta_responses_server_event.rbi +3945 -54
- data/rbi/openai/models/beta/beta_web_search_tool.rbi +15 -0
- data/rbi/openai/models/beta/response_compact_params.rbi +30 -0
- data/rbi/openai/models/beta/response_create_params.rbi +47 -0
- data/rbi/openai/models/chat_model.rbi +2 -0
- data/rbi/openai/models/completion_usage.rbi +39 -6
- data/rbi/openai/models/containers/file_create_params.rbi +8 -0
- data/rbi/openai/models/content_provenance_check_create_params.rbi +8 -0
- data/rbi/openai/models/conversations/conversation_item.rbi +12 -3
- data/rbi/openai/models/file_create_params.rbi +8 -0
- data/rbi/openai/models/image_create_variation_params.rbi +8 -0
- data/rbi/openai/models/image_edit_params.rbi +16 -0
- data/rbi/openai/models/model.rbi +14 -1
- data/rbi/openai/models/responses/mcp_tool_call_error.rbi +120 -0
- data/rbi/openai/models/responses/response.rbi +17 -0
- data/rbi/openai/models/responses/response_compact_params.rbi +30 -0
- data/rbi/openai/models/responses/response_create_params.rbi +17 -0
- data/rbi/openai/models/responses/response_input_item.rbi +27 -3
- data/rbi/openai/models/responses/response_item.rbi +27 -3
- data/rbi/openai/models/responses/response_output_item.rbi +27 -3
- data/rbi/openai/models/responses/response_output_item_added_event.rbi +8 -2
- data/rbi/openai/models/responses/response_output_text_annotation_added_event.rbi +297 -5
- data/rbi/openai/models/responses/response_reasoning_item.rbi +10 -0
- data/rbi/openai/models/responses/response_stream_event.rbi +1 -1
- data/rbi/openai/models/responses/responses_client_event.rbi +36 -0
- data/rbi/openai/models/responses/responses_server_event.rbi +3385 -53
- data/rbi/openai/models/responses/web_search_tool.rbi +15 -0
- data/rbi/openai/models/responses_model.rbi +25 -0
- data/rbi/openai/models/skill_create_params.rbi +8 -0
- data/rbi/openai/models/skills/version_create_params.rbi +8 -0
- data/rbi/openai/models/uploads/part_create_params.rbi +8 -0
- data/rbi/openai/models/video_create_character_params.rbi +8 -0
- data/rbi/openai/models/video_create_params.rbi +8 -0
- data/rbi/openai/models/video_edit_params.rbi +8 -0
- data/rbi/openai/models/video_extend_params.rbi +8 -0
- data/rbi/openai/net_http_client.rbi +2 -2
- data/rbi/openai/providers.rbi +11 -0
- data/rbi/openai/resources/audio/transcriptions.rbi +14 -2
- data/rbi/openai/resources/audio/translations.rbi +7 -1
- data/rbi/openai/resources/beta/responses.rbi +8 -0
- data/rbi/openai/resources/containers/files.rbi +4 -0
- data/rbi/openai/resources/content_provenance_checks.rbi +4 -0
- data/rbi/openai/resources/files.rbi +5 -1
- data/rbi/openai/resources/images.rbi +20 -0
- data/rbi/openai/resources/responses.rbi +8 -0
- data/rbi/openai/resources/skills/versions.rbi +4 -0
- data/rbi/openai/resources/skills.rbi +4 -0
- data/rbi/openai/resources/uploads/parts.rbi +4 -0
- data/rbi/openai/resources/videos.rbi +16 -0
- data/sig/openai/client.rbs +6 -2
- data/sig/openai/errors.rbs +5 -0
- data/sig/openai/http_client.rbs +37 -2
- data/sig/openai/internal/logging.rbs +88 -0
- data/sig/openai/internal/provider.rbs +3 -1
- data/sig/openai/internal/read_io_adapter.rbs +3 -1
- data/sig/openai/internal/transport/base_client.rbs +33 -5
- data/sig/openai/internal/type/base_model.rbs +13 -0
- data/sig/openai/internal/type/base_page.rbs +8 -1
- data/sig/openai/internal/type/base_stream.rbs +8 -2
- data/sig/openai/internal/util.rbs +12 -0
- data/sig/openai/models/admin/organization/audit_log_list_params.rbs +2 -0
- data/sig/openai/models/admin/organization/audit_log_list_response.rbs +2 -0
- data/sig/openai/models/all_models.rbs +10 -0
- data/sig/openai/models/beta/beta_mcp_tool_call_error.rbs +75 -0
- data/sig/openai/models/beta/beta_response.rbs +14 -1
- data/sig/openai/models/beta/beta_response_input_item.rbs +4 -4
- data/sig/openai/models/beta/beta_response_item.rbs +4 -4
- data/sig/openai/models/beta/beta_response_output_item.rbs +4 -4
- data/sig/openai/models/beta/beta_response_output_text_annotation_added_event.rbs +149 -4
- data/sig/openai/models/beta/beta_responses_client_event.rbs +21 -1
- data/sig/openai/models/beta/beta_responses_server_event.rbs +2005 -53
- data/sig/openai/models/beta/beta_web_search_tool.rbs +7 -0
- data/sig/openai/models/beta/response_compact_params.rbs +12 -0
- data/sig/openai/models/beta/response_create_params.rbs +14 -1
- data/sig/openai/models/chat_model.rbs +2 -0
- data/sig/openai/models/completion_usage.rbs +27 -6
- data/sig/openai/models/conversations/conversation_item.rbs +4 -4
- data/sig/openai/models/model.rbs +12 -2
- data/sig/openai/models/responses/mcp_tool_call_error.rbs +73 -0
- data/sig/openai/models/responses/response.rbs +2 -1
- data/sig/openai/models/responses/response_compact_params.rbs +12 -0
- data/sig/openai/models/responses/response_create_params.rbs +2 -1
- data/sig/openai/models/responses/response_input_item.rbs +4 -4
- data/sig/openai/models/responses/response_item.rbs +4 -4
- data/sig/openai/models/responses/response_output_item.rbs +4 -4
- data/sig/openai/models/responses/response_output_text_annotation_added_event.rbs +149 -4
- data/sig/openai/models/responses/responses_client_event.rbs +9 -1
- data/sig/openai/models/responses/responses_server_event.rbs +1834 -53
- data/sig/openai/models/responses/web_search_tool.rbs +7 -0
- data/sig/openai/models/responses_model.rbs +10 -0
- data/sig/openai/net_http_client.rbs +2 -2
- data/sig/openai/providers.rbs +7 -0
- metadata +30 -1
data/bedrock.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Amazon Bedrock
|
|
2
|
+
|
|
3
|
+
Configure the standard `OpenAI::Client` with the Bedrock provider to use [Amazon Bedrock's OpenAI-compatible API](https://docs.aws.amazon.com/bedrock/latest/userguide/models-api-compatibility.html):
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "openai"
|
|
7
|
+
|
|
8
|
+
client = OpenAI::Client.new(
|
|
9
|
+
provider: OpenAI::Providers.bedrock(region: "us-west-2")
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
response = client.responses.create(
|
|
13
|
+
model: ENV.fetch("BEDROCK_MODEL"),
|
|
14
|
+
input: "Say hello!"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
puts(response.output_text)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The provider uses `https://bedrock-mantle.<region>.api.aws/v1` by default and exposes the normal SDK resources. AWS controls which endpoints and features are supported; unsupported calls surface as normal API errors.
|
|
21
|
+
|
|
22
|
+
## Bedrock Runtime
|
|
23
|
+
|
|
24
|
+
Pass `endpoint: :runtime` to use the Bedrock Runtime OpenAI-compatible endpoint. The provider routes requests to `https://bedrock-runtime.<region>.amazonaws.com/openai/v1` and uses the AWS SigV4 signing service `bedrock` when AWS credentials are selected:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
require "openai"
|
|
28
|
+
|
|
29
|
+
client = OpenAI::Client.new(
|
|
30
|
+
provider: OpenAI::Providers.bedrock(
|
|
31
|
+
endpoint: :runtime,
|
|
32
|
+
region: "us-east-1"
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
completion = client.chat.completions.create(
|
|
37
|
+
model: "us.openai.gpt-5.6-sol",
|
|
38
|
+
messages: [{role: :user, content: "Say hello!"}]
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
puts(completion.choices.first.message.content)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Use the inference profile configured for your AWS account, for example `us.openai.gpt-5.6-sol`, `us.openai.gpt-5.6-terra`, or `us.openai.gpt-5.6-luna`. Global inference profiles, such as `global.openai.gpt-5.6-sol`, require the appropriate AWS account and regional permissions. Set `BEDROCK_MODEL` to override the model used by [`examples/bedrock_runtime.rb`](examples/bedrock_runtime.rb). The example uses Chat Completions by default; set `BEDROCK_RESPONSES=1` to also use Responses or `BEDROCK_STREAM=1` to also stream Chat Completions. Set `BEDROCK_AUTH_MODE=sigv4` to explicitly select AWS authentication when a bearer credential is also present.
|
|
45
|
+
|
|
46
|
+
The same client supports Responses and response streaming when AWS makes those APIs available for the selected model:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
response = client.responses.create(
|
|
50
|
+
model: "us.openai.gpt-5.6-sol",
|
|
51
|
+
input: "Say hello!"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
stream = client.chat.completions.stream(
|
|
55
|
+
model: "us.openai.gpt-5.6-sol",
|
|
56
|
+
messages: [{role: :user, content: "Say hello!"}]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
stream.text.each { |text| print(text) }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The existing Mantle endpoint remains the default. Pass `endpoint: :mantle` when you need to explicitly select it. Both endpoint symbols and the strings `"runtime"` and `"mantle"` are accepted.
|
|
63
|
+
|
|
64
|
+
| Endpoint | Default URL | SigV4 signing service |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| `:mantle` (default) | `https://bedrock-mantle.<region>.api.aws/v1` | `bedrock-mantle` |
|
|
67
|
+
| `:runtime` | `https://bedrock-runtime.<region>.amazonaws.com/openai/v1` | `bedrock` |
|
|
68
|
+
|
|
69
|
+
Runtime URLs automatically use the correct DNS suffix for AWS China, European Sovereign Cloud, and isolated partitions. Canonical Runtime, FIPS, and dual-stack `base_url` values automatically select Runtime when `endpoint` is omitted. Canonical AWS endpoint URLs must use HTTPS, match the selected endpoint family, and agree with the configured AWS region.
|
|
70
|
+
|
|
71
|
+
## Endpoint configuration
|
|
72
|
+
|
|
73
|
+
The region is resolved from the explicit `region` option, `AWS_REGION`, `AWS_DEFAULT_REGION`, or the selected AWS profile. Override the endpoint with `base_url` or `AWS_BEDROCK_BASE_URL`:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
provider = OpenAI::Providers.bedrock(
|
|
77
|
+
endpoint: :runtime,
|
|
78
|
+
region: "us-west-2",
|
|
79
|
+
base_url: "https://bedrock.example.com/openai/v1"
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Custom endpoints using AWS credentials default to Mantle signing when `endpoint` is omitted; pass `endpoint: :runtime` to use Runtime signing. Local HTTP proxies remain supported when the endpoint is selected explicitly. Custom endpoints using bearer credentials can omit `endpoint`; in that case the default remains Mantle.
|
|
84
|
+
|
|
85
|
+
## Authentication
|
|
86
|
+
|
|
87
|
+
The provider selects authentication in this order:
|
|
88
|
+
|
|
89
|
+
1. One explicit mode passed to `bedrock(...)`: `api_key`, `token_provider`, static AWS credentials, `profile`, or `credentials_provider`.
|
|
90
|
+
2. The Bedrock bearer credential in `AWS_BEARER_TOKEN_BEDROCK`.
|
|
91
|
+
3. The standard AWS credential chain.
|
|
92
|
+
|
|
93
|
+
Explicit bearer and AWS credential modes are mutually exclusive. Configure only one explicit AWS mode at a time.
|
|
94
|
+
|
|
95
|
+
### AWS credentials and SigV4
|
|
96
|
+
|
|
97
|
+
AWS authentication is an optional integration so applications that do not use Bedrock do not install or load AWS packages. Add the AWS SDK core gem to your application:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
gem "aws-sdk-core", "~> 3"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then run `bundle install`. If you are not using Bundler, run `gem install aws-sdk-core`.
|
|
104
|
+
|
|
105
|
+
Omit explicit authentication to use environment credentials, the shared credentials and config files, SSO or assume-role profiles, web identity, ECS credentials, or EC2 instance credentials:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
client = OpenAI::Client.new(
|
|
109
|
+
provider: OpenAI::Providers.bedrock(region: "us-west-2")
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Select a named profile with:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
client = OpenAI::Client.new(
|
|
117
|
+
provider: OpenAI::Providers.bedrock(
|
|
118
|
+
region: "us-west-2",
|
|
119
|
+
profile: "engineering"
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
You can omit `region` when that profile defines one in `~/.aws/config`.
|
|
125
|
+
|
|
126
|
+
Pass temporary credentials directly, including the session token:
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
client = OpenAI::Client.new(
|
|
130
|
+
provider: OpenAI::Providers.bedrock(
|
|
131
|
+
region: "us-west-2",
|
|
132
|
+
access_key_id: ENV.fetch("AWS_ACCESS_KEY_ID"),
|
|
133
|
+
secret_access_key: ENV.fetch("AWS_SECRET_ACCESS_KEY"),
|
|
134
|
+
session_token: ENV["AWS_SESSION_TOKEN"]
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
For credentials managed by your application, pass an AWS credential provider or a callable that returns `Aws::Credentials`. It is consulted before every request attempt, including retries:
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
credentials_provider = lambda do
|
|
143
|
+
Aws::Credentials.new(
|
|
144
|
+
ENV.fetch("AWS_ACCESS_KEY_ID"),
|
|
145
|
+
ENV.fetch("AWS_SECRET_ACCESS_KEY"),
|
|
146
|
+
ENV["AWS_SESSION_TOKEN"]
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
client = OpenAI::Client.new(
|
|
151
|
+
provider: OpenAI::Providers.bedrock(
|
|
152
|
+
region: "us-west-2",
|
|
153
|
+
credentials_provider: credentials_provider
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The provider signs each finalized attempt with AWS SigV4 service name `bedrock-mantle` for Mantle or `bedrock` for Runtime. Standard JSON API requests have replayable bodies and work normally. SigV4 rejects one-shot request streams before sending, and signed requests do not automatically follow redirects because a new target requires a new signature. Response streaming is unaffected.
|
|
159
|
+
|
|
160
|
+
### Bearer authentication
|
|
161
|
+
|
|
162
|
+
Pass a Bedrock bearer credential directly, set `AWS_BEARER_TOKEN_BEDROCK`, or use a callable to resolve a fresh token before every attempt:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
client = OpenAI::Client.new(
|
|
166
|
+
provider: OpenAI::Providers.bedrock(
|
|
167
|
+
region: "us-west-2",
|
|
168
|
+
api_key: ENV.fetch("BEDROCK_API_KEY")
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
client = OpenAI::Client.new(
|
|
175
|
+
provider: OpenAI::Providers.bedrock(
|
|
176
|
+
region: "us-west-2",
|
|
177
|
+
token_provider: -> { refresh_bedrock_token }
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Bearer authentication does not load or require `aws-sdk-core`. Passing `api_key: nil` explicitly skips `AWS_BEARER_TOKEN_BEDROCK` and selects AWS authentication.
|
|
183
|
+
|
|
184
|
+
## Live verification
|
|
185
|
+
|
|
186
|
+
Real AWS requests are disabled in the test suite unless `BEDROCK_LIVE_TEST=1` is explicitly set. To exercise Runtime against all three default US inference profiles, run:
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
AWS_REGION=us-east-1 BEDROCK_LIVE_TEST=1 \
|
|
190
|
+
bundle exec ruby test/openai/providers/bedrock_live_test.rb
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Set `BEDROCK_AUTH_MODE` to `auto`, `bearer`, `token-provider`, `sigv4`, `static`, or `profile` to select credentials. Use `BEDROCK_MODEL` for one inference profile or `BEDROCK_LIVE_MODELS` for a comma-separated list. Set `BEDROCK_LIVE_RESPONSES=1` to also verify Responses and `BEDROCK_LIVE_STREAM=1` to verify Chat Completions streaming.
|
|
194
|
+
|
|
195
|
+
## Security
|
|
196
|
+
|
|
197
|
+
Use Bedrock from a server-side Ruby runtime. Prefer roles, profiles, SSO, and temporary credentials over long-lived static keys. Do not send AWS access keys, session tokens, bearer credentials, or signed authorization headers to browsers or application logs.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# This example sends an API-key request over mutual TLS by configuring the
|
|
5
|
+
# SDK's pooled Net::HTTP connections with Ruby's native OpenSSL support. Set
|
|
6
|
+
# OPENAI_CLIENT_CERTIFICATE_CHAIN to a PEM file containing the leaf certificate
|
|
7
|
+
# followed by any required intermediates, OPENAI_CLIENT_KEY to the matching
|
|
8
|
+
# private key, and OPENAI_MODEL to a model available to the project.
|
|
9
|
+
# OPENAI_CLIENT_KEY_PASSPHRASE is optional.
|
|
10
|
+
|
|
11
|
+
require_relative "../lib/openai"
|
|
12
|
+
|
|
13
|
+
# Set this to the mTLS endpoint for your data residency. EU Data Residency uses
|
|
14
|
+
# https://mtls-eu.api.openai.com/v1. For enrollment and current endpoint
|
|
15
|
+
# guidance, see:
|
|
16
|
+
# https://help.openai.com/en/articles/10876024-openai-mutual-tls-beta-program
|
|
17
|
+
mtls_endpoint = URI("https://mtls.api.openai.com/v1")
|
|
18
|
+
certificates = OpenSSL::X509::Certificate.load(
|
|
19
|
+
File.binread(ENV.fetch("OPENAI_CLIENT_CERTIFICATE_CHAIN"))
|
|
20
|
+
)
|
|
21
|
+
raise ArgumentError, "Expected a client certificate" if certificates.empty?
|
|
22
|
+
|
|
23
|
+
leaf_certificate, *intermediates = certificates
|
|
24
|
+
private_key = OpenSSL::PKey.read(
|
|
25
|
+
File.binread(ENV.fetch("OPENAI_CLIENT_KEY")),
|
|
26
|
+
ENV["OPENAI_CLIENT_KEY_PASSPHRASE"]
|
|
27
|
+
)
|
|
28
|
+
unless leaf_certificate.check_private_key(private_key)
|
|
29
|
+
raise ArgumentError, "The client certificate and private key do not match"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
now = Time.now
|
|
33
|
+
raise ArgumentError, "The client certificate is not yet valid" if now < leaf_certificate.not_before
|
|
34
|
+
raise ArgumentError, "The client certificate has expired" if now > leaf_certificate.not_after
|
|
35
|
+
|
|
36
|
+
mtls_destination = [mtls_endpoint.host, mtls_endpoint.port]
|
|
37
|
+
http_client = OpenAI::NetHTTPClient.new do |http|
|
|
38
|
+
unless http.use_ssl? && mtls_destination == [http.address, http.port]
|
|
39
|
+
raise ArgumentError, "Refusing to present the client certificate to an unexpected origin"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
http.cert = leaf_certificate
|
|
43
|
+
http.extra_chain_cert = intermediates
|
|
44
|
+
http.key = private_key
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
client = OpenAI::Client.new(
|
|
48
|
+
api_key: ENV.fetch("OPENAI_API_KEY"),
|
|
49
|
+
base_url: mtls_endpoint.to_s,
|
|
50
|
+
http_client: http_client
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
response = client.responses.create(
|
|
55
|
+
model: ENV.fetch("OPENAI_MODEL"),
|
|
56
|
+
input: "Reply with: mTLS transport configured"
|
|
57
|
+
)
|
|
58
|
+
puts(response.output_text)
|
|
59
|
+
ensure
|
|
60
|
+
http_client.close
|
|
61
|
+
end
|
|
@@ -6,12 +6,23 @@ module OpenAI
|
|
|
6
6
|
attr_reader :client_id, :identity_provider_id, :service_account_id, :provider, :refresh_buffer_seconds
|
|
7
7
|
|
|
8
8
|
def initialize(
|
|
9
|
-
identity_provider_id:,
|
|
10
|
-
service_account_id:,
|
|
11
9
|
provider:,
|
|
10
|
+
identity_provider_id: ENV["IDENTITY_PROVIDER_ID"],
|
|
11
|
+
service_account_id: ENV["SERVICE_ACCOUNT_ID"],
|
|
12
12
|
client_id: nil,
|
|
13
13
|
refresh_buffer_seconds: 1200
|
|
14
14
|
)
|
|
15
|
+
if identity_provider_id.to_s.strip.empty?
|
|
16
|
+
raise ArgumentError,
|
|
17
|
+
"identity_provider_id must not be blank; pass identity_provider_id: " \
|
|
18
|
+
"or set IDENTITY_PROVIDER_ID"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if service_account_id.to_s.strip.empty?
|
|
22
|
+
raise ArgumentError,
|
|
23
|
+
"service_account_id must not be blank; pass service_account_id: or set SERVICE_ACCOUNT_ID"
|
|
24
|
+
end
|
|
25
|
+
|
|
15
26
|
@client_id = client_id&.to_s
|
|
16
27
|
@identity_provider_id = identity_provider_id.to_s
|
|
17
28
|
@service_account_id = service_account_id.to_s
|
|
@@ -87,7 +87,8 @@ module OpenAI
|
|
|
87
87
|
token_type = @config.provider.token_type
|
|
88
88
|
subject_token_type = SUBJECT_TOKEN_TYPES.fetch(token_type) do
|
|
89
89
|
raise ArgumentError,
|
|
90
|
-
"Unsupported token type: #{token_type.inspect}.
|
|
90
|
+
"Unsupported token type: #{token_type.inspect}. " \
|
|
91
|
+
"Supported types: #{SUBJECT_TOKEN_TYPES.keys.join(', ')}"
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
request = Net::HTTP::Post.new(@token_exchange_url)
|
data/lib/openai/client.rb
CHANGED
|
@@ -229,9 +229,12 @@ module OpenAI
|
|
|
229
229
|
# @param base_url [String, nil] Override the default base URL for the API, e.g.,
|
|
230
230
|
# `"https://api.example.com/v2/"`. Defaults to `ENV["OPENAI_BASE_URL"]`
|
|
231
231
|
#
|
|
232
|
+
# @param default_headers [Hash{String=>String, nil}, nil] Extra headers to send
|
|
233
|
+
# with every request. Explicit values override `ENV["OPENAI_CUSTOM_HEADERS"]`.
|
|
234
|
+
#
|
|
232
235
|
# @param max_retries [Integer] Max number of retries to attempt after a failed retryable request.
|
|
233
236
|
#
|
|
234
|
-
# @param timeout [Float]
|
|
237
|
+
# @param timeout [Float, nil]
|
|
235
238
|
#
|
|
236
239
|
# @param initial_retry_delay [Float]
|
|
237
240
|
#
|
|
@@ -239,6 +242,13 @@ module OpenAI
|
|
|
239
242
|
#
|
|
240
243
|
# @param http_client [#execute, nil] The HTTP client used to
|
|
241
244
|
# execute SDK requests. Defaults to {OpenAI::NetHTTPClient}.
|
|
245
|
+
#
|
|
246
|
+
# @param logger [#debug, #info, #warn, #error, nil] Logger for SDK request diagnostics.
|
|
247
|
+
#
|
|
248
|
+
# @param log_level [Symbol, String, nil] SDK request log level. Defaults to
|
|
249
|
+
# `ENV["OPENAI_LOG"]`, `:info` when `logger` is provided, and `:off` otherwise.
|
|
250
|
+
#
|
|
251
|
+
# @param on_retry [Proc, nil] Callback invoked immediately before an API retry delay.
|
|
242
252
|
def initialize(
|
|
243
253
|
api_key: OpenAI::Internal::OMIT,
|
|
244
254
|
admin_api_key: OpenAI::Internal::OMIT,
|
|
@@ -248,11 +258,15 @@ module OpenAI
|
|
|
248
258
|
webhook_secret: OpenAI::Internal::OMIT,
|
|
249
259
|
provider: nil,
|
|
250
260
|
base_url: OpenAI::Internal::OMIT,
|
|
261
|
+
default_headers: nil,
|
|
251
262
|
max_retries: self.class::DEFAULT_MAX_RETRIES,
|
|
252
263
|
timeout: self.class::DEFAULT_TIMEOUT_IN_SECONDS,
|
|
253
264
|
initial_retry_delay: self.class::DEFAULT_INITIAL_RETRY_DELAY,
|
|
254
265
|
max_retry_delay: self.class::DEFAULT_MAX_RETRY_DELAY,
|
|
255
|
-
http_client: nil
|
|
266
|
+
http_client: nil,
|
|
267
|
+
logger: nil,
|
|
268
|
+
log_level: nil,
|
|
269
|
+
on_retry: nil
|
|
256
270
|
)
|
|
257
271
|
provider_runtime = nil
|
|
258
272
|
unless provider.nil?
|
|
@@ -301,24 +315,30 @@ module OpenAI
|
|
|
301
315
|
|
|
302
316
|
if provider_runtime.nil? && api_key.nil? && admin_api_key.nil? && workload_identity.nil?
|
|
303
317
|
raise ArgumentError,
|
|
304
|
-
"Missing credentials. Please pass an `api_key`, `workload_identity`,
|
|
318
|
+
"Missing credentials. Please pass an `api_key`, `workload_identity`, " \
|
|
319
|
+
"`admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` " \
|
|
320
|
+
"environment variable."
|
|
305
321
|
end
|
|
306
322
|
|
|
307
323
|
headers = {
|
|
308
324
|
"openai-organization" => (@organization = organization&.to_s),
|
|
309
325
|
"openai-project" => (@project = project&.to_s)
|
|
310
326
|
}
|
|
327
|
+
parsed = {}
|
|
311
328
|
custom_headers_env = ENV["OPENAI_CUSTOM_HEADERS"] unless provider_runtime
|
|
312
329
|
unless custom_headers_env.nil?
|
|
313
|
-
parsed = {}
|
|
314
330
|
custom_headers_env.split("\n").each do |line|
|
|
315
331
|
colon = line.index(":")
|
|
316
332
|
unless colon.nil?
|
|
317
333
|
parsed[line[0...colon].strip] = line[(colon + 1)..].strip
|
|
318
334
|
end
|
|
319
335
|
end
|
|
320
|
-
headers = parsed.merge(headers)
|
|
321
336
|
end
|
|
337
|
+
client_headers = OpenAI::Internal::Util.normalized_headers(default_headers.to_h)
|
|
338
|
+
unless provider_runtime.nil?
|
|
339
|
+
provider_runtime.authentication_headers.each { client_headers.delete(_1) }
|
|
340
|
+
end
|
|
341
|
+
headers = OpenAI::Internal::Util.normalized_headers(parsed, headers, client_headers)
|
|
322
342
|
|
|
323
343
|
if workload_identity.nil?
|
|
324
344
|
@api_key = api_key&.to_s
|
|
@@ -341,7 +361,10 @@ module OpenAI
|
|
|
341
361
|
initial_retry_delay: initial_retry_delay,
|
|
342
362
|
max_retry_delay: max_retry_delay,
|
|
343
363
|
headers: headers,
|
|
344
|
-
http_client: http_client
|
|
364
|
+
http_client: http_client,
|
|
365
|
+
logger: logger,
|
|
366
|
+
log_level: log_level,
|
|
367
|
+
on_retry: on_retry
|
|
345
368
|
)
|
|
346
369
|
|
|
347
370
|
@completions = OpenAI::Resources::Completions.new(client: self)
|
data/lib/openai/errors.rb
CHANGED
|
@@ -58,6 +58,13 @@ module OpenAI
|
|
|
58
58
|
# @return [String, nil]
|
|
59
59
|
attr_accessor :type
|
|
60
60
|
|
|
61
|
+
# The ID of the API request, returned via the `x-request-id` response
|
|
62
|
+
# header. This is nil when no HTTP response was received or the response
|
|
63
|
+
# did not include the header.
|
|
64
|
+
#
|
|
65
|
+
# @return [String, nil]
|
|
66
|
+
def request_id = headers&.[]("x-request-id")
|
|
67
|
+
|
|
61
68
|
# @api private
|
|
62
69
|
#
|
|
63
70
|
# @param url [URI::Generic]
|
|
@@ -269,7 +276,10 @@ module OpenAI
|
|
|
269
276
|
attr_reader :error_code
|
|
270
277
|
|
|
271
278
|
def initialize(status:, body:, headers:)
|
|
272
|
-
@error_code = OpenAI::Internal::Type::Converter.coerce(
|
|
279
|
+
@error_code = OpenAI::Internal::Type::Converter.coerce(
|
|
280
|
+
OpenAI::Models::OAuthErrorCode,
|
|
281
|
+
body&.dig(:error)
|
|
282
|
+
)
|
|
273
283
|
|
|
274
284
|
message =
|
|
275
285
|
if body&.dig(:error_description)
|
|
@@ -8,6 +8,7 @@ module OpenAI
|
|
|
8
8
|
|
|
9
9
|
def initialize(raw_stream:, response_format: nil, input_tools: nil)
|
|
10
10
|
@raw_stream = raw_stream
|
|
11
|
+
@last_response = raw_stream.last_response
|
|
11
12
|
@state = ChatCompletionStreamState.new(
|
|
12
13
|
response_format: response_format,
|
|
13
14
|
input_tools: input_tools
|
|
@@ -33,7 +34,7 @@ module OpenAI
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def until_done
|
|
36
|
-
each {
|
|
37
|
+
each { |_event| next }
|
|
37
38
|
self
|
|
38
39
|
end
|
|
39
40
|
|
|
@@ -507,8 +508,7 @@ module OpenAI
|
|
|
507
508
|
|
|
508
509
|
index = delta_entry[:index] || delta_entry["index"]
|
|
509
510
|
if index.nil?
|
|
510
|
-
raise
|
|
511
|
-
"Expected list delta entry to have an `index` key; #{delta_entry}"
|
|
511
|
+
raise "Expected list delta entry to have an `index` key; #{delta_entry}"
|
|
512
512
|
end
|
|
513
513
|
unless index.is_a?(Integer)
|
|
514
514
|
raise TypeError,
|
|
@@ -10,6 +10,7 @@ module OpenAI
|
|
|
10
10
|
@text_format = text_format
|
|
11
11
|
@starting_after = starting_after
|
|
12
12
|
@raw_stream = raw_stream
|
|
13
|
+
@last_response = raw_stream.last_response
|
|
13
14
|
@iterator = iterator
|
|
14
15
|
@state = ResponseStreamState.new(
|
|
15
16
|
text_format: text_format
|
|
@@ -17,7 +18,7 @@ module OpenAI
|
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def until_done
|
|
20
|
-
each {
|
|
21
|
+
each { |_event| next }
|
|
21
22
|
self
|
|
22
23
|
end
|
|
23
24
|
|
|
@@ -35,7 +36,7 @@ module OpenAI
|
|
|
35
36
|
def get_final_response
|
|
36
37
|
until_done
|
|
37
38
|
response = @state.completed_response
|
|
38
|
-
raise
|
|
39
|
+
raise "Didn't receive a 'response.completed' event" unless response
|
|
39
40
|
response
|
|
40
41
|
end
|
|
41
42
|
|
|
@@ -218,10 +219,8 @@ module OpenAI
|
|
|
218
219
|
parsed = JSON.parse(text, symbolize_names: true)
|
|
219
220
|
OpenAI::Internal::Type::Converter.coerce(@text_format, parsed)
|
|
220
221
|
rescue JSON::ParserError => e
|
|
221
|
-
raise
|
|
222
|
-
|
|
223
|
-
"Raw text: #{text.inspect}"
|
|
224
|
-
)
|
|
222
|
+
raise "Failed to parse structured text as JSON for #{@text_format}: #{e.message}. " \
|
|
223
|
+
"Raw text: #{text.inspect}"
|
|
225
224
|
end
|
|
226
225
|
end
|
|
227
226
|
end
|
|
@@ -25,26 +25,27 @@ module OpenAI
|
|
|
25
25
|
#
|
|
26
26
|
# @return [Hash{Symbol=>Object}]
|
|
27
27
|
def to_json_schema_inner(state:)
|
|
28
|
+
field_values = fields.values
|
|
29
|
+
duplicate = field_values.map { _1.fetch(:api_name) }.tally.find { _2 > 1 }
|
|
30
|
+
raise ArgumentError.new("Duplicate API field name: #{duplicate.first}") if duplicate
|
|
31
|
+
|
|
28
32
|
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter.cache_def!(state, type: self) do
|
|
29
33
|
path = state.fetch(:path)
|
|
30
|
-
properties =
|
|
31
|
-
type, nilable, meta = field.fetch_values(:type, :nilable, :meta)
|
|
32
|
-
new_state = {**state, path: [*path, ".#{
|
|
34
|
+
properties = field_values.to_h do |field|
|
|
35
|
+
api_name, type, nilable, meta = field.fetch_values(:api_name, :type, :nilable, :meta)
|
|
36
|
+
new_state = {**state, path: [*path, ".#{api_name}"]}
|
|
33
37
|
|
|
34
|
-
schema =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
else
|
|
39
|
-
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter.to_json_schema_inner(
|
|
40
|
-
type,
|
|
41
|
-
state: new_state
|
|
42
|
-
)
|
|
43
|
-
end
|
|
38
|
+
schema = OpenAI::Helpers::StructuredOutput::JsonSchemaConverter.to_json_schema_inner(
|
|
39
|
+
type,
|
|
40
|
+
state: new_state
|
|
41
|
+
)
|
|
44
42
|
schema = OpenAI::Helpers::StructuredOutput::JsonSchemaConverter.to_nilable(schema) if nilable
|
|
45
|
-
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter.assoc_meta!(
|
|
43
|
+
OpenAI::Helpers::StructuredOutput::JsonSchemaConverter.assoc_meta!(
|
|
44
|
+
schema,
|
|
45
|
+
meta: meta.except(:api_name)
|
|
46
|
+
)
|
|
46
47
|
|
|
47
|
-
[
|
|
48
|
+
[api_name, schema]
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
{
|
|
@@ -59,10 +60,10 @@ module OpenAI
|
|
|
59
60
|
|
|
60
61
|
class << self
|
|
61
62
|
def optional(...)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
raise
|
|
63
|
+
message =
|
|
64
|
+
"`optional` is not supported for structured output APIs, " \
|
|
65
|
+
"use `#required` with `nil?: true` instead"
|
|
66
|
+
raise message
|
|
66
67
|
end
|
|
67
68
|
end
|
|
68
69
|
end
|
|
@@ -98,7 +98,10 @@ module OpenAI
|
|
|
98
98
|
}
|
|
99
99
|
defs.store(type, stored)
|
|
100
100
|
schema = blk.call
|
|
101
|
-
|
|
101
|
+
definition_name = path.map { _1.gsub("~", "~0").gsub("/", "~1") }.join("/")
|
|
102
|
+
pointer_token = definition_name.gsub("~", "~0").gsub("/", "~1")
|
|
103
|
+
escaped_name = URI::RFC2396_PARSER.escape(pointer_token, /[^A-Za-z0-9._~-]/)
|
|
104
|
+
ref_path.replace("#/$defs/#{escaped_name}")
|
|
102
105
|
stored.update(schema)
|
|
103
106
|
ref
|
|
104
107
|
end
|
|
@@ -140,7 +143,10 @@ module OpenAI
|
|
|
140
143
|
no_refs.each { _1.replace(_1.except(:$ref).merge(sch)) }
|
|
141
144
|
end
|
|
142
145
|
|
|
143
|
-
xformed = reused_defs.transform_keys
|
|
146
|
+
xformed = reused_defs.transform_keys do
|
|
147
|
+
pointer_token = URI::RFC2396_PARSER.unescape(_1.delete_prefix("#/$defs/"))
|
|
148
|
+
pointer_token.gsub("~1", "/").gsub("~0", "~")
|
|
149
|
+
end
|
|
144
150
|
xformed.empty? ? schema : {"$defs": xformed}.update(schema)
|
|
145
151
|
end
|
|
146
152
|
|
|
@@ -190,9 +196,10 @@ module OpenAI
|
|
|
190
196
|
OpenAI::UnionOf
|
|
191
197
|
OpenAI::BaseModel
|
|
192
198
|
]
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
199
|
+
message =
|
|
200
|
+
"#{type} does not implement the " \
|
|
201
|
+
"#{OpenAI::Helpers::StructuredOutput::JsonSchemaConverter} interface. " \
|
|
202
|
+
"Please use one of the supported types: #{models}"
|
|
196
203
|
raise ArgumentError.new(message)
|
|
197
204
|
end
|
|
198
205
|
end
|