@robota-sdk/agent-provider-openai 3.0.0-beta.83 → 3.0.0-beta.84

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @robota-sdk/agent-provider-openai
2
2
 
3
+ ## 3.0.0-beta.84
4
+
5
+ ### Patch Changes
6
+
7
+ - 9c6a8db: Provider adapters now throw the specific typed failure a vendor response describes, instead of a
8
+ generic `ProviderError` carrying only its status. A 401/403 (or a vendor-specific authentication type)
9
+ becomes an `AuthenticationError`; a response naming an unavailable model — by code or, when the vendor
10
+ gives neither (Anthropic's and Gemini's real bodies carry no distinguishing code), by its message —
11
+ becomes a `ModelNotAvailableError`, whose `model` is now optional since a bare HTTP status does not
12
+ always name one; a transport failure becomes a `NetworkError`, which now also carries the provider
13
+ name. A 429's `retry-after` header (a delta or an HTTP-date) is read into `RateLimitError.retryAfter`
14
+ when the vendor sends one.
15
+
16
+ Vendor error text is scrubbed of anything that reads like a credential — an `Authorization`/`Bearer`
17
+ value, an API key in a header, query string or JSON body, a bare `sk-`/`AIza…` token — before it is
18
+ kept on any of these typed errors, so a misconfigured self-hosted gateway that echoes a request back
19
+ can no longer leak one into a message or a "Details" disclosure. The new `scrubSecrets` export applies
20
+ the same scrubbing to arbitrary text.
21
+
22
+ - Updated dependencies [9f46375]
23
+ - Updated dependencies [9c6a8db]
24
+ - @robota-sdk/agent-core@3.0.0-beta.84
25
+ - @robota-sdk/agent-provider-openai-compatible@3.0.0-beta.84
26
+
3
27
  ## 3.0.0-beta.83
4
28
 
5
29
  ### Patch Changes
package/README.md CHANGED
@@ -44,6 +44,7 @@ const provider = new OpenAIProvider({
44
44
  baseURL: 'https://ai-gateway.vercel.sh/v1',
45
45
  });
46
46
  // then: defaultModel: { provider: 'openai', model: 'anthropic/claude-sonnet-4-5' }
