Repository navigation
feat(images): Generate images with GPT Image through the AI SDK - #2317
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Sentry Evalsjunior-behavioral
Base commit junior-guardian
Base commit junior-integration
Base commit |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3c0c46a. Configure here.
sentry-junior
Bot
force-pushed
the
feat/image-only-models
branch
from
October 29, 2026 04:09
ab6df7b to
07159e5
Compare
sentry-junior
Bot
force-pushed
the
feat/image-only-models
branch
from
October 29, 2026 04:17
9b83d84 to
8f645e3
Compare
sentry-junior
Bot
force-pushed
the
feat/image-only-models
branch
from
October 29, 2026 04:53
8f645e3 to
cd6aa99
Compare
sentry-junior
Bot
force-pushed
the
feat/image-only-models
branch
from
October 29, 2026 04:54
cd6aa99 to
04fd90a
Compare
This was referenced Oct 29, 2026
dcramer
pushed a commit
that referenced
this pull request
Oct 29, 2026
Junior apps no longer install `@sentry/nitro` themselves.
`@sentry/junior` now depends on it, and `@sentry/junior/nitro` exports
`withSentryConfig`.
The Sentry SDK keeps one client for each SDK version
(`globalThis.__SENTRY__[SDK_VERSION]`). `initSentry()` creates that
client with Junior's `@sentry/node`. If an app pins an `@sentry/nitro`
version that differs from Junior's `@sentry/node`, the Nitro runtime
hooks find no client. The error hook and request spans then do nothing,
and nothing reports it. Before this change, each app had to keep its
`@sentry/nitro` pin equal to the `@sentry/node` version inside
`@sentry/junior` by hand.
Now `@sentry/nitro` and `@sentry/node` are next to each other in the
pnpm catalog, so they get bumped together. Apps write:
```ts
import { juniorNitro, withSentryConfig } from "@sentry/junior/nitro";
```
`withSentryConfig` is a thin wrapper around the SDK function. It sets
one default: the build release name is `getDeploymentServiceVersion()`,
which is `<junior version>+<VERCEL_GIT_COMMIT_SHA>`. This is the same
release that `initSentry()` reports. Without this default,
`@sentry/nitro` names the build release after the bare commit SHA, so
source maps and runtime events would be under two different releases. An
explicit `release.name` still wins. The wrapper types come from
`@sentry/nitro`'s own signature, so `dist/nitro.d.ts` does not inline
Nitro's types.
The `apps/example` app, the `junior init` scaffold, and the
`existing-app` docs use the new import. The package README no longer
tells apps to install `@sentry/node`. That instruction is how
`getsentry/junior-prod` got an unused second copy
(`@sentry/node@10.49.0` next to Junior's 10.65.0).
Why now: `junior-prod` never adopted `withSentryConfig`, so production
source maps are never uploaded. Production stack frames point at
`_libs/@sentry/junior+[...].mjs` around line 226k with `js_no_source`.
getsentry/junior-prod#568 is the draft follow-up that uses this export.
**Trade-off:** every Junior app now installs `@sentry/nitro`,
`@sentry/bundler-plugin-core`, and `@sentry/cli`. `@sentry/junior`
already hard-depends on `@sentry/node`, so this seemed acceptable.
Without a Sentry auth token, the build logs a warning and skips the
upload.
**Not done here:** wrapping the Sentry setup inside `juniorNitro()`
itself. `@sentry/nitro` does not export the module factory for that, and
it must change the config before modules run. Doing it here would mean
copying SDK internals.
**Verification**
- `pnpm --filter @sentry/junior build`: `dist/nitro.js` imports
`@sentry/nitro` as an external dependency, and `dist/nitro.d.ts` stays
small (47 lines).
- `pnpm typecheck` and `pnpm lint` in `packages/junior`, and `pnpm
typecheck` in `apps/example`.
- `vitest run` on `init-cli`, `check-cli`, and `nitro-plugin-module`
passed.
- A temporary test with a mocked `@sentry/nitro` checked the release
default. It passed `0.264.0+deadbeef` when `VERCEL_GIT_COMMIT_SHA` was
set, kept an explicit name, and left the name undefined when no SHA was
set.
- `nitro build` in `apps/example`: one `@sentry/core` (10.65.0) is in
the output, and `scripts/check-vercel-output.mjs` passes.
- `pnpm build:ci` in `junior-prod` linked to this branch: the build
passes with one `@sentry/core` (10.65.0). The upload step ran
`sentry-cli releases new 0.264.0+deadbeef`. The sandbox token was not
authorized, so the step got a 401 and created no release.
- `pnpm release:check` passes.
- The behavioral evals failure also occurs on unrelated PRs (#2339,
#2328, #2317, #2313). The integration evals pass.
- I did not test a real source map upload.
<!-- junior-request-attribution:start -->
via **Francesco Gringl-Novy**.
<!-- junior-request-attribution:end -->
<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AD0CA8FXFXAS%3A1793266810.973359 -->
--
[View Junior
Session](https://junior-prod.sentry.dev/conversations/slack%3AD0CA8FXFXAS%3A1793266810.973359)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AD0CA8FXFXAS%3A1793266810.973359/?project=4510944073809921)
<!-- junior-session-footer:end -->
---------
Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Francesco Gringl-Novy <francesco.novy@sentry.io>
dcramer
added a commit
that referenced
this pull request
Oct 29, 2026
The integration eval `conversation/delivery.eval.ts` > "when asked for a top-level channel post, explain the limitation instead" fails on `main` since #2315. This change fixes the Slack delivery rule in `prompt.ts` that the eval checks. **Why it fails** #2315 merged the two Slack delivery rules into one line. The merged line ties the explanation to tool calls ("explain the limitation instead of calling a tool"). It also says that assistant text goes to the current conversation. For `post this to the channel: deploy is unblocked`, a plain reply with the text calls no tool and lands in the current conversation, so it looked allowed. Both live samples on #2317 replied only `deploy is unblocked`. Before #2315, every recorded live sample passed, and each reply used the old wording: "I can't create a new top-level channel post…". **Is the eval correct?** Yes. The mention is a top-level channel message. Junior can only reply in the thread under it, so a reply with only the requested text is wrong. The rubric follows the product rule, and the eval file has not changed since Oct 8. **Change** The delivery rule stays one line, so the #2315 cleanup holds. It now: - states the fact: a reply is not a channel post, and Junior cannot create a new top-level channel post - says what to do for any "post to a channel or anywhere else" request: explain the limitation, call no tool, and do not reply with only the requested text or present it as delivered This PR also removes `tests/unit/prompt.test.ts`. It only checked that the prompt output contains static strings. Evals own prompt behavior. **Verification** - `tsc --noEmit` passes for `@sentry/junior`. - I could not run evals live locally (no AI Gateway or Roach credentials). The integration eval workflow does not watch `packages/junior/src/**`, so this PR has the `trigger-evals-integration` label. The prompt change makes every model request new, so the whole integration suite runs live. That gives a real comparison against the failures on #2317. **Open question** Integration and behavioral evals do not run for `packages/junior/src/**` changes, and no eval suite runs on `main`. That gap let #2315 merge without this eval. It is out of scope here. <!-- junior-request-attribution:start --> via **David Cramer**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC0B595QDZLL%3A1793251347.186889 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/slack%3AC0B595QDZLL%3A1793251347.186889) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0B595QDZLL%3A1793251347.186889/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: David Cramer <david@sentry.io>
Switch imageGenerate to the AI SDK `generateImage()` call with a Gateway image model. Prompt-only calls generate an image. Calls with input images edit those images. The SDK owns the request, the response parsing and the retries, so the hand-written chat completions request goes away. The default image model is now `openai/gpt-image-2`. AI_IMAGE_MODEL must name a Gateway image model. A language model such as `google/gemini-3-pro-image` gets a config hint error. Co-Authored-By: Greg Pstrucha <greg.pstrucha@sentry.io>
The tool error check rejects a throw hidden behind a helper. Throw the config error where it happens and move its baseline entry to the new line. The model id comes from app config, so it stays a plain Error. Co-Authored-By: Greg Pstrucha <greg.pstrucha@sentry.io>
sentry-junior
Bot
force-pushed
the
feat/image-only-models
branch
from
November 2, 2026 21:50
cb7b01c to
9092ff8
Compare
Contributor
|
3 screenshot changes — 3 changed · 0 added · 0 removed Review screenshots in Frameshift
|
dcramer
approved these changes
Nov 2, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Switch
imageGenerateto the AI SDK and make GPT Image the default image model.The tool now calls
generateImage()withgateway.imageModel(modelId)from@ai-sdk/gateway. A prompt-only call generates an image. A call with input images sends them asprompt: { text, images }, which the gateway runs as an edit. The SDK owns the request, response parsing and retries, so the hand-written/v1/chat/completionsrequest is gone.imageGenerationModelId/AI_IMAGE_MODELis nowopenai/gpt-image-2(wasgoogle/gemini-3-pro-image).AI_IMAGE_MODELmust name an AI Gateway image model. A language model such as Gemini image gets the existing config hint error.resolveGatewayCredential(), likewebSearch./v4/ai/image-modelinstead of chat completions.Verification
openai/gpt-image-2returned PNGs (about 11s and 13s).google/gemini-3-pro-imageon this path returns "is a language model, not an image model".imageGenerateand chat config pass. Typecheck, oxlint and the file-length check pass.This replaces the earlier prototype on this branch, which looked up the model type and kept two raw
fetchpaths.via greg.pstrucha.
--
View Junior Session [Sentry]