@cartanova/qgrid-cli 2.10.5 → 2.10.7

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.
Files changed (96) hide show
  1. package/bundle/dist/application/monit/monit.frame.js +1 -1
  2. package/bundle/dist/application/qgrid/expired-token-reminder.js +1 -1
  3. package/bundle/dist/application/qgrid/oauth.js +1 -1
  4. package/bundle/dist/application/qgrid/qgrid-image-generation.js +7 -11
  5. package/bundle/dist/application/qgrid/qgrid-response-format.js +4 -4
  6. package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +26 -7
  7. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +12 -7
  8. package/bundle/dist/application/qgrid/qgrid.frame.js +24 -10
  9. package/bundle/dist/application/qgrid/token-death.js +1 -1
  10. package/bundle/dist/application/qgrid/token-window-keepalive.js +1 -1
  11. package/bundle/dist/application/qgrid/tool-emulation.js +1 -1
  12. package/bundle/dist/application/request-log/request-log.model.js +5 -3
  13. package/bundle/dist/application/sonamu.generated.js +4 -1
  14. package/bundle/dist/application/sonamu.generated.sso.js +3 -1
  15. package/bundle/dist/i18n/sd.generated.js +4 -3
  16. package/bundle/dist/migrations/20261016155350_alter_request_logs_add1_alter1.js +15 -0
  17. package/bundle/dist/sonamu.config.js +2 -2
  18. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +1 -1
  19. package/bundle/dist/utils/providers/anthropic/anthropic-thinking.js +1 -1
  20. package/bundle/dist/utils/providers/common/model-cost.js +14 -3
  21. package/bundle/dist/utils/providers/openai/openai-backend-protocol.js +13 -2
  22. package/bundle/dist/utils/providers/openai/openai-direct-client.js +5 -4
  23. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +111 -57
  24. package/bundle/dist/utils/providers/openai/openai-image-generation.js +25 -17
  25. package/bundle/dist/utils/providers/openai/openai-refresh.js +1 -1
  26. package/bundle/src/application/qgrid/qgrid-image-generation.test.ts +19 -8
  27. package/bundle/src/application/qgrid/qgrid-image-generation.ts +17 -24
  28. package/bundle/src/application/qgrid/qgrid-response-format.test.ts +4 -2
  29. package/bundle/src/application/qgrid/qgrid-response-format.ts +7 -10
  30. package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +105 -9
  31. package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +47 -5
  32. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +160 -2
  33. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +40 -18
  34. package/bundle/src/application/qgrid/qgrid.frame.test.ts +74 -0
  35. package/bundle/src/application/qgrid/qgrid.frame.ts +39 -4
  36. package/bundle/src/application/request-log/request-log.entity.json +11 -2
  37. package/bundle/src/application/request-log/request-log.model.ts +4 -0
  38. package/bundle/src/application/sonamu.generated.http +2 -2
  39. package/bundle/src/application/sonamu.generated.sso.ts +2 -0
  40. package/bundle/src/application/sonamu.generated.ts +4 -0
  41. package/bundle/src/i18n/sd.generated.ts +3 -2
  42. package/bundle/src/migrations/20261016155350_alter_request_logs_add1_alter1.ts +15 -0
  43. package/bundle/src/utils/providers/common/model-cost.test.ts +28 -0
  44. package/bundle/src/utils/providers/common/model-cost.ts +19 -2
  45. package/bundle/src/utils/providers/openai/openai-backend-protocol.test.ts +23 -1
  46. package/bundle/src/utils/providers/openai/openai-backend-protocol.ts +24 -2
  47. package/bundle/src/utils/providers/openai/openai-direct-client.test.ts +15 -0
  48. package/bundle/src/utils/providers/openai/openai-direct-client.ts +14 -4
  49. package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +223 -32
  50. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +174 -72
  51. package/bundle/src/utils/providers/openai/openai-image-generation.test.ts +89 -0
  52. package/bundle/src/utils/providers/openai/openai-image-generation.ts +26 -15
  53. package/bundle/web-dist/client/assets/cost-DH2jZbrw.js +1 -0
  54. package/bundle/web-dist/client/assets/index-Bw2s88Ot.css +1 -0
  55. package/bundle/web-dist/client/assets/{index-Dxw7DiXF.js → index-CwqqPF4F.js} +4 -4
  56. package/bundle/web-dist/client/assets/logs-CZgl1RSu.js +1 -0
  57. package/bundle/web-dist/client/assets/{monit-E8eGW1Sj.js → monit-TarvUWiE.js} +1 -1
  58. package/bundle/web-dist/client/assets/request-model-CojtinqK.js +1 -0
  59. package/bundle/web-dist/client/assets/{routes-7spLfIGZ.js → routes-BXR6OLah.js} +2 -2
  60. package/bundle/web-dist/client/assets/{services.generated-jnPHYMER.js → services.generated-BsieU37H.js} +1 -1
  61. package/bundle/web-dist/client/assets/{settings-BdosZFNC.js → settings-B1y5MNSU.js} +1 -1
  62. package/bundle/web-dist/client/assets/show-CG-BAdhS.js +2 -0
  63. package/bundle/web-dist/client/assets/{tokens-CBdHb2_U.js → tokens-D9tAWBEW.js} +1 -1
  64. package/bundle/web-dist/client/assets/{use-oauth-login-flow-Cyn3YPqW.js → use-oauth-login-flow-DxdOhz4a.js} +1 -1
  65. package/bundle/web-dist/client/assets/{use-update-token-mutation-DQCwCYfR.js → use-update-token-mutation-DAq0F8p-.js} +1 -1
  66. package/bundle/web-dist/client/index.html +3 -3
  67. package/bundle/web-dist/server/assets/{cost-59jBdx-I.js → cost-eEHeTRT5.js} +4 -5
  68. package/bundle/web-dist/server/assets/{lazyRouteComponent-CUR-kWzI.js → lazyRouteComponent-Dtd0NR4r.js} +2 -2
  69. package/bundle/web-dist/server/assets/{logs-CaaWO9-Z.js → logs-l91avYif.js} +33 -21
  70. package/bundle/web-dist/server/assets/{monit-CBil92fJ.js → monit-DvzribSE.js} +4 -4
  71. package/bundle/web-dist/server/assets/{plus-Bvdj8Pwx.js → plus-KZrECewT.js} +1 -1
  72. package/bundle/web-dist/server/assets/{react-dom-CyuShs9K.js → react-dom-B4a6-uuR.js} +1 -1
  73. package/bundle/web-dist/server/assets/{refresh-cw-3QAiSWBd.js → refresh-cw-BND93JrF.js} +1 -1
  74. package/bundle/web-dist/server/assets/{request-model-CnCeaNLE.js → request-model-CbqfnQOJ.js} +19 -1
  75. package/bundle/web-dist/server/assets/{routes-DtVf9yqo.js → routes-BAPWTizT.js} +6 -7
  76. package/bundle/web-dist/server/assets/{services.generated-FPwsG9p3.js → services.generated-CEbGjsI0.js} +3 -2
  77. package/bundle/web-dist/server/assets/{settings-DeHNwoMv.js → settings-BZR5HSAL.js} +3 -3
  78. package/bundle/web-dist/server/assets/{show-8NNdRxhM.js → show-CxVZTAdv.js} +17 -21
  79. package/bundle/web-dist/server/assets/{tokens-odgS2-9q.js → tokens-D1hb4eKk.js} +4 -4
  80. package/bundle/web-dist/server/assets/{use-oauth-login-flow-DTQKdYBO.js → use-oauth-login-flow-Cvdy0zF8.js} +1 -1
  81. package/bundle/web-dist/server/assets/{use-update-token-mutation-xvXbPrCI.js → use-update-token-mutation-CfYmk_4_.js} +1 -1
  82. package/bundle/web-dist/server/entry-server.generated.js +13 -10
  83. package/package.json +1 -1
  84. package/skills/qgrid/SKILL.md +9 -8
  85. package/skills/qgrid/references/ai-sdk-provider-contract.md +27 -17
  86. package/skills/qgrid/references/decision-rationale.md +8 -8
  87. package/skills/qgrid/references/openai-codex-runtime.md +19 -11
  88. package/skills/qgrid/references/prompt-cache-and-usage.md +3 -0
  89. package/skills/qgrid/references/request-log-run-lifecycle.md +14 -2
  90. package/skills/qgrid/references/sonamu-api-web-flow.md +2 -2
  91. package/skills/qgrid/references/verification-and-debugging.md +23 -9
  92. package/bundle/web-dist/client/assets/cost-CY2BJEPC.js +0 -1
  93. package/bundle/web-dist/client/assets/index-CvG4UB9v.css +0 -1
  94. package/bundle/web-dist/client/assets/logs-3n_U4qnI.js +0 -1
  95. package/bundle/web-dist/client/assets/request-model-D4CZPmnG.js +0 -1
  96. package/bundle/web-dist/client/assets/show-b2PFNI3D.js +0 -2
