Commit Graph

3 Commits

Author SHA1 Message Date
亢奋猫
26508591f8 refactor(paintings): migrate to v2 data layer and UI (#15154)
Co-authored-by: jidan745le <420511176@qq.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: SuYao <sy20010504@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
Signed-off-by: jidan745le <420511176@qq.com>
Signed-off-by: suyao <sy20010504@gmail.com>
2026-06-02 15:18:53 +08:00
SuYao
26d877e0fa hotfix(image-generation): fix gpt-image-2 / gpt-image-1.5 failures and stuck pending placeholder (#14578)
<!-- Template from
https://github.com/kubevirt/kubevirt/blob/main/.github/PULL_REQUEST_TEMPLATE.md?-->
<!--  Thanks for sending a pull request!  Here are some tips for you:
1. Consider creating this PR as draft:
https://github.com/CherryHQ/cherry-studio/blob/main/CONTRIBUTING.md
-->

<!--

🚨 Branch Strategy Change (Effective April 3, 2026) 🚨

The `main` branch is now under CODE FREEZE.

- main branch: Only accepts critical bug fixes via `hotfix/*` branches.
Fix PRs must be minimal in scope and must not include any refactoring
code.
- v2 branch: All new features, refactoring, and optimizations should be
submitted to the `v2` branch.

If you are submitting a bug fix to main, please ensure your PR is from a
`hotfix/*` branch.

-->

### What this PR does

Before this PR:

- `gpt-image-2` (and any image-edit request routed through AiHubMix /
NewAPI / CherryIN) failed with `common.UnmarshalBodyReusable failed:
bind request body failed: invalid character '-' in numeric literal`. The
providers hard-coded `Content-Type: application/json` in
`authHeaders()`, but `OpenAICompatibleImageModel.doGenerate` uses
`postFormDataToApi` for `/images/edits` and relies on `fetch` to
auto-set `multipart/form-data; boundary=...`. Forcing JSON made the
server receive a multipart body under a JSON content-type and choke on
the leading `--boundary`.
- Even when the request reached the server, image uploads failed with
`unsupported mimetype ('image')` because `collectImagesFromMessages`
built data URIs using `block.file.type` — which is the `FileType` enum
(`"image"`), not a MIME type.
- Some saved/pasted images produced `ENOENT: ... <uuid>jpg` because
`saveBase64Image` / `savePastedImage` return `ext` without the leading
dot, and the caller was concatenating `file.id + file.ext`.
- After a successful image generation, a `BeatLoader` placeholder kept
spinning next to the finished image. `fetchImageGeneration` only emitted
`LLM_RESPONSE_COMPLETE`; the trailing loader in `Blocks/index.tsx` is
driven by `isMessageProcessing(message)`, which is only cleared when
`onComplete` runs on `BLOCK_COMPLETE`.
- If a `file` chunk arrived without a preceding `IMAGE_CREATED`, the
initial UNKNOWN placeholder block was never claimed, leaving an orphan
spinner below the image.
- `gpt-image-2` / `gpt-image-1.5` / `gpt-image-1*` / `chatgpt-image-*`
generations through any OpenAI-compatible provider still failed with
`400 Unknown parameter: 'response_format'` (see #14485, #14540, #14579).
The earlier `@ai-sdk/openai@3.0.53` patch (#14488) only covered
`OpenAIImageModel` (direct OpenAI + Azure via `@ai-sdk/azure`);
`OpenAICompatibleImageModel.doGenerate` unconditionally added
`response_format: "b64_json"` to the `/images/generations` body, so
every provider whose image model is wired to
`OpenAICompatibleImageModel` kept hitting the 400 — including `type:
'openai-compatible'` and the AiHubMix / NewAPI / CherryIN gateway image
models in this repo.

After this PR:

- `aihubmix-provider`, `newapi-provider`, and `cherryin-provider` no
longer hard-code `Content-Type` in auth headers. `postJsonToApi` still
defaults JSON for other endpoints, and `postFormDataToApi` is free to
let `fetch` set `multipart/form-data; boundary=...` for `/images/edits`.
- `collectImagesFromMessages` reads via
`window.api.file.base64Image(block.file.name)`, which returns a correct
`data:image/<ext>;base64,...` URI (with `jpg → jpeg` normalization) and
uses the consistent on-disk filename regardless of whether the stored
`ext` has a leading dot.
- `fetchImageGeneration` now emits `ChunkType.BLOCK_COMPLETE` before
`LLM_RESPONSE_COMPLETE`, so `onComplete` runs and the assistant message
transitions out of `PROCESSING`.
- `onImageGenerated` reuses the initial UNKNOWN placeholder when one
exists, so a `file` chunk without a prior `IMAGE_CREATED` no longer
leaves an orphan spinner.
- `gpt-image-2` is registered in `IMAGE_ENHANCEMENT_MODELS`.
- `patches/@ai-sdk__openai-compatible@2.0.37.patch` is extended with the
same `hasDefaultResponseFormat` guard that `@ai-sdk/openai` already
uses. `OpenAICompatibleImageModel.doGenerate` now spreads
`response_format: "b64_json"` only when the model ID does **not** start
with `chatgpt-image-`, `gpt-image-1-mini`, `gpt-image-1.5`,
`gpt-image-1`, or `gpt-image-2`. This fixes #14485, #14540, #14579 for
`openai-compatible` typed providers and the AiHubMix / NewAPI / CherryIN
gateways.

Fixes #14485
Fixes #14540
Fixes #14579

### Why we need it and why it was done in this way

The following tradeoffs were made:

- The `ext`-with-or-without-dot inconsistency exists in historical DB
records, so patching only `FileStorage.ts` would not help already-saved
rows. The fix uses `file.name` at the consumer side — always the real
on-disk filename across every save path — without touching the storage
layer.
- The provider `authHeaders()` change could theoretically affect
non-JSON, non-multipart requests, but all SDK models in use go through
`postJsonToApi`, which already defaults `Content-Type: application/json`
internally, so behavior is preserved.
- Extending the `@ai-sdk/openai-compatible` patch rather than overriding
`response_format` per-provider keeps the fix in one place and
automatically covers every caller of `OpenAICompatibleImageModel`
(current and future). The guard list mirrors the upstream
`@ai-sdk/openai` list, so the two stay in sync.

The following alternatives were considered:

- Normalizing `ext` in `FileStorage.saveBase64Image` /
`savePastedImage`: rejected because it would not repair existing records
and could regress other callers that have compensated for the dotless
form.
- Overriding `Content-Type` only inside the image model factory:
rejected as more surface area than needed; the global `authHeaders()`
already defers to `postJsonToApi` defaults.
- Stripping `response_format` in each custom provider (AiHubMix / NewAPI
/ CherryIN) instead of patching the SDK: rejected — it would leave
`type: 'openai-compatible'` users still broken and duplicate the
model-prefix list in three places.

Links to places where the discussion took place: #14485, #14540, #14579,
#14488

### Breaking changes

<!-- optional -->

None.

### Special notes for your reviewer

<!-- optional -->

Scope is intentionally minimal for the main branch freeze: no
refactoring, no new abstractions. Surface changes:

- `authHeaders()` in three custom providers (drops one hard-coded
header).
- `collectImagesFromMessages` (one helper, swaps input building).
- `fetchImageGeneration` (one extra chunk emission).
- `onImageGenerated` (placeholder-claim fallback).
- `IMAGE_ENHANCEMENT_MODELS` (one new entry).
- `patches/@ai-sdk__openai-compatible@2.0.37.patch` (adds
`hasDefaultResponseFormat` helper + conditional spread of
`response_format`; mirrors `@ai-sdk/openai@3.0.53` patch).

Drop the openai-compatible patch addition once
`@ai-sdk/openai-compatible` ships the equivalent check upstream.

### Checklist

This checklist is not enforcing, but it's a reminder of items that could
be relevant to every PR.
Approvers are expected to review this list.

- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: [Write code that humans can
understand](https://en.wikiquote.org/wiki/Martin_Fowler#code-for-humans)
and [Keep it simple](https://en.wikipedia.org/wiki/KISS_principle)
- [x] Refactor: You have [left the code cleaner than you found it (Boy
Scout
Rule)](https://learning.oreilly.com/library/view/97-things-every/9780596809515/ch08.html)
- [x] Upgrade: Impact of this change on upgrade flows was considered and
addressed if required
- [ ] Documentation: A [user-guide update](https://docs.cherry-ai.com)
was considered and is present (link) or not required. Check this only
when the PR introduces or changes a user-facing feature or behavior.
- [x] Self-review: I have reviewed my own code (e.g., via
[`/gh-pr-review`](/.claude/skills/gh-pr-review/SKILL.md), `gh pr diff`,
or GitHub UI) before requesting review from others

### Release note

<!--  Write your release note:
1. Enter your extended release note in the below block. If the PR
requires additional action from users switching to the new release,
include the string "action required".
2. If no release note is required, just write "NONE".
3. Only include user-facing changes (new features, bug fixes visible to
users, UI changes, behavior changes). For CI, maintenance, internal
refactoring, build tooling, or other non-user-facing work, write "NONE".
-->

```release-note
Fix image generation and editing for gpt-image-2 / gpt-image-1.5 / gpt-image-1* / chatgpt-image-* across AiHubMix, NewAPI, CherryIN, Azure OpenAI, and other OpenAI-compatible providers: requests no longer fail with `Unknown parameter: 'response_format'`, multipart content-type, or mimetype errors; generated/pasted images are resolved correctly; and the loading placeholder disappears once the image is rendered.
```

---------

Signed-off-by: suyao <sy20010504@gmail.com>
2026-04-26 00:34:41 +08:00
suyao
6b14fcd5ce refactor: bump AI SDK deps and fix provider API host formatting
- Bump @ai-sdk/* packages to latest versions and update patches
- Fix newapi provider: defer API version suffix to build phase so gemini
  endpoints get /v1beta instead of /v1
- Fix Azure provider: split host formatting so azure-anthropic (Claude)
  endpoints don't get the /openai suffix meant for Azure OpenAI
- Re-add @ai-sdk/google getModelPath patch (includes("models/") check)
- Support azure-openai provider type in Claude Code agent service by
  auto-constructing the /anthropic base URL for Claude models

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: suyao <sy20010504@gmail.com>
2026-04-03 14:18:02 +08:00