47
+ // (a gateway model slug, passed through to the gateway as written)
47
48
  ```
48
49
 
49
50
  The API surface follows `baseURL` unless `apiSurface` is set:
@@ -63,28 +64,52 @@ tool is sent with `strict: true` on both the Responses and Chat Completions surf
63
64
  OpenAI-compatible endpoint that rejects the `strict` field needs `strictTools` left off. With
64
65
  `strictTools` off (the default) tool schemas are forwarded as authored and no `strict` field is sent.
65
66
 
67
+ ### Reasoning effort and web tools
68
+
69
+ A reasoning-effort selection (`defaultModel.effort` on a `Robota` agent, or `effort` on a chat call)
70
+ is sent as `reasoning.effort` only on the official Responses API (no `baseURL`) and only for models
71
+ in the provider's verified effort table. On Chat Completions, behind a custom `baseURL`, or for other
72
+ models, the effort is reported as not applied and no native field is sent. When a call selects an
73
+ effort, a static `reasoning` option's `effort` must equal the value sent; a different value, or any
74
+ value when no native effort is sent, is rejected.
75
+
76
+ Native web search and web fetch are not available through this provider on either surface. Setting
77
+ `nativeWebTools.webSearch` or `nativeWebTools.webFetch` to `true` makes the constructor throw.
78
+
66
79
  ## Options
67
80
 
68
81
  `new OpenAIProvider(options: IOpenAIProviderOptions)`. One of `apiKey`, `client` or `executor` is
69
82
  required; the constructor throws otherwise.
70
83
 
71
- | Option | Type | Description |
72
- | -------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
73
- | `apiKey` | `string` | API key used to create the `openai` client. |
74
- | `baseURL` | `string` | Endpoint base URL (default `https://api.openai.com/v1`). Also switches the default surface to Chat Completions. |
75
- | `apiSurface` | `'responses' \| 'chat-completions'` | Forces the API surface instead of deriving it from `baseURL`. |
76
- | `defaultModel` | `string` | Model used when a call does not name one. |
77
- | `organization` | `string` | OpenAI organization ID. |
78
- | `timeout` | `number` | Request timeout in milliseconds. |
79
- | `strictTools` | `boolean` | Closes tool object schemas and requests OpenAI strict function calling (see above). |
80
- | `responseFormat` | `'text' \| 'json_object' \| 'json_schema'` | Response format; `'json_schema'` uses `jsonSchema`. |
81
- | `jsonSchema` | `IOpenAIJsonSchemaDefinition` | Schema for Structured Outputs when `responseFormat` is `'json_schema'`. |
82
- | `reasoning` | `IOpenAIResponsesReasoningOptions` | Responses API reasoning controls (`effort`, `summary`). |
83
- | `includeStreamUsage` | `boolean` | Requests token usage on streaming Chat Completions turns (default `true`). |
84
- | `client` | `OpenAI` | A pre-built `openai` client, used instead of `apiKey`. |
85
- | `executor` | `IExecutor` | Delegates chat calls to an executor instead of calling the API directly. |
86
- | `payloadLogger` | `IPayloadLogger` | Logs a summary of each Chat Completions request (model, message count, tools present, temperature, max tokens). |
87
- | `logger` | `ILogger` | Logger for internal provider messages (silent by default). |
84
+ | Option | Type | Description |
85
+ | --------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
86
+ | `apiKey` | `string` | API key used to create the `openai` client. |
87
+ | `baseURL` | `string` | Endpoint base URL (default `https://api.openai.com/v1`). Also switches the default surface to Chat Completions. |
88
+ | `apiSurface` | `'responses' \| 'chat-completions'` | Forces the API surface instead of deriving it from `baseURL`. |
89
+ | `defaultModel` | `string` | Model used when a call does not name one. |
90
+ | `organization` | `string` | OpenAI organization ID. |
91
+ | `timeout` | `number` | Request timeout in milliseconds. |
92
+ | `strictTools` | `boolean` | Closes tool object schemas and requests OpenAI strict function calling (see above). |
93
+ | `responseFormat` | `'text' \| 'json_object' \| 'json_schema'` | Response format; `'json_schema'` uses `jsonSchema`. |
94
+ | `jsonSchema` | `IOpenAIJsonSchemaDefinition` | Schema for Structured Outputs when `responseFormat` is `'json_schema'`. |
95
+ | `reasoning` | `IOpenAIResponsesReasoningOptions` | Responses API reasoning controls (`effort`, `summary`). |
96
+ | `store` | `boolean` | Responses API only: whether OpenAI stores the response; when unset, OpenAI's default applies. |
97
+ | `includeEncryptedReasoning` | `boolean` | Responses API only: asks for encrypted reasoning items, so reasoning can continue without server-side state. |
98
+ | `nativeWebTools` | `IOpenAINativeWebToolsOptions` | Not supported: `true` for `webSearch` or `webFetch` throws (see above). |
99
+ | `includeStreamUsage` | `boolean` | Requests token usage on streaming Chat Completions turns (default `true`). |
100
+ | `client` | `OpenAI` | A pre-built `openai` client, used instead of `apiKey`. |
101
+ | `executor` | `IExecutor` | Delegates chat calls to an executor instead of calling the API directly. |
102
+ | `payloadLogger` | `IPayloadLogger` | Logs a summary of each Chat Completions request (model, message count, tools present, temperature, max tokens). |
103
+ | `logger` | `ILogger` | Logger for internal provider messages (silent by default). |
104
+
105
+ ## Errors
106
+
107
+ A failed API call throws a typed error from `@robota-sdk/agent-core`: `RateLimitError` (with
108
+ `retryAfter` from the `retry-after` header), `AuthenticationError`, `ModelNotAvailableError` or
109
+ `NetworkError` when the failure is one of those, otherwise a `ProviderError` that carries the HTTP
110
+ `status` and the error `type` the endpoint reported and keeps the SDK error as `originalError`. Both
111
+ API surfaces, streaming included, map failures the same way. An aborted call rethrows the abort
112
+ unchanged. See [Provider failures](../../content/guide/error-handling.md#provider-failures).
88
113
 
89
114
  ## Exports
90
115
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-provider-openai",
3
- "version": "3.0.0-beta.83",
3
+ "version": "3.0.0-beta.84",
4
4
  "description": "OpenAI provider implementation for Robota SDK",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -78,10 +78,11 @@
78
78
  "url": "https://github.com/woojubb/robota.git",
79
79
  "directory": "packages/agent-provider-openai"
80
80
  },