@@ -6,13 +6,26 @@ Use this reference before changing `packages/ai-sdk`.
6
6
 
7
7
  `packages/ai-sdk` is the active public SDK package. The old v1 SDK (`@cartanova/qgrid-sdk`) has been removed from the repository.
8
8
 
9
- qgrid's custom provider implements the AI SDK `LanguageModelV3` contract. General usage should follow the AI SDK docs and normal AI SDK APIs such as `generateText`, `streamText`, tools, structured output, and telemetry. Do not re-document generic AI SDK usage in qgrid-specific docs unless the behavior differs. qgrid docs and this skill should focus on qgrid-specific config, `providerOptions.qgrid`, request logging, runtime routing, cache/session behavior, and provider limitations.
9
+ qgrid's custom provider implements `LanguageModelV3` through `qgrid()` and `ImageModelV3` through `qgrid.image()`. General usage should follow the AI SDK docs and normal AI SDK APIs such as `generateText`, `streamText`, `generateImage`, tools, structured output, and telemetry. Do not re-document generic AI SDK usage in qgrid-specific docs unless the behavior differs. qgrid docs and this skill should focus on qgrid-specific config, `providerOptions.qgrid`, request logging, runtime routing, cache/session behavior, and provider limitations.
10
+
11
+ ## Explicit image model
12
+
13
+ Use `generateImage({ model: qgrid.image("openai/gpt-image-2"), prompt })` for explicit image generation. The image factory returns `ImageModelV3`, accepts `serverUrl` and `projectName`, and sends the existing query endpoint `model: "openai/gpt-image-2", imageGeneration: true`. These image-generation fields are internal wire inputs, not public language-model options. The server calls subscription Codex Images directly, without a chat driver. `qgrid()` / `generateText` handles text, tools, and structured output. Dashboard auto-image mode registers an internal `generate_image` function, which calls the same subscription Images endpoints for ordinary or transparent output after the chat model chooses a prompt and references. Do not add a public SDK image-tool factory.
14
+
15
+ Reference editing uses AI SDK `prompt: { text, images }`, with up to five references. Omitted references use `images/generations`; supplied references use `images/edits`. The new direct interface returns PNG only; unsupported output formats and masks fail before generation. `n` is handled through AI SDK batching with `maxImagesPerCall: 1`; callers cannot override this to send a multi-image backend request. `seed` and `aspectRatio` produce unsupported warnings. Size and quality are hints, never exact-output guarantees; prompt for the intended ratio and preserve observed settings.
16
+
17
+ `gpt-image-2` is the Codex wire request identifier, not proof that the serving weights are Images 2.0. OpenAI announced Images 2.5 in Codex; private-route probes accepted even nonexistent model IDs, so Flare/Sunburst selection is not established. Standalone metadata preserves a reported model, otherwise it falls back to the requested identifier. Do not describe that fallback as an observed serving version. GPT Image 2 and 2.5 have the same published token rates; per-image fallback prices remain GPT Image 2 estimates when usage is absent.
18
+
19
+ Use `QgridImageProviderOptions` with `satisfies` on `providerOptions.qgrid`. Options are flat: `quality`, `background`, `outputFormat: "png"`, `tokenName`, and `logger`. Standard `size` belongs at the `generateImage` top level. Do not send chat effort, thinking, tools, or structured output to the image model.
20
+
21
+ Map image response usage into standard `generateImage().usage`. Preserve per-image generation metadata and request-log IDs under `providerMetadata.qgrid.images[]`: AI SDK 6 merges that array and discards arbitrary provider-level keys. The raw query's top-level usage/cost remain zero chat-driver accounting; server logs price the separate image usage using the existing API-price estimator. Do not duplicate image price calculation in the SDK or count aggregate usage once per output. Without reported image usage, keep standard usage unknown and preserve the server's existing per-image cost fallback. `logger: false`, token targeting, project labeling, and cancellation still apply.
10
22
 
11
23
  For tool calling, qgrid intentionally follows the AI SDK interface while implementing provider calls through qgrid structured-output emulation. Read `tool-calling-and-multiturn.md` before changing tool behavior, `stopWhen`/multi-step behavior, or tool-call request logs.
12
24
 
13
25
  Main files:
14
26
 
15
27
  - `packages/ai-sdk/src/index.ts`: `qgrid()` provider implementation.
28
+ - `packages/ai-sdk/src/image-model.ts`: `qgrid.image()` provider implementation.
16
29
  - `packages/ai-sdk/src/index.types.ts`: public config/options/types.
17
30
  - `packages/ai-sdk/src/utils.ts`: prompt/history/tool conversion.
18
31
  - `packages/ai-sdk/src/logger.ts`: telemetry logger integration.
