@opencode/ai 0.0.0-dev-21747 → 0.0.0-dev-21748

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.
@@ -1,10 +1,11 @@
1
1
  import { Effect, Schema, Stream } from "effect";
2
2
  import { ImageFinishEvent, ImageModel, ImageOutputEvent, ImagePartialEvent, } from "../image.js";
3
3
  import { Media } from "../media.js";
4
+ import { classifyProviderFailure } from "../provider-error.js";
4
5
  import { Framing } from "../route/framing.js";
5
6
  import { MediaProtocol } from "../route/media-protocol.js";
6
7
  import { MediaRoute } from "../route/media.js";
7
- import { mergeJsonRecords, ProviderID } from "../schema/index.js";
8
+ import { AIError, mergeJsonRecords, ProviderID } from "../schema/index.js";
8
9
  import { ProviderShared } from "./shared.js";
9
10
  import { ImageSizing } from "./utils/image-sizing.js";
10
11
  import { MediaInput } from "./utils/media-input.js";
@@ -52,6 +53,10 @@ const StreamEvent = Schema.Union([
52
53
  output_format: Schema.String,
53
54
  usage: Schema.optional(Usage),
54
55
  }),
56
+ Schema.Struct({
57
+ type: Schema.Literal("error"),
58
+ error: Schema.optional(Schema.Struct({ message: Schema.optional(Schema.String) })),
59
+ }),
55
60
  ]);
56
61
  const decodeEvent = route.decodeFrame(StreamEvent);
57
62
  const decodeDocument = Schema.decodeUnknownEffect(Schema.fromJsonString(OpenAIImageResponse));
@@ -160,6 +165,13 @@ const eventImage = (frame, label, data, format, size) => MediaInput.decodedAsset
160
165
  });
161
166
  const onEvent = Effect.fnUntraced(function* (state, frame) {
162
167
  const event = yield* decodeEvent(frame);
168
+ if (event.type === "error")
169
+ return yield* new AIError({
170
+ reason: classifyProviderFailure({
171
+ message: `${route.name} stream failed: ${event.error?.message ?? "unknown error"}`,
172
+ rawBody: frame,
173
+ }),
174
+ });
163
175
  const format = event.output_format;
164
176
  if ("partial_image_index" in event) {
165
177
  const image = yield* eventImage(frame, `${route.name} partial image`, event.b64_json, format, event.size);
@@ -132,7 +132,7 @@ const INVALID_REQUEST_CODES = new Set([
132
132
  // Azure OpenAI reports `content_filter` with `innererror.code` ResponsibleAIPolicyViolation.
133
133
  // OpenRouter tags provider failures with a typed `error_type`; its Responses skin also
134
134
  // emits `image_content_policy_violation` as the native code. Alibaba Model Studio reports
135
- // moderation rejections as `DataInspectionFailed`.
135
+ // moderation rejections as `DataInspectionFailed`. OpenAI Images reports `moderation_blocked`.
136
136
  const CONTENT_POLICY_CODES = new Set([
137
137
  "content_filter",
138
138
  "content_moderation",
@@ -141,6 +141,7 @@ const CONTENT_POLICY_CODES = new Set([
141
141
  "image_content_policy_violation",
142
142
  "refusal",
143
143
  "datainspectionfailed",
144
+ "moderation_blocked",
144
145
  ]);
145
146
  // OpenCode Zen replaces upstream codes outside its allow-list but keeps the original
146
147
  // as a `[code]` label at the start of the rewritten message.
@@ -154,8 +155,8 @@ const QUOTA_TEXT = /insufficient[-_\s]?(?:quota|balance)|quota[-_\s]?exceeded|bu
154
155
  // Policy rejections without a dedicated code, matched against the provider's own
155
156
  // explanation only. OpenAI reuses `invalid_prompt` for usage-policy rejections while
156
157
  // Bedrock Mantle reuses it for schema validation; Anthropic reports blocked output
157
- // under `invalid_request_error`.
158
- const CONTENT_POLICY_TEXT = /violating our usage policy|blocked by content filtering policy|content[-_\s]?policy|rejected as a result of our safety system|detected potentially unsafe or sensitive content/i;
158
+ // under `invalid_request_error`. The Vercel AI Gateway drops OpenAI's `moderation_blocked` code.
159
+ const CONTENT_POLICY_TEXT = /violating our usage policy|blocked by content filtering policy|content[-_\s]?policy|rejected (?:as a result of our|by the) safety system|detected potentially unsafe or sensitive content/i;
159
160
  const SERVER_ERROR_TEXT = /\b(?:try again|(?:please |you can )?retry (?:the |this |your )?request|try (?:the |this |your )?request again|(?:currently |temporarily )?at capacity|overloaded|temporarily unavailable|service[-_\s]?unavailable|(?:server|internal)[-_\s]?error|server (?:is )?busy|provider returned (?:an )?error|resource[-_\s]?exhausted|upstream (?:connect|connection|request)|request buffer limit while retrying upstream)\b/i;
160
161
  const Message = Schema.String.check(Schema.isPattern(/\S/));
161
162
  const messageAt = (fields, message) => Schema.Struct(fields).pipe(Schema.decodeTo(Schema.String, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-21747",
3
+ "version": "0.0.0-dev-21748",
4
4
  "name": "@opencode/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,7 +35,7 @@
35
35
  "devDependencies": {
36
36
  "@clack/prompts": "1.0.0-alpha.1",
37
37
  "@effect/platform-node": "4.0.1",
38
- "@opencode/http-recorder": "0.0.0-dev-21747",
38
+ "@opencode/http-recorder": "0.0.0-dev-21748",
39
39
  "@tsconfig/bun": "1.0.9",
40
40
  "@types/bun": "1.4.3",
41
41
  "@typescript/native": "npm:typescript@7.1.0-beta",
@@ -45,7 +45,7 @@
45
45
  "@aws-sdk/credential-providers": "3.1057.0",
46
46
  "@smithy/eventstream-codec": "4.2.14",
47
47
  "@smithy/util-utf8": "4.2.2",
48
- "@opencode/schema": "0.0.0-dev-21747",
48
+ "@opencode/schema": "0.0.0-dev-21748",
49
49
  "aws4fetch": "1.0.20",
50
50
  "effect": "4.0.1",
51
51
  "google-auth-library": "10.5.0"