81
+ "homepage": "https://docs.robota.io/en/packages/agent-provider-openai/",
81
82
  "dependencies": {
82
83
  "openai": "^4.104.0",
83
- "@robota-sdk/agent-core": "3.0.0-beta.83",
84
- "@robota-sdk/agent-provider-openai-compatible": "3.0.0-beta.83"
84
+ "@robota-sdk/agent-core": "3.0.0-beta.84",
85
+ "@robota-sdk/agent-provider-openai-compatible": "3.0.0-beta.84"
85
86
  },
86
87
  "volta": {
87
88
  "extends": "../../package.json"
@@ -3,7 +3,14 @@
3
3
  * collapsing into a bare `Error` whose only trace of the status is its message text.
4
4
  */
5
5
 
6
- import { ProviderError, RateLimitError, classifyProviderFailure } from '@robota-sdk/agent-core';
6
+ import {
7
+ AuthenticationError,
8
+ ModelNotAvailableError,
9
+ NetworkError,
10
+ ProviderError,
11
+ RateLimitError,
12
+ classifyProviderFailure,
13
+ } from '@robota-sdk/agent-core';
7
14
  import { APIConnectionError, APIError, APIUserAbortError } from 'openai';
8
15
  import { describe, expect, it, vi } from 'vitest';
9
16
 
@@ -44,7 +51,6 @@ const statuses: Array<[number, string]> = [
44
51
  [529, 'overloaded_error'],
45
52
  [503, 'server_error'],
46
53
  [500, 'server_error'],
47
- [401, 'invalid_api_key'],
48
54
  ];
49
55
 
50
56
  describe('OpenAI provider errors', () => {
@@ -73,7 +79,7 @@ describe('OpenAI provider errors', () => {
73
79
  expect(classifyProviderFailure(error)).toEqual({ switchable: false, reason: 'aborted' });
74
80
  });
75
81
 
76
- it(`${surface} classifies an SDK connection failure as network`, async () => {
82
+ it(`${surface} classifies an SDK connection failure as a NetworkError`, async () => {
77
83
  const provider = providerWith(
78
84
  surface,
79
85
  new APIConnectionError({
@@ -82,11 +88,11 @@ describe('OpenAI provider errors', () => {
82
88
  }),
83
89
  );
84
90
  const error = await failure(() => provider.chat(messages, { model: 'gpt-4o' }));
85
- expect(error).toBeInstanceOf(ProviderError);
91
+ expect(error).toBeInstanceOf(NetworkError);
86
92
  expect(classifyProviderFailure(error)).toEqual({ switchable: false, reason: 'network' });
87
93
  });
88
94
 
89
- it(`${surface} classifies a 400 model_not_found as model-unavailable`, async () => {
95
+ it(`${surface} maps a 400 model_not_found to ModelNotAvailableError`, async () => {
90
96
  const provider = providerWith(
91
97
  surface,
92
98
  APIError.generate(
@@ -99,6 +105,7 @@ describe('OpenAI provider errors', () => {
99
105
  ),
100
106
  );
101
107
  const error = await failure(() => provider.chat(messages, { model: 'gpt-4o' }));
108
+ expect(error).toBeInstanceOf(ModelNotAvailableError);
102
109
  expect(classifyProviderFailure(error)).toEqual({
103
110
  switchable: true,
104
111
  reason: 'model-unavailable',
@@ -110,5 +117,27 @@ describe('OpenAI provider errors', () => {
110
117
  const error = await failure(() => provider.chat(messages, { model: 'gpt-4o' }));
111
118
  expect(error).toBeInstanceOf(RateLimitError);
112
119
  });
120
+
121
+ it(`${surface} chat maps 401 to AuthenticationError, carrying the provider name`, async () => {
122
+ const provider = providerWith(surface, httpError(401, 'invalid_api_key'));
123
+ const error = await failure(() => provider.chat(messages, { model: 'gpt-4o' }));
124
+ expect(error).toBeInstanceOf(AuthenticationError);
125
+ expect((error as AuthenticationError).provider).toBe('openai');
126
+ });
127
+
128
+ it(`${surface} chat reads the retry-after header off a 429 into RateLimitError.retryAfter`, async () => {
129
+ const provider = providerWith(
130
+ surface,
131
+ APIError.generate(
132
+ 429,
133
+ { error: { type: 'rate_limit_exceeded', message: 'slow down' } },
134
+ undefined,
135
+ { 'retry-after': '12' },
136
+ ),
137
+ );
138
+ const error = await failure(() => provider.chat(messages, { model: 'gpt-4o' }));
139
+ expect(error).toBeInstanceOf(RateLimitError);
140
+ expect((error as RateLimitError).retryAfter).toBe(12);
141
+ });
113
142
  }
114
143
  });