@@ -55,7 +68,7 @@ const result = await generateText({
55
68
 
56
69
  Do not write `providerOptions: { ... } satisfies QgridProviderOptions`: the exported type describes the nested qgrid options object, not AI SDK's outer provider-name map. Use this typed pattern in qgrid setup guidance and examples so misspelled keys and invalid option values fail at compile time.
57
70
 
58
- `providerOptions.qgrid` supports:
71
+ Language-model `providerOptions.qgrid` supports:
59
72
 
60
73
  - `tokenName`: strict active-token targeting for both providers. Include the provider prefix and
61
74
  match it to the model (`anthropic/yds` with `anthropic/*`, for example). Missing, inactive, or
@@ -70,8 +83,8 @@ Do not write `providerOptions: { ... } satisfies QgridProviderOptions`: the expo
70
83
  - `serviceTier`: OpenAI/Codex route only.
71
84
  - `timeoutMs`: Anthropic server-side Claude Code process timeout in milliseconds. It must be a positive integer no greater than 30 minutes and defaults to 240 seconds. For non-stream `generateText`, the SDK derives request-scoped Undici `headersTimeout` and `bodyTimeout` values as `timeoutMs + 60_000` without changing the process-global dispatcher.
72
85
  - `fallbackModels`: deprecated, ignored compatibility field. The provider emits a standard unsupported-option warning when supplied; remove it from callers. It is not Claude Code's upstream Fable safety-refusal fallback.
73
- - `imageGeneration`: OpenAI/Codex non-stream only. Enables Codex's built-in `image_generation` tool for that request.
74
- - `imageGenerationOptions`: optional `quality: "low" | "medium" | "high"`, `size` (`auto` or positive-integer `WIDTHxHEIGHT`), and `background: "auto" | "opaque" | "transparent"`. Transparent requests use Codex standalone Images with `gpt-image-2`; other requests use the hosted Responses tool. Transparent mode rejects tools/structured output, supports up to five references, validates real transparent and visible PNG pixels, and never invokes background-removal fallback. Size is a positive-integer WIDTHxHEIGHT hint, not an exact-output guarantee; omit it or use auto and put the desired aspect ratio in the prompt. Do not impose public Images API 16-pixel/4K rules on the private subscription route. Both routes expose observations (`codex-responses` or `codex-images`); hosted model is optional. PNG dimensions come from bytes; JPEG/WebP size comes from backend metadata. Hosted image cost is null, and arbitrary/auto size hints never receive a made-up price-table estimate. Returned size/quality may differ from requested settings; read `result.providerMetadata.qgrid.imageGeneration` or per-file `result.content` metadata. `result.files[]` contains bytes/mediaType only. Logs separate `requestedOptions` from `observedGeneration`; image usage is separate from the zero text-driver usage on this route.
86
+
87
+ `imageGeneration` and `imageGenerationOptions` are not public SDK options. Language-model generation and streaming reject either key before transport, directing callers to `generateImage` with `qgrid.image()`. Use flat `QgridImageProviderOptions`; do not add deprecated language-model shims for the removed fields.
75
88
 
76
89
  `providerOptions.qgrid` does not currently support `projectName` or `project_name`. Prefer `QGRID_PROJECT_NAME` for the default project label; use config `projectName` only when a caller needs to override that default.
77
90
 
@@ -131,15 +144,16 @@ const result = await generateText({
131
144
  Fast mode contract:
132
145
 
133
146
  - It is OpenAI/Codex-only. Do not send it to `anthropic/*` models.
134
- - It works on both `generateText` and `streamText`; image generation remains non-stream for unrelated reasons.
147
+ - It works on both `generateText` and `streamText`; it is not an image-model option.
135
148
  - Omit `serviceTier` for normal/default routing. qgrid's current API accepts `"fast"` and `"flex"`; callers should not send Codex's normalized `"priority"` value directly.
136
- - qgrid is a pass-through for this selection. Codex applies the tier only when its Fast mode feature is enabled and the selected model advertises support; otherwise Codex can omit the unsupported tier.
149
+ - qgrid forwards this selection to the subscription backend; sending it does not by itself prove the upstream serving tier. Do not assume that public API Flex discounts or availability apply to the subscription route. Omitting `serviceTier` does not select `flex`.
137
150
  - Fast mode changes upstream service-tier routing only. It does not change token weights or client concurrency.
138
151
  - Do not promise a fixed latency improvement. Verify TTFT, duration, throughput, and quota consumption with a workload-specific A/B test.
152
+ - Since 2.10.7, native request logs retain the requested tier and show fast requests with a filled blue lightning icon. New OpenAI query/stream costs use the verified Fast API rates for supported current models. See `request-log-run-lifecycle.md` and `prompt-cache-and-usage.md` for storage and pricing semantics.
139
153
 
140
154
  ## Request construction
141
155
 
142
- The provider sends `POST /api/qgrid/query` for generate and `prepareStream` plus SSE for stream.
156
+ The language provider sends `POST /api/qgrid/query` for generate and `prepareStream` plus SSE for stream.
143
157
 
144
158
  Payload responsibilities:
145
159
 
@@ -153,9 +167,8 @@ Payload responsibilities:
153
167
  - Preserve and resend `runContext` for tool-call follow-ups.
154
168
  - Let the server infer single-turn completion versus an open tool-call run. Logging-disabled tool calls still use the SDK's local pending-call correlation and continue without a request-log id.
155
169
  - Derive/store/resend opaque OpenAI affinity coordinates by `sessionKey`.
156
- - Send `imageGeneration` and `imageGenerationOptions` when configured.
157
- - Send AI SDK multimodal image/file message parts as qgrid `input` only when `imageGeneration` is enabled. Normal non-image-generation calls keep `user_prompt` text-only and do not forward image input.
158
- - Reject oversized reference-image data URLs before the request leaves the SDK. Reference images travel as JSON data URLs; callers should compress/resize large photos, preferably to WebP/JPEG.
170
+ - Keep language-model prompts text-only. Ignore image parts with an unsupported warning; this adapter does not support general vision.
171
+ - The separate image provider converts `generateImage` prompt references to qgrid `input` and rejects oversized inline reference images before transport. Callers should compress/resize large reference photos, preferably to WebP/JPEG.
159
172
 
160
173
  Tools and `jsonSchema` can be used together as of qgrid 2.5.4. On OpenAI the
161
174
  server composes them into one strict action envelope enforced by constrained
@@ -194,9 +207,7 @@ rejected.
194
207
 
195
208
  When tools are present, qgrid sends tool definitions to the server as `tools`; it does not send them to OpenAI or Anthropic as native provider tools. The server converts them into a strict structured-output schema and maps the model's structured result back into AI SDK `tool-call` content.
196
209
 
197
- Image generation is different from AI SDK client tools: it is an opt-in Codex-hosted tool exposed by qgrid through `providerOptions.qgrid.imageGeneration`. Use `generateText`; `streamText` rejects image generation before opening the stream.
198
-
199
- Reference images for image generation use normal AI SDK multimodal message parts. The provider wraps image/file base64 as `data:${mediaType};base64,...` qgrid `input` items. This is intentionally scoped to image-generation requests, not general vision/chat behavior.
210
+ SDK image generation uses `ImageModelV3`, independently of AI SDK language-model tools. Pass references through `generateImage`'s `prompt: { text, images }`; the image provider wraps inline image bytes as data URLs and accepts HTTPS reference URLs. This supports image generation/editing, not general vision chat. Raw/server image flags remain available for dashboard chat and internal routing; they are not part of `QgridProviderOptions`.
200
211
 
201
212
  ## Response mapping
202
213
 
@@ -206,13 +217,12 @@ Since 2.10.0, `generateText` exposes the parent request-log ID at `result.provid
206
217
 
207
218
  Update both server and SDK to use this metadata. The field is omitted when logging is disabled or an older server does not return it. Tool-run steps share the parent ID; `runContext.requestLogId` remains separate continuation state. This does not expose external-provider telemetry logger IDs or add IDs to stream delta/error events.
208
219
 
209
- qgrid response content maps to AI SDK content:
220
+ Language-model response content maps to AI SDK content:
210
221
 
211
222
  - qgrid `text` -> AI SDK text content.
212
223
  - qgrid `tool-call` -> AI SDK tool-call content.
213
- - qgrid `image` -> AI SDK file content with the returned `mediaType` (`image/png`, `image/jpeg`, or `image/webp`). Older responses without `mediaType` retain the PNG default.
214
224
 
215
- `imageGenerationOptions.outputFormat` accepts `png | jpeg | webp` for the hosted Responses route and maps to `image_generation.output_format`. Omission retains PNG. The standalone transparent route only supports PNG; a transparent request with JPEG/WebP is rejected before generation. Preserve the returned MIME type through response content, SDK file parts, and request-log image data URLs.
225
+ The image provider maps direct Images output to standard AI SDK `image`/`images` and image `usage`, preserving generation metadata under `providerMetadata.qgrid.images[]`. It supports PNG output only. Raw/server hosted Responses output can be PNG/JPEG/WebP; preserve its actual MIME type in raw content and request-log data URLs.
216
226
 
217
227
  `finishReason` maps `tool-calls` when qgrid returns tool calls.
218
228
 
@@ -220,7 +230,7 @@ For tools plus structured output, qgrid serializes the final schema-constrained
220
230
  `answer` as JSON text. AI SDK then parses and validates that text into
221
231
  `Output.object`'s `output`. This path requires both server and SDK 2.5.4 or later.
222
232
 
223
- When `imageGeneration` was requested and the server returns no image part, the AI SDK provider throws a version-skew/error guard instead of silently accepting text-only output.
233
+ The image provider requires exactly one output from route `codex-images` per call and rejects text-only, hosted-route, or multiple-image responses with a server/SDK version-skew guard.
224
234
 
225
235
  For a successful Fable refusal fallback (Fable 5 or 5.1):
226
236
 
@@ -231,20 +231,20 @@ Sources:
231
231
 
232
232
  Key decisions:
233
233
 
234
- - Image generation is OpenAI/Codex-only and request-level opt-in outside dashboard chat. In 2.9.8, chat explicitly opts into raw `imageGeneration: "auto"` each turn. Most requests use the Codex-hosted `image_generation` tool. Transparent backgrounds use Codex standalone Images because the hosted route rejected them in live probes. Do not globally enable image tools for other callers: that would change their tool framing and cache behavior.
234
+ - Image generation is OpenAI/Codex-only and request-level opt-in outside dashboard chat. Chat explicitly opts into raw `imageGeneration: "auto"` each turn. Auto mode registers one internal `generate_image` function and calls standalone Images for ordinary and transparent backgrounds. Raw explicit chat-model `imageGeneration: true` retains its hosted route for non-transparent images. Public SDK image requests use `qgrid.image()` / `generateImage` and standalone Images for any background, following AI SDK's separate `ImageModelV3` contract. The language-model SDK has no image-generation options; the former boolean and nested options are removed without deprecated shims. Do not add a public image-tool factory or globally enable image tools for other callers: that would change their tool framing and cache behavior.
235
235
  - It is non-stream only. Codex returns completed base64 image payloads, not useful image deltas.
236
236
  - Image turns send full input directly and retain no provider conversation state. This prevents base64 payloads from entering reusable state.
237
- - qgrid performs preflight capability/model gates. Explicit `imageGeneration: true` requires an image; `"auto"` allows normal text replies but still rejects attempted image generation without a completed image. The public AI SDK retains its boolean option and image-required contract.
238
- - Returned images are inline base64 content parts for consumers and AI SDK `file` parts. Reference images are accepted through AI SDK multimodal message parts only on the `imageGeneration` path, and are transported as JSON data URLs with SDK-side size guarding.
239
- - `imageGenerationOptions` supports quality, size, and background. Size accepts `auto` or positive-integer `WIDTHxHEIGHT` hints; the subscription backend can ignore them. Put desired ratios in the prompt, report observed output settings, and never silently resize. Hosted image cost is unknown because image usage/model are not reliably exposed. Only legacy responses without observations retain known three-size pricing assumptions. Transparent-route image usage is priced separately, and no background-removal fallback is allowed.
237
+ - qgrid performs preflight capability/model gates. Raw `imageGeneration: true` requires an image; `"auto"` allows normal text replies but still rejects attempted image generation without a completed image. Public `generateImage` requires one direct Images output per provider call; AI SDK batches requests for `n` outputs.
238
+ - Raw returned images are inline base64 content parts. The image SDK maps them to AI SDK `image`/`images` and takes references through standard `prompt: { text, images }`, with JSON data URL size guarding and HTTPS reference URL support. The language SDK ignores image parts with a warning and does not implement general vision.
239
+ - Raw/server `imageGenerationOptions` supports quality, size, and background. The public image SDK uses flat `QgridImageProviderOptions` and standard top-level `size`. The subscription backend can ignore size and quality hints. Put desired ratios in the prompt, report observed output settings, and never silently resize. Reported image usage is priced at public token rates; without it, use the requested quality/size table with a default-size price for auto/custom hints. Preserve observed settings separately from pricing assumptions. No background-removal fallback is allowed.
240
240
  - qgrid does not manage generated or reference images as durable assets through a `generated_images` table or object-storage layer. Current request logs can still contain inline image data URLs for inspection and synthetic `image_generation` tool-call steps; reference input images live in the first synthetic step's `tool_args.inputImages`.
241
- - Image cost is stored separately in `request_logs.image_cost_usd`; `request_logs.cost_usd` remains the Codex driver model token cost. Hosted image cost is unknown (`null`). Standalone image costs are public-price estimates from reported image usage. Only legacy responses without observations and with known size prices retain size-table estimates. List rows and filtered totals mark known subtotals when image costs are unknown; `totalCost.unknownImageCostCount` counts those requests under the same filters.
241
+ - Image cost is stored separately in `request_logs.image_cost_usd`; `request_logs.cost_usd` remains the Codex driver model token cost. Image costs use reported image usage at public token rates, or per-image quality/size estimates when usage is absent. Auto/custom sizes fall back to the 1536x1024 price; omitted quality uses medium. Rows and totals display combined costs as dollar amounts without Cost labels, without Known/Estimated labels or pricing disclaimers. Existing stored null costs are not backfilled; `totalCost.unknownImageCostCount` remains for API compatibility.
242
242
  - Inspect current plans, tests, and implementation before changing behavior; the Codex tool surface can change underneath qgrid.
243
243
 
244
- The 2.9.8 chat design removes the discarded client `request_image_generation` decision step, which asked GPT to choose a tool and then called GPT again for ordinary generation. Native `image_generation` handles ordinary generation directly. A separate qgrid-defined `generate_transparent_image` function is necessary to reach the standalone transparent endpoint, but the server executes it with the same token and cancellation signal without another GPT request. Routing belongs to the server; the chat only sends context and displays results.
244
+ The current chat design registers `generate_image` for both ordinary and transparent images instead of splitting native hosted generation from a transparent-only function. The driver decides whether an image is needed, writes a self-contained prompt, and chooses `transparent_background` and `num_last_images_to_include` (0–5). Zero references starts a fresh image; a positive count selects only the last N conversation images, preserving their chronological order. The server executes the standalone request with the same token and cancellation signal without a second text-driver request. Routing belongs to the server; the chat only sends context and displays results. This preserves conversational interpretation while sharing direct Images execution with explicit SDK generation.
245
245
 
246
246
  Image edits need complete prior image bytes, not just text history or an assumption that the backend remembers earlier outputs. Chat replays uploaded and generated images, representing generated images as user visual references because assistant `input_image` blocks are invalid. Image previews report actual extension, decoded byte size, and natural dimensions. Uploaded images describe the existing resized JPEG sent to the model; generated images retain their returned bytes.
247
247
 
248
- Model labels show requested model to image model using serving metadata or the image model encoded in the logged cost method. The cost-method fallback remains an accounting assumption, not new evidence of the upstream model version. The UI has no image-generation or assumption badge and never fixes the name to `gpt-image-2.5`. Hosted and auto transparent logs retain driver identity and token costs separately from image metadata/cost.
248
+ Model labels show requested model to image model using serving metadata or the image model encoded in the logged cost method. The cost-method fallback remains an accounting assumption, not new evidence of the upstream model version. The UI has no image-generation or assumption badge and never fixes the name to `gpt-image-2.5`. The `gpt-image-2` wire ID also does not prove Images 2.0 serving weights or selectable 2.5 variants. Hosted and auto image logs retain driver identity and token costs separately from image metadata/cost; direct `qgrid.image()` requests have no driver cost.
249
249
 
250
- Prompt wording alone is not a reliable format control: a 2026-09-29 probe returned PNG for a WebP prompt and WebP with explicit `outputFormat: "webp"`. Model prose contradicted the output and must not be used as evidence. Transparent generation remains PNG-only, and this release does not infer format from chat prose or introduce post-generation conversion.
250
+ Prompt wording alone is not a reliable format control: a 2026-09-29 hosted-route probe returned PNG for a WebP prompt and WebP with explicit `outputFormat: "webp"`. Model prose contradicted the output and must not be used as evidence. Auto chat and standalone generation are PNG-only and reject explicit non-PNG options. Do not infer format from chat prose or introduce post-generation conversion or a hosted fallback.
@@ -48,8 +48,10 @@ availability or identical limits on qgrid's ChatGPT-subscription route.
48
48
  ### GPT-6 Astra, Sol, Luna, and GPT-6.1 Sol
49
49
 
50
50
  `openai/gpt-6-astra` uses the existing text generation, streaming and structured-output paths.
51
- The model supports image input, but qgrid keeps its existing SDK restriction: image parts are
52
- forwarded only for opt-in `imageGeneration` reference-image requests, not general vision chat.
51
+ The model supports image input, but qgrid's language-model SDK adapter does not support general
52
+ vision: it ignores image parts with a warning. Image generation/editing references belong to
53
+ `generateImage({ model: qgrid.image(...), prompt: { text, images } })`; dashboard chat uses its
54
+ own raw/server image inputs.
53
55
  Standard API-equivalent prices per million tokens are input $10, cached input $1, cache write
54
56
  $12.50, and output $50. Above 272,000 input tokens, the full request uses 2x input/cache rates and
55
57
  1.5x output rates. These are qgrid's cost estimates, not subscription billing amounts or Fast-tier prices.
@@ -179,23 +181,29 @@ Quota is fetched directly from `https://chatgpt.com/backend-api/wham/usage` with
179
181
 
180
182
  OpenAI usage reports total input, cached input, output, reasoning, and total tokens for the request. Preserve cached input as a subset of input; do not add it to input again when computing cache-hit rate.
181
183
 
182
- Image generation is opt-in and non-stream at the AI SDK interface. The backend still delivers its response through SSE. `imageGenerationOptions.outputFormat` maps to the hosted tool's `output_format` (`png`, `jpeg`, or `webp`); omission retains PNG. Returned bytes determine the MIME type when recognizable, with the reported format as fallback. Preserve that type through qgrid content, AI SDK file parts, and request-log image data URLs. The standalone transparent route remains PNG-only and rejects other output formats. Hosted Responses preserves observed image settings with route `codex-responses`, using PNG byte dimensions or the backend-reported JPEG/WebP size. Unreported model/settings stay absent. Hosted image cost is null because exact image usage/model are unavailable; requested settings cannot establish a price. Legacy responses without observations retain known three-size estimates only.
184
+ Raw/server chat-model image requests are opt-in and non-stream to the caller, while the Responses backend still delivers its response through SSE. Explicit chat-model `imageGeneration: true` retains the hosted image tool for non-transparent requests. On that route, raw `imageGenerationOptions.outputFormat` maps to the hosted tool's `output_format` (`png`, `jpeg`, or `webp`); omission retains PNG. These are not public language-model SDK options: SDK image generation uses `qgrid.image()` / `generateImage` and calls standalone Images directly. Auto chat also calls standalone Images through its internal function and accepts PNG only. Returned bytes determine the MIME type when recognizable, with the reported format as fallback. Preserve that type through qgrid content and request-log image data URLs. Standalone Images rejects other output formats. Hosted Responses preserves observed image settings with route `codex-responses`, using PNG byte dimensions or the backend-reported JPEG/WebP size. Unreported model/settings stay absent. Image costs use reported image usage when available, otherwise the requested quality/size price table. Auto or unsupported sizes use the 1536x1024 price, and omitted quality uses medium. These are API-price estimates, not observed output settings or subscription charges.
183
185
 
184
- ### Transparent image route
186
+ Hosted image accounting reads `response.completed.response.tool_usage.image_gen`, separately from the driver's top-level `response.usage`. Preserve the response-level image usage on the first hosted output only, and obtain a missing image model from the response's `tools` entry. Do not overwrite per-image observed size/quality with that entry's `auto` configuration. On 2026-10-14, a raw subscription response reported image input/output usage and `model: gpt-image-2-codex`, while echoing `quality: auto` and `size: auto` despite explicit outgoing `high`/`1024x1536`. The raw image output was medium/1037x1517. This is an upstream override; qgrid must preserve observations rather than claim the requested controls were honored. Image costs remain GPT Image 2 public-price equivalents, not subscription billing.
185
187
 
186
- Explicit `imageGeneration: true` with `imageGenerationOptions.background: "transparent"` switches from hosted Responses to Codex `images/generations` or `images/edits`. The same selected subscription token, cancellation deadline, in-flight tracking, and one definitive 401 refresh apply. Use JSON content negotiation; do not reuse the Responses SSE Accept header. Never replay an ambiguous generation or fall back to opaque output/IS-Net. This explicit route runs `gpt-image-2` directly, so actual model metadata differs from the requested driver ID and ordinary driver usage stays zero. Image response usage and actual settings are preserved on `generation`; usage belongs to the first output only. Costs remain explicitly estimated from public prices.
188
+ ### Standalone image routes
187
189
 
188
- The image request adapter preserves text/system context and reference images (maximum five), rejects unsupported structured/tool requests before generation, and validates PNG contents with pngjs. Both a fully transparent pixel and visible content are required. Invalid, opaque, empty, or oversized results fail. Returned dimensions come from the decoded PNG; qgrid never silently resizes. A 2026-09-08 live request returned 1254x1254/medium for requested 1024x1024/high. Do not promise exact size/quality enforcement. See `openai-image-generation.ts` and opt-in `transparent`/`transparent-edit` cases in the image smoke script.
190
+ The explicit image-model route `openai/gpt-image-2` with raw `imageGeneration: true` uses standalone Images for opaque, auto, or transparent backgrounds. It is exposed by `qgrid.image()` / AI SDK `generateImage`; the raw flag is an internal transport detail. Public `generateText` has no image-generation options. Reference inputs select `images/edits`, otherwise `images/generations`. Direct image output is PNG-only; decode and validate returned dimensions, applying the transparent-and-visible pixel requirement only when transparency was explicitly requested. Preserve returned background, quality, model, and image usage without claiming requested hints were honored. Image-model requests cannot use chat tools, structured output, auto-image mode, or streaming. The `gpt-image-2` wire identifier does not establish the serving Images version or selectable 2.5 variant.
189
191
 
190
- ### Automatic dashboard chat images (2.9.8)
192
+ Raw/server chat-model requests with explicit `imageGeneration: true` and `imageGenerationOptions.background: "transparent"` switch from hosted Responses to Codex `images/generations` or `images/edits`. The same selected subscription token, cancellation deadline, in-flight tracking, and one definitive 401 refresh apply. Use JSON content negotiation; do not reuse the Responses SSE Accept header. Never replay an ambiguous generation or fall back to opaque output/IS-Net. This explicit route runs `gpt-image-2` directly, so actual model metadata differs from the requested driver ID and ordinary driver usage stays zero. Image response usage and actual settings are preserved on `generation`; usage belongs to the first output only. Costs remain explicitly estimated from public prices.
191
193
 
192
- Raw `QueryInput.imageGeneration` accepts boolean or `"auto"`; the public AI SDK option remains boolean. OpenAI dashboard chat sends `"auto"` through one non-stream POST per turn. The dispatcher offers native `image_generation` plus a qgrid-defined Responses function, `generate_transparent_image`, reserved for transparent backgrounds. It executes that function server-side against standalone Codex Images using the same selected token and abort signal, without another GPT request. The client must not implement a `request_image_generation` decision loop.
194
+ The direct image request adapter preserves text/system context and reference images (maximum five), rejects unsupported structured/tool requests before generation, and validates PNG contents with pngjs. Explicit transparency requires both a fully transparent pixel and visible content; opaque or entirely empty results fail that requirement. Invalid or oversized results fail for every background. Returned dimensions come from the decoded PNG; qgrid never silently resizes. A 2026-09-08 live request returned 1254x1254/medium for requested 1024x1024/high. Do not promise exact size/quality enforcement. See `openai-image-generation.ts` and opt-in `transparent`/`transparent-edit` cases in the image smoke script.
193
195
 
194
- Auto mode accepts text-only completion when no image tool was attempted. Explicit `true` still requires an image, and an attempted image tool that returns no complete image still fails. Caller tools and `outputSchema` are incompatible with auto mode. The transparent function and native image tool must not be mixed in one response; at most one transparent call is allowed.
196
+ ### Automatic dashboard chat images
195
197
 
196
- Auto transparent turns preserve the Responses driver model and token usage; image model, settings, and usage remain separate generation metadata. Do not apply the explicit standalone route's zero-driver accounting to auto turns. Full image history is replayed on each turn without reusable provider conversation state. Global image-tool opt-in remains unchanged outside chat to preserve regular callers' tool framing and cache behavior.
198
+ Raw `QueryInput.imageGeneration` accepts boolean or `"auto"`; these flags are absent from the public AI SDK language-model options. OpenAI dashboard chat sends `"auto"` through one non-stream POST per turn. The dispatcher registers only the qgrid-defined Responses function `generate_image`, with server-validated arguments `prompt: string`, `transparent_background: boolean`, and `num_last_images_to_include: integer` from 0 to 5. Its wire setting is `strict: false`, matching Codex: the 2026-10-14 live Astra probe failed with `strict: true` even after removing bounds, and succeeded with `strict: false` with bounds retained. Keep server-side required-field/type/range/extra-field validation. It does not register the native hosted `image_generation` tool in auto mode. The server executes the function against standalone Codex Images with `model: gpt-image-2`, using the same selected token and abort signal, without another text-driver request. Both ordinary and transparent images use this route. The client must not implement a `request_image_generation` decision loop; no public SDK tool factory is exposed.
197
199
 
198
- The 2026-09-29 hosted-route probe returned PNG for a prompt-only WebP request and WebP for explicit `imageGenerationOptions.outputFormat: "webp"`. Verify bytes rather than model claims. The standalone transparent endpoint still returns PNG; no format conversion is added. See `output/webp-prompt-probe-2026-09-29/verification.md` and `docs/solutions/architecture-patterns/qgrid-native-auto-image-generation-2.9.8.md`.
200
+ The driver receives complete text and image history and writes a self-contained generation/edit prompt. `num_last_images_to_include: 0` starts a fresh generation, even when historical images exist. A positive count selects the last N conversation images in chronological order for editing, up to five. Do not pass all history to the Images endpoint or assume it retains previous images. Use the selected references and the function's prompt for the direct request.
201
+
202
+ Auto mode accepts text-only completion when no image tool was attempted. Explicit `true` still requires an image, and an attempted image tool that returns no complete image still fails. Caller tools and `outputSchema` are incompatible with auto mode. At most one `generate_image` call is allowed per turn. Auto image output is PNG-only; reject explicit JPEG/WebP options before generation rather than converting output or falling back to hosted Responses.
203
+
204
+ Auto image turns preserve the Responses driver model and token usage; image model, settings, and usage remain separate generation metadata. Their combined cost is a chat-driver estimate plus an image-usage estimate. If the image stage fails after the driver completed, carry that completed driver result internally so error/aborted logs still record its usage and price once. Do not attach partial image output or claim a successful image cost. Do not apply the explicit standalone route's zero-driver accounting to auto turns. Full image history is replayed on each turn without reusable provider conversation state. Global image-tool opt-in remains unchanged outside chat to preserve regular callers' tool framing and cache behavior.
205
+
206
+ The 2026-09-29 hosted-route probe returned PNG for a prompt-only WebP request and WebP for the raw/server option `imageGenerationOptions.outputFormat: "webp"`. Verify bytes rather than model claims. Standalone Images still returns PNG; no format conversion is added. See `output/webp-prompt-probe-2026-09-29/verification.md` and `docs/solutions/architecture-patterns/qgrid-native-auto-image-generation-2.9.8.md`.
199
207
 
200
208
  ### Size hints and observed dimensions (2026-10-13)
201
209
 
@@ -64,6 +64,9 @@ Cache-hit rate uses `input_tokens` as its denominator. Do not add cache subfield
64
64
  `packages/api/src/utils/providers/common/model-cost.ts` contains model-price fallback.
65
65
 
66
66
  - OpenAI cost normally uses qgrid's price table.
67
+ - Since 2.10.7, native OpenAI `serviceTier: "fast"` calls apply the verified Fast API multiplier for the actual serving model: 2x for GPT-6 Astra, 6.1 Sol, 6 Sol, 6 Luna, and GPT-5.6 Sol/Terra/Luna (verified 2026-10-16 at https://developers.openai.com/api/docs/pricing). Apply it to input, cached input, cache writes, and output after the existing long-context calculation. Keep the explicit model allowlist; do not extrapolate to unverified or retired models.
68
+ - This is an API-equivalent estimate based on the requested tier, not confirmed upstream billing or subscription quota consumption. Do not use the subscription quota multiplier for dollar costs. Omitted/flex tiers, models without verified Fast rates, and external telemetry keep their existing pricing behavior; positive provider-reported costs remain authoritative.
69
+ - Query, stream, tool turns, and completed chat drivers preserved after image failures share the tier-aware calculation. Image costs stay separate and receive no Fast multiplier. Persist step costs and sum them once; do not reprice existing logs or infer their missing tiers.
67
70
  - OpenAI image generation stores a separate `image_cost_usd` estimate because the direct private route does not expose exact image-tool billing.
68
71
  - Anthropic prefers a positive Claude Code `total_cost_usd`; otherwise qgrid uses its price table.
69
72
  - Preserve Anthropic's five-minute and one-hour cache-creation token split and price each TTL separately.
@@ -61,6 +61,16 @@ This is a forward-only storage normalization. Do not backfill legacy prefixless
61
61
 
62
62
  Provider-qualified ids widen `model_name` and `requested_model_name` from 50 to 255 characters on both request-log entities. Generate and apply the normal Sonamu schema migration before running the new server. Do not hand-edit generated types, hand-author a derivable migration, or rewrite existing model values as part of that width migration.
63
63
 
64
+ ## Requested service tier (2.10.7+)
65
+
66
+ Native OpenAI query/stream logs store the requested `serviceTier` in nullable `request_logs.service_tier`: `fast`, `flex`, or `NULL` when omitted. Other providers and external telemetry logger calls leave it `NULL`. Creation records the initial turn's tier; successful completion replaces it with the final turn's tier alongside the final model. Pending tool runs and error/aborted runs retain the initial tier; this is not a per-step tier history.
67
+
68
+ The list and detail subsets expose this field. Show a filled lightning icon in `#1877F2` after the model display only when the stored value is `fast`, with the accessible label and tooltip `Fast 모드`. Do not mark `flex` or `NULL` as fast.
69
+
70
+ This records caller intent, not the service tier confirmed by the upstream response. The OpenAI transport sends `fast` as `service_tier: "priority"`. Existing rows remain `NULL`; this means unrecorded, not proof of default mode. Do not infer historical tiers from model names, latency, or cost. Apply the generated service-tier migration before using the updated log queries.
71
+
72
+ Each new generate step stores its tier-aware API-equivalent cost; the parent sums those step costs, including runs whose tier changes between turns. Do not multiply the parent total again based on its final `service_tier`. See `prompt-cache-and-usage.md` for pricing scope.
73
+
64
74
  ## Usage fields
65
75
 
66
76
  The dashboard total includes driver `cost_usd` and `image_cost_usd` once each. Both the stored sum and legacy driver-price adjustment apply the same normalized Sonamu filters as the list. The `/api/qgrid/stats` `requests` field is an in-memory count of completed provider generations since process start, separated by provider and token name; tool follow-up generations count separately. It is not a persisted lifetime count or a count of failed attempts.
@@ -82,9 +92,11 @@ OpenAI uses the completed Responses event's per-request usage fields.
82
92
 
83
93
  Anthropic normalizes native mutually exclusive categories by summing input + cache creation + cache read into total input. Prefer positive Claude Code `total_cost_usd`; otherwise calculate from the actual serving model and TTL split.
84
94
 
85
- For image requests, keep `cost_usd` as the Codex driver model token cost. Hosted image output costs use quality/size assumptions because image-tool usage is not exposed. Explicit transparent standalone Images requests run no driver and report zero driver usage/cost; auto transparent turns preserve the Responses driver model and usage while keeping image usage/settings separately. Image usage and actual settings are preserved as `observedGeneration` alongside `requestedOptions` in the synthetic tool log. Their separate image cost uses reported usage with an explicitly conservative public-price estimate. Treat `image_cost_usd` as a price-table estimate that may be inaccurate if Codex changes its underlying image accounting.
95
+ For image requests, keep `cost_usd` as the Codex driver model token cost. Hosted image usage comes from `response.tool_usage.image_gen`, separately from the driver's `response.usage`. Explicit standalone Images requests, including `qgrid.image()`, run no driver and report zero driver usage/cost. Auto image turns preserve the Responses driver model and usage for every background while keeping standalone image usage/settings separately. Image usage and actual settings are preserved as `observedGeneration` alongside `requestedOptions` in the synthetic tool log. Price reported image usage at public token rates; only fall back to requested quality/size estimates when usage is absent. Display combined driver and image costs as dollar amounts without Cost labels; omit pricing disclaimers and cost-source metadata from the dashboard. Treat `image_cost_usd` as an API-price estimate, not subscription billing.
96
+
97
+ For `imageGeneration: "auto"`, set `is_image_generation` only when returned content actually contains an image; offering the `generate_image` function is not sufficient. Keep stored `model_name` as the driver for hosted and auto image turns. Dashboard requested-to-image labels can derive the image model from serving/image-cost metadata without changing the stored driver or adding image-generation/assumption badges.
86
98
 
87
- For `imageGeneration: "auto"`, set `is_image_generation` only when returned content actually contains an image; offering image tools is not sufficient. Keep stored `model_name` as the driver for hosted and auto transparent turns. Dashboard requested-to-image labels can derive the image model from serving/image-cost metadata without changing the stored driver or adding image-generation/assumption badges.
99
+ If the auto image stage fails or is cancelled after the chat driver has completed, preserve the completed driver result as internal failure metadata. The frame converts it with the same usage/pricing mapper used on success; terminal logging writes one generate step before aggregating the run and retains the actual/requested model and token. The parent remains `error` or `aborted` with the original error message. Do not write a completed image step, invent an image cost, expose failure metadata in the HTTP error, or record this step again on success. A driver that never completed and a direct image-only call have no completed chat usage to retain. `logger: false` still creates no logs. Log-writing failures must not replace the original generation failure.
88
100
 
89
101
  Reference images for image generation are not stored in `request_logs.user_prompt`; that field remains the text prompt. For inspection in the detail view, qgrid stores reference image metadata/base64 in the synthetic `image_generation` step's `tool_args.inputImages`. If Codex returns multiple image outputs, store `inputImages` only on the first synthetic image tool step so multi-output logs do not duplicate the same input payload on every output.
90
102
 
@@ -62,13 +62,13 @@ In 2.9.8, list and detail model labels show requested model to image model for g
62
62
 
63
63
  The list header ends its filter row with a reload button (2.10.2+) that calls the generated hooks' `refresh()` for the list and the total-cost query with the current filters; it does not refetch the token/project/model filter options, and TanStack Query's `isRefreshing` disables it while a reload is in flight.
64
64
 
65
- Structured output visibility: the list has an `Fmt` column (`json` badge from `is_structured` vs `text`) and a `broken` badge driven by `response_json_ok = false`; there is no broken-JSON list filter. At review time, all 114,647 succeeded structured rows had `response_json_ok = true` and zero false rows existed; the unindexed zero-result lookup scanned 1,286,016 rows and took about 3 seconds. Before reinstating the filter, validate the failure-signal semantics and index the predicate. The detail page shows a dark Response Type panel (zod expression by default, `type` declaration via toggle) rendered server-side from the stored `json_schema`. Plain-text rows render no panel. Rows written by pre-2.7.2 servers show `text`.
65
+ Structured output visibility: the list has an `Fmt` column (`img` when `is_image_generation` is true, otherwise `json` when `is_structured` is true, otherwise `text`) and a `broken` badge driven by `response_json_ok = false`; there is no broken-JSON list filter. At review time, all 114,647 succeeded structured rows had `response_json_ok = true` and zero false rows existed; the unindexed zero-result lookup scanned 1,286,016 rows and took about 3 seconds. Before reinstating the filter, validate the failure-signal semantics and index the predicate. The detail page shows a dark Response Type panel (zod expression by default, `type` declaration via toggle) rendered server-side from the stored `json_schema`. Plain-text rows render no panel. Rows written by pre-2.7.2 servers show `text`.
66
66
 
67
67
  Tool contract visibility: the detail page renders a Tools panel between Response Type and Steps for requests whose `tools` is non-empty. It starts collapsed and its header shows the tool count plus a responsive, truncated name list built from the detail subset — collapsed views issue no extra query. Expanding fires `toolsView` once and caches it for that request id, since a stored tool contract never changes. Each card shows the name, the full description, the parameter count, and a compact display-only zod shape; enum values past the sixth collapse to `/* +N */`, with the full shape reachable through hover and keyboard focus. All JSON Schema conversion happens server-side — the web renders completed strings and holds no schema logic.
68
68
 
69
69
  Chat token targeting: the dashboard chat panel can pin a conversation to one account. It defaults to automatic distribution and lists only active tokens whose provider matches the selected model, sending the choice as `tokenName` so the request lands on exactly that account with no fallback. Changing either the model or the token resets the existing conversation coordinates, because a thread's prompt cache belongs to the account that built it. A selection that disappears — deactivated or deleted — is cleared automatically, but the selection is preserved while the token list is still loading so a slow fetch does not silently reset the user's choice.
70
70
 
71
- OpenAI chat in 2.9.8 uses one non-stream POST with `imageGeneration: "auto"` per turn, allowing native image generation or an ordinary text response. The server handles transparent image calls directly. Keep the removed image-generation checkbox, background checkbox, automatic-generation label, and client decision tool out of the UI. Replay full text and image history, including generated images as user visual references. Abort in-flight requests when chat unmounts.
71
+ OpenAI chat uses one non-stream POST with `imageGeneration: "auto"` per turn, allowing image generation or an ordinary text response. The server registers one `generate_image` function and directly calls subscription Images for ordinary or transparent output. The driver sees full text and image history, writes the image prompt, and chooses the last 0–5 conversation images to include; zero starts a fresh image. Keep the removed image-generation checkbox, background checkbox, automatic-generation label, and client decision tool out of the UI. Replay full text and image history, including generated images as user visual references. Abort in-flight requests when chat unmounts.
72
72
 
73
73
  Clicking an attached or generated image opens a preview with extension, decoded byte size, natural width/height, original-size viewing, and download. Attachment metadata describes the existing resized JPEG conversion sent by chat; generated image bytes are preserved. Do not label attachment preview metadata as the original upload's metadata.
74
74
 
@@ -128,15 +128,25 @@ AI SDK tools plus structured output:
128
128
 
129
129
  Image generation:
130
130
 
131
- - `packages/api/scripts/smoke-test-image-generation.ts`
131
+ - `packages/api/e2e/image-generation.ts`
132
132
  - Human-initiated only.
133
133
  - Requires active OpenAI token with image entitlement and applicable migration.
134
134
  - For 2.9.8 chat, verify the actual browser POST and served bundle, not only a direct function test. A globally installed CLI on port 44900 can serve stale assets while repository tests pass; identify the listening process and its executable before assuming a rebuild or refresh affects it.
135
- - Cover auto text-only replies, native ordinary generation, server-side transparent editing, and replay of prior generated images. Explicit `true` must retain its image-required behavior. Verify driver usage stays separate from image usage and auto text replies are not image logs.
136
- - Verify extension, decoded bytes, and natural dimensions in chat image previews. A format request in prose is not evidence: the 2026-09-29 WebP probe returned PNG from a prompt-only request and WebP from explicit `outputFormat: "webp"`. Transparent output remains PNG.
137
- - Env: `QGRID_URL`, `QGRID_OPENAI_MODEL`.
135
+ - Cover auto text-only replies and the single server-side `generate_image` function for ordinary generation and transparent editing. Zero selected references must create a fresh image despite older history; positive counts must select only the last N conversation images in order, up to five. Explicit raw `true` must retain its image-required hosted behavior for non-transparent chat-model requests. Verify driver usage stays separate from image usage and auto text replies are not image logs.
136
+ - Verify extension, decoded bytes, and natural dimensions in chat image previews. A format request in prose is not evidence: the 2026-09-29 hosted-route WebP probe returned PNG from a prompt-only request and WebP from explicit `outputFormat: "webp"`. Auto chat and standalone output remain PNG-only and must reject explicit non-PNG options.
137
+ - Requires `QGRID_REAL_PROVIDER_ACCEPTANCE=1`, explicit `QGRID_URL` and `QGRID_OPENAI_MODEL`. Optional `QGRID_TOKEN_NAME` pins a subscription token. `QGRID_IMAGE_SMOKE_CASES` selects named cases; the default covers eight images plus text, rejected inputs, and cancellation. Optional `batch`, `logger-off`, and `cancel-direct` cover SDK batching, no-log calls, and cancellation during direct Images requests. Never replay generations automatically.
138
+ - Set `QGRID_IMAGE_E2E_OUTPUT` for PNGs and sanitized usage/cost/log evidence; `QGRID_IMAGE_E2E_PROJECT` groups test logs. Use an isolated DB and access-only test token, with notifications and keepalive disabled. For final acceptance, build the current API and cold-start `sonamu start` against that isolated DB rather than relying on HMR. HMR can leave a fresh dispatcher uninitialized; readiness alone does not prove the intended module version. The suite checks readiness before live calls.
139
+ - Subscription `response.completed.tool_usage.image_gen` can be present with all-zero usage for text-only or function-tool replies. This metadata alone is not a hosted image execution. Keep the zero-usage regression test and verify auto text against the real backend.
140
+ - Run auto text and generation with both Luna and Astra when changing the internal tool schema. Astra's private backend failed with `strict: true` even with bounds removed, while the same schema succeeded with `strict: false`. Keep `strict: false` and server-side validation; do not add a runtime fallback/retry.
138
141
  - Not part of CI; it consumes ChatGPT subscription quota.
139
142
 
143
+ Image failure accounting:
144
+
145
+ - `packages/api/e2e/image-failure-accounting.ts` calls the real subscription chat model and compares its completed usage with the persisted failed/aborted request and generate step.
146
+ - Start a separate built local API with `NODE_OPTIONS=--import=<absolute path>/packages/api/e2e/image-failure-preload.mjs`. This test-only preload intercepts standalone Images requests with HTTP errors, network errors, invalid image responses, cancellation waits, or a synthetic success control. It never forwards Images requests upstream; no production test switch or fallback is added.
147
+ - Both processes require `QGRID_REAL_PROVIDER_ACCEPTANCE=1`, `QGRID_IMAGE_E2E_FAULT_FILE`, and `QGRID_IMAGE_E2E_TRACE_FILE`. The preload requires `HOST=127.0.0.1` and an isolated `SONAMU_DB_NAME` starting with `qgrid_e2e_`. The runner also requires `QGRID_URL`, `QGRID_TOKEN_NAME`, and `QGRID_IMAGE_E2E_OUTPUT`.
148
+ - Verify one completed driver step and exact input/output/cache usage and cost on failure; parent status and original errors stay failed/aborted, image cost stays unknown, and `logger: false` creates no records. The success control must not double-count driver usage. Trace files retain only completed model/usage metadata, never credentials or image bytes.
149
+
140
150
  Other scripts under `scripts/smoke-test-*` and `scripts/debug-*` are ad hoc probes. Read the script header and env vars before running.
141
151
 
142
152
  ## Common Error Triage
@@ -188,21 +198,25 @@ Generated SSE handlers missing a newly added event field:
188
198
  - Treat as a real error, not a recoverable partial result.
189
199
  - Check schema strictness and `stream-json-adapter` behavior.
190
200
 
191
- `qgrid: imageGeneration is not supported with streamText`:
201
+ `qgrid: imageGeneration and imageGenerationOptions were removed from language-model options`:
202
+
203
+ - Either removed key makes `generateText` and `streamText` fail before transport. Use `generateImage` with `qgrid.image()` and flat `QgridImageProviderOptions` for image output.
204
+
205
+ `qgrid language models do not support image input`:
192
206
 
193
- - Image generation is non-stream only. Use `generateText`.
207
+ - General vision remains unsupported by the language adapter; it ignores image parts with this warning. Reference-image editing belongs in `generateImage`'s `prompt: { text, images }`.
194
208
 
195
209
  `image generation is not supported on the Anthropic route`:
196
210
 
197
211
  - Image generation is OpenAI/Codex-only.
198
212
 
199
- `imageGeneration was requested but the response contained no image`:
213
+ `qgrid.image: expected one image from the direct subscription Images route`:
200
214
 
201
- - AI SDK version-skew guard. The client requested image generation, but the server returned text-only content. Check server version and the OpenAI image-generation path.
215
+ - Image-provider version-skew guard. Each call requires exactly one image with `generation.route: "codex-images"`. Check that the server supports the explicit image model and update the server and SDK together.
202
216
 
203
217
  `reference image input is too large for JSON transport`:
204
218
 
205
- - AI SDK reference-image guard. The client passed a base64 image data URL large enough to likely exceed qgrid's JSON request body transport. Compress or resize the image before calling `generateText`, preferably as WebP/JPEG.
219
+ - AI SDK reference-image guard. The client passed a base64 image data URL large enough to likely exceed qgrid's JSON request body transport. Compress or resize the image before calling `generateImage`, preferably as WebP/JPEG.
206
220
 
207
221
  `qgrid query transport failed: response headers timed out`:
208
222
 
@@ -1 +0,0 @@
1
- var e=1e6,t=4;function n(t){return t/e}function r(e,n=!1){return`${n?`Known `:``}$${e.toFixed(t)}`}function i(e){return r(n(e))}function a(e){let t=e.is_image_generation&&e.image_cost_usd===null;return e.cost_usd===null&&e.image_cost_usd===null?t?`Unknown`:`—`:r(n((e.cost_usd??0)+(e.image_cost_usd??0)),t)}function o(e){return e.input_tokens<=0?`—`:`${Math.round(e.cache_read_tokens/e.input_tokens*100)}%`}export{r as i,i as n,a as r,o as t};