@cartanova/qgrid-cli 2.10.4 → 2.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/dist/application/monit/monit.frame.js +1 -1
- package/bundle/dist/application/qgrid/expired-token-reminder.js +1 -1
- package/bundle/dist/application/qgrid/oauth.js +1 -1
- package/bundle/dist/application/qgrid/qgrid-image-generation.js +6 -6
- package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +24 -7
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +8 -3
- package/bundle/dist/application/qgrid/qgrid.frame.js +25 -11
- package/bundle/dist/application/qgrid/qgrid.types.js +4 -8
- package/bundle/dist/application/qgrid/token-death.js +1 -1
- package/bundle/dist/application/qgrid/token-window-keepalive.js +1 -1
- package/bundle/dist/application/qgrid/tool-emulation.js +1 -1
- package/bundle/dist/application/request-log/request-log.model.js +23 -17
- package/bundle/dist/i18n/sd.generated.js +3 -3
- package/bundle/dist/sonamu.config.js +2 -2
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +1 -1
- package/bundle/dist/utils/providers/anthropic/anthropic-thinking.js +1 -1
- package/bundle/dist/utils/providers/openai/openai-backend-protocol.js +31 -9
- package/bundle/dist/utils/providers/openai/openai-direct-client.js +5 -4
- package/bundle/dist/utils/providers/openai/openai-dispatcher.js +111 -57
- package/bundle/dist/utils/providers/openai/openai-image-generation.js +25 -17
- package/bundle/dist/utils/providers/openai/openai-refresh.js +1 -1
- package/bundle/src/application/qgrid/qgrid-image-generation.test.ts +34 -5
- package/bundle/src/application/qgrid/qgrid-image-generation.ts +17 -13
- package/bundle/src/application/qgrid/qgrid-response-format.test.ts +11 -0
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +87 -9
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +45 -5
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +136 -2
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +11 -1
- package/bundle/src/application/qgrid/qgrid.frame.test.ts +73 -0
- package/bundle/src/application/qgrid/qgrid.frame.ts +43 -6
- package/bundle/src/application/qgrid/qgrid.types.ts +14 -4
- package/bundle/src/application/request-log/request-log.entity.json +2 -2
- package/bundle/src/application/request-log/request-log.model.test.ts +17 -12
- package/bundle/src/application/request-log/request-log.model.ts +8 -2
- package/bundle/src/application/sonamu.generated.http +2 -2
- package/bundle/src/i18n/sd.generated.ts +2 -2
- package/bundle/src/utils/providers/openai/openai-backend-protocol.test.ts +42 -2
- package/bundle/src/utils/providers/openai/openai-backend-protocol.ts +46 -2
- package/bundle/src/utils/providers/openai/openai-direct-client.test.ts +15 -0
- package/bundle/src/utils/providers/openai/openai-direct-client.ts +14 -4
- package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +235 -32
- package/bundle/src/utils/providers/openai/openai-dispatcher.ts +174 -72
- package/bundle/src/utils/providers/openai/openai-image-generation.test.ts +89 -0
- package/bundle/src/utils/providers/openai/openai-image-generation.ts +26 -15
- package/bundle/web-dist/client/assets/cost-DH2jZbrw.js +1 -0
- package/bundle/web-dist/client/assets/{index-reW8VvJF.js → index-D9ryIyL-.js} +4 -4
- package/bundle/web-dist/client/assets/logs-CLf2VNfk.js +1 -0
- package/bundle/web-dist/client/assets/{monit-DkABv7PG.js → monit-DojxOLj6.js} +1 -1
- package/bundle/web-dist/client/assets/{routes-BRmQ4M3-.js → routes-DXJhNvbI.js} +2 -2
- package/bundle/web-dist/client/assets/{services.generated-jnPHYMER.js → services.generated-DqFYgWsY.js} +1 -1
- package/bundle/web-dist/client/assets/{settings-BHvtycEZ.js → settings-bopyz9pa.js} +1 -1
- package/bundle/web-dist/client/assets/{show-qeTIzgDZ.js → show-DbLD3MzB.js} +1 -1
- package/bundle/web-dist/client/assets/{tokens-5Bep_G-w.js → tokens-2-AIi9sZ.js} +1 -1
- package/bundle/web-dist/client/assets/{use-oauth-login-flow-Cx-QO25M.js → use-oauth-login-flow-CnqTWs_H.js} +1 -1
- package/bundle/web-dist/client/assets/{use-update-token-mutation-DQCwCYfR.js → use-update-token-mutation-Cxb9K0fM.js} +1 -1
- package/bundle/web-dist/client/index.html +2 -2
- package/bundle/web-dist/server/assets/{cost-CSH_t17I.js → cost-eEHeTRT5.js} +5 -1
- package/bundle/web-dist/server/assets/{lazyRouteComponent-CUR-kWzI.js → lazyRouteComponent-Bi0fYGLs.js} +2 -2
- package/bundle/web-dist/server/assets/{logs-CagNGTBj.js → logs-CIVFxfoi.js} +11 -8
- package/bundle/web-dist/server/assets/{monit-DSxs4Jnl.js → monit-DBXezg7R.js} +4 -4
- package/bundle/web-dist/server/assets/{plus-Bvdj8Pwx.js → plus-BH-t4cm_.js} +1 -1
- package/bundle/web-dist/server/assets/{react-dom-CyuShs9K.js → react-dom-CZ2eynd8.js} +1 -1
- package/bundle/web-dist/server/assets/{refresh-cw-3QAiSWBd.js → refresh-cw-CosAdcz0.js} +1 -1
- package/bundle/web-dist/server/assets/{routes-i-sTqXMX.js → routes-COWYIvA-.js} +5 -6
- package/bundle/web-dist/server/assets/{services.generated-FPwsG9p3.js → services.generated-607iwhMI.js} +2 -2
- package/bundle/web-dist/server/assets/{settings-DeHNwoMv.js → settings-C_6dpK5p.js} +3 -3
- package/bundle/web-dist/server/assets/{show-C4jk1Hrp.js → show-DY396-LD.js} +8 -20
- package/bundle/web-dist/server/assets/{tokens-odgS2-9q.js → tokens-COvV7qUP.js} +4 -4
- package/bundle/web-dist/server/assets/{use-oauth-login-flow-DTQKdYBO.js → use-oauth-login-flow-VYCYf3uv.js} +1 -1
- package/bundle/web-dist/server/assets/{use-update-token-mutation-xvXbPrCI.js → use-update-token-mutation-D8XYV5of.js} +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +10 -10
- package/package.json +3 -2
- package/skills/qgrid/SKILL.md +9 -8
- package/skills/qgrid/references/ai-sdk-provider-contract.md +25 -16
- package/skills/qgrid/references/decision-rationale.md +8 -8
- package/skills/qgrid/references/openai-codex-runtime.md +23 -11
- package/skills/qgrid/references/request-log-run-lifecycle.md +4 -2
- package/skills/qgrid/references/sonamu-api-web-flow.md +2 -2
- package/skills/qgrid/references/verification-and-debugging.md +23 -9
- package/bundle/web-dist/client/assets/cost-CpjVoAqp.js +0 -1
- package/bundle/web-dist/client/assets/logs-DDIt97D1.js +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { A as require_react, E as useQuery, X as __toESM, i as RequestLogStepService, k as require_jsx_runtime, l as isSonamuError, q as __commonJSMin, r as RequestLogService } from "./services.generated-
|
|
2
|
-
import { a as Link } from "./lazyRouteComponent-
|
|
1
|
+
import { A as require_react, E as useQuery, X as __toESM, i as RequestLogStepService, k as require_jsx_runtime, l as isSonamuError, q as __commonJSMin, r as RequestLogService } from "./services.generated-607iwhMI.js";
|
|
2
|
+
import { a as Link } from "./lazyRouteComponent-Bi0fYGLs.js";
|
|
3
3
|
import { c as lucideX, d as Markdown, t as Route, u as remarkGfm } from "../entry-server.generated.js";
|
|
4
|
-
import { n as formatMicroUsd, t as cacheHitRate } from "./cost-
|
|
4
|
+
import { n as formatMicroUsd, t as cacheHitRate } from "./cost-eEHeTRT5.js";
|
|
5
5
|
import { t as requestModelDisplay } from "./request-model-CnCeaNLE.js";
|
|
6
6
|
//#region ../../node_modules/.pnpm/@babel+runtime@7.29.2/node_modules/@babel/runtime/helpers/extends.js
|
|
7
7
|
var require_extends = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -2194,15 +2194,11 @@ function MetricsPanel({ data, toolCallCount }) {
|
|
|
2194
2194
|
value: formatTokensPerSec(data.output_tokens, data.duration_ms, data.ttft_ms, tokensPerSecEnabled)
|
|
2195
2195
|
}),
|
|
2196
2196
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
2197
|
-
label: "Driver
|
|
2197
|
+
label: "Driver",
|
|
2198
2198
|
value: data.cost_usd !== null ? formatMicroUsd(data.cost_usd) : "—"
|
|
2199
2199
|
}),
|
|
2200
2200
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
2201
|
-
label: "
|
|
2202
|
-
value: data.cost_source ?? "legacy"
|
|
2203
|
-
}),
|
|
2204
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
2205
|
-
label: "Image Cost",
|
|
2201
|
+
label: "Image",
|
|
2206
2202
|
value: data.image_cost_usd !== null ? formatMicroUsd(data.image_cost_usd) : "—"
|
|
2207
2203
|
}),
|
|
2208
2204
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
@@ -2277,8 +2273,8 @@ function formatTokensPerSec(outputTokens, durationMs, ttftMs, enabled) {
|
|
|
2277
2273
|
}
|
|
2278
2274
|
function CompactMetric({ label, value }) {
|
|
2279
2275
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
2280
|
-
className: "min-w-0",
|
|
2281
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
2276
|
+
className: "min-w-0 self-end",
|
|
2277
|
+
children: [label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
2282
2278
|
className: "text-[10px] uppercase tracking-wider text-sand-400 font-medium truncate",
|
|
2283
2279
|
children: label
|
|
2284
2280
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
@@ -2534,14 +2530,7 @@ function StepTreeItem({ entry, tokensPerSecEnabled }) {
|
|
|
2534
2530
|
label: "Cache 5m / 1h",
|
|
2535
2531
|
value: `${generate.cacheCreation5mTokens === null ? "—" : formatNum(generate.cacheCreation5mTokens)} / ${generate.cacheCreation1hTokens === null ? "—" : formatNum(generate.cacheCreation1hTokens)}`
|
|
2536
2532
|
}),
|
|
2537
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
|
|
2538
|
-
label: "Cost",
|
|
2539
|
-
value: generate.costUsd === null ? "—" : formatMicroUsd(generate.costUsd)
|
|
2540
|
-
}),
|
|
2541
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
|
|
2542
|
-
label: "Cost Source",
|
|
2543
|
-
value: generate.costSource ?? "legacy"
|
|
2544
|
-
}),
|
|
2533
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, { value: generate.costUsd === null ? "—" : formatMicroUsd(generate.costUsd) }),
|
|
2545
2534
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
|
|
2546
2535
|
label: "Reasoning",
|
|
2547
2536
|
value: generate.reasoningTokens !== null && generate.reasoningTokens !== void 0 ? formatNum(generate.reasoningTokens) : "—"
|
|
@@ -2598,7 +2587,6 @@ function buildStepTree(steps) {
|
|
|
2598
2587
|
cacheCreation5mTokens: step.cache_creation_5m_tokens,
|
|
2599
2588
|
cacheCreation1hTokens: step.cache_creation_1h_tokens,
|
|
2600
2589
|
costUsd: step.cost_usd,
|
|
2601
|
-
costSource: step.cost_source,
|
|
2602
2590
|
durationMs: step.duration_ms,
|
|
2603
2591
|
ttftMs: step.ttft_ms,
|
|
2604
2592
|
finishReason: step.finish_reason,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { A as require_react, O as useQueryClient, X as __toESM, k as require_jsx_runtime, n as QgridService, o as TokenService } from "./services.generated-
|
|
1
|
+
import { A as require_react, O as useQueryClient, X as __toESM, k as require_jsx_runtime, n as QgridService, o as TokenService } from "./services.generated-607iwhMI.js";
|
|
2
2
|
import { f as lucideKeyRound, o as Q } from "../entry-server.generated.js";
|
|
3
|
-
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-
|
|
4
|
-
import { n as OAuthCodeEntry, t as useOAuthLoginFlow } from "./use-oauth-login-flow-
|
|
5
|
-
import { t as lucidePlus } from "./plus-
|
|
3
|
+
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-D8XYV5of.js";
|
|
4
|
+
import { n as OAuthCodeEntry, t as useOAuthLoginFlow } from "./use-oauth-login-flow-VYCYf3uv.js";
|
|
5
|
+
import { t as lucidePlus } from "./plus-BH-t4cm_.js";
|
|
6
6
|
//#region src/components/qgrid/AddTokenModal.tsx
|
|
7
7
|
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
8
8
|
var import_jsx_runtime = require_jsx_runtime();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as require_react, O as useQueryClient, X as __toESM, k as require_jsx_runtime, n as QgridService, o as TokenService } from "./services.generated-
|
|
1
|
+
import { A as require_react, O as useQueryClient, X as __toESM, k as require_jsx_runtime, n as QgridService, o as TokenService } from "./services.generated-607iwhMI.js";
|
|
2
2
|
import { o as Q } from "../entry-server.generated.js";
|
|
3
3
|
//#region src/components/qgrid/OAuthCodeEntry.tsx
|
|
4
4
|
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as useMutation, n as QgridService } from "./services.generated-
|
|
1
|
+
import { T as useMutation, n as QgridService } from "./services.generated-607iwhMI.js";
|
|
2
2
|
//#region src/services/token/use-update-token-mutation.ts
|
|
3
3
|
function useUpdateTokenMutation() {
|
|
4
4
|
return useMutation({ mutationFn: ({ id, name, quotaThreshold, weight, keepaliveEnabled }) => QgridService.updateToken(id, name, quotaThreshold, weight, keepaliveEnabled) });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { A as require_react, B as matchQuery, C as union, D as QueryClientProvider, F as notifyManager, G as focusManager, H as partialMatchKey, I as functionalUpdate, J as __exportAll, K as Subscribable, L as hashKey, M as infiniteQueryBehavior, N as Query, P as onlineManager, R as hashQueryKeyByOptions, S as string$2, U as resolveStaleTime, V as noop$1, W as skipToken, X as __toESM, Y as __require, _ as custom, b as number$1, c as fetch$1, d as SD, f as SUPPORTED_LOCALES, g as boolean$1, h as array, j as Mutation, k as require_jsx_runtime, m as _enum, n as QgridService, o as TokenService, p as setLocale, q as __commonJSMin, s as SonamuQueryMode, u as zArrayable, v as date, w as unknown, x as object, y as int, z as matchMutation } from "./assets/services.generated-
|
|
2
|
-
import { A as decodePath, C as findRouteMatch, D as createLRUCache, E as processRouteTree, F as last, I as replaceEqualDeep, M as findLast, N as functionalUpdate$1, O as invariant, P as isPromise, S as findFlatMatch, T as processRouteMasks, _ as interpolatePath, a as Link$1, b as trimPath, c as usePrevious$1, d as useRouter, f as getRouterContext, g as cleanPath, h as isNotFound, i as createRootRouteWithContext, j as deepEqual$2, k as createControlledPromise, l as matchContext, m as rootRouteId, n as createFileRoute, p as warning, r as createLazyFileRoute, s as useLayoutEffect$3, t as lazyRouteComponent, u as useRouterState, v as joinPaths, w as findSingleMatch, x as trimPathRight, y as resolvePath } from "./assets/lazyRouteComponent-
|
|
3
|
-
import { n as require_shim, t as require_react_dom } from "./assets/react-dom-
|
|
1
|
+
import { A as require_react, B as matchQuery, C as union, D as QueryClientProvider, F as notifyManager, G as focusManager, H as partialMatchKey, I as functionalUpdate, J as __exportAll, K as Subscribable, L as hashKey, M as infiniteQueryBehavior, N as Query, P as onlineManager, R as hashQueryKeyByOptions, S as string$2, U as resolveStaleTime, V as noop$1, W as skipToken, X as __toESM, Y as __require, _ as custom, b as number$1, c as fetch$1, d as SD, f as SUPPORTED_LOCALES, g as boolean$1, h as array, j as Mutation, k as require_jsx_runtime, m as _enum, n as QgridService, o as TokenService, p as setLocale, q as __commonJSMin, s as SonamuQueryMode, u as zArrayable, v as date, w as unknown, x as object, y as int, z as matchMutation } from "./assets/services.generated-607iwhMI.js";
|
|
2
|
+
import { A as decodePath, C as findRouteMatch, D as createLRUCache, E as processRouteTree, F as last, I as replaceEqualDeep, M as findLast, N as functionalUpdate$1, O as invariant, P as isPromise, S as findFlatMatch, T as processRouteMasks, _ as interpolatePath, a as Link$1, b as trimPath, c as usePrevious$1, d as useRouter, f as getRouterContext, g as cleanPath, h as isNotFound, i as createRootRouteWithContext, j as deepEqual$2, k as createControlledPromise, l as matchContext, m as rootRouteId, n as createFileRoute, p as warning, r as createLazyFileRoute, s as useLayoutEffect$3, t as lazyRouteComponent, u as useRouterState, v as joinPaths, w as findSingleMatch, x as trimPathRight, y as resolvePath } from "./assets/lazyRouteComponent-Bi0fYGLs.js";
|
|
3
|
+
import { n as require_shim, t as require_react_dom } from "./assets/react-dom-CZ2eynd8.js";
|
|
4
4
|
import minpath from "node:path";
|
|
5
5
|
import minproc from "node:process";
|
|
6
6
|
import { fileURLToPath as urlToPath } from "node:url";
|
|
@@ -46874,7 +46874,7 @@ function ChatWidget() {
|
|
|
46874
46874
|
}
|
|
46875
46875
|
//#endregion
|
|
46876
46876
|
//#region src/components/Sidebar.tsx
|
|
46877
|
-
var qgridVersion = "2.10.
|
|
46877
|
+
var qgridVersion = "2.10.6";
|
|
46878
46878
|
function Sidebar({ className }) {
|
|
46879
46879
|
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
46880
46880
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", {
|
|
@@ -68752,15 +68752,15 @@ function RootComponent() {
|
|
|
68752
68752
|
}
|
|
68753
68753
|
//#endregion
|
|
68754
68754
|
//#region src/routes/tokens.tsx
|
|
68755
|
-
var $$splitComponentImporter$5 = () => import("./assets/tokens-
|
|
68755
|
+
var $$splitComponentImporter$5 = () => import("./assets/tokens-COvV7qUP.js");
|
|
68756
68756
|
var Route$5 = createFileRoute("/tokens")({ component: lazyRouteComponent($$splitComponentImporter$5, "component") });
|
|
68757
68757
|
//#endregion
|
|
68758
68758
|
//#region src/routes/settings.tsx
|
|
68759
|
-
var $$splitComponentImporter$4 = () => import("./assets/settings-
|
|
68759
|
+
var $$splitComponentImporter$4 = () => import("./assets/settings-C_6dpK5p.js");
|
|
68760
68760
|
var Route$4 = createFileRoute("/settings")({ component: lazyRouteComponent($$splitComponentImporter$4, "component") });
|
|
68761
68761
|
//#endregion
|
|
68762
68762
|
//#region src/routes/monit.tsx
|
|
68763
|
-
var $$splitComponentImporter$3 = () => import("./assets/monit-
|
|
68763
|
+
var $$splitComponentImporter$3 = () => import("./assets/monit-DBXezg7R.js");
|
|
68764
68764
|
var monitSearchSchema = object({ level: _enum([
|
|
68765
68765
|
"all",
|
|
68766
68766
|
"warn",
|
|
@@ -69137,7 +69137,7 @@ object({
|
|
|
69137
69137
|
_enum(["A"]);
|
|
69138
69138
|
//#endregion
|
|
69139
69139
|
//#region src/routes/logs.tsx
|
|
69140
|
-
var $$splitComponentImporter$2 = () => import("./assets/logs-
|
|
69140
|
+
var $$splitComponentImporter$2 = () => import("./assets/logs-CIVFxfoi.js");
|
|
69141
69141
|
var logsSearchSchema = object({
|
|
69142
69142
|
token: string$2().optional(),
|
|
69143
69143
|
project: string$2().optional(),
|
|
@@ -69151,11 +69151,11 @@ var Route$2 = createFileRoute("/logs")({
|
|
|
69151
69151
|
});
|
|
69152
69152
|
//#endregion
|
|
69153
69153
|
//#region src/routes/index.tsx
|
|
69154
|
-
var $$splitComponentImporter$1 = () => import("./assets/routes-
|
|
69154
|
+
var $$splitComponentImporter$1 = () => import("./assets/routes-COWYIvA-.js");
|
|
69155
69155
|
var Route$1 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter$1, "component") });
|
|
69156
69156
|
//#endregion
|
|
69157
69157
|
//#region src/routes/requests/show.tsx
|
|
69158
|
-
var $$splitComponentImporter = () => import("./assets/show-
|
|
69158
|
+
var $$splitComponentImporter = () => import("./assets/show-DY396-LD.js");
|
|
69159
69159
|
var showSearchSchema = object({ id: number$1() });
|
|
69160
69160
|
var Route = createFileRoute("/requests/show")({
|
|
69161
69161
|
validateSearch: showSearchSchema,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cartanova/qgrid-cli",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/cartanova-ai/qgrid"
|
|
@@ -59,7 +59,8 @@
|
|
|
59
59
|
"node": ">=20"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
|
-
"bundle": "
|
|
62
|
+
"bundle": "cd ../api && pnpm build && cd ../cli && pnpm run bundle:copy",
|
|
63
|
+
"bundle:copy": "pnpm run check:bundle-dependencies && rm -rf bundle && mkdir -p bundle/public && touch bundle/.env && cp -r ../api/dist ./bundle/dist && cp -r ../api/web-dist ./bundle/web-dist && cp -r ../api/src ./bundle/src",
|
|
63
64
|
"build": "tsdown",
|
|
64
65
|
"check:bundle-dependencies": "node tooling/check-bundled-api-dependencies.mjs",
|
|
65
66
|
"postinstall": "node postinstall.mjs",
|
package/skills/qgrid/SKILL.md
CHANGED
|
@@ -38,13 +38,13 @@ When helping a user set up local `@cartanova/qgrid-ai-sdk`, `createQgridLogger`,
|
|
|
38
38
|
|
|
39
39
|
Prefer `QGRID_PROJECT_NAME` as the project-wide default. Use config `projectName` only for a deliberate per-call/per-provider override. Do not add `projectName` or `project_name` under `providerOptions.qgrid`; current code does not read it there.
|
|
40
40
|
|
|
41
|
-
When writing AI SDK examples or setup code,
|
|
41
|
+
When writing AI SDK examples or setup code, use `satisfies QgridProviderOptions` for language-model options or `satisfies QgridImageProviderOptions` for image-model options under `providerOptions.qgrid`. AI SDK types the outer `providerOptions` as a generic JSON record, so it cannot infer qgrid's option names and values by itself. These types describe the inner qgrid namespace, not the outer record.
|
|
42
42
|
|
|
43
43
|
Request logging is enabled by default. Use `providerOptions.qgrid.logger: false` only for a call that must create zero qgrid request-log rows. The same option suppresses both qgrid's server-native logging and `createQgridLogger` telemetry logging for that generation; it does not disable generation, AI SDK tool execution, multi-step continuation, or OpenAI cache affinity. The old `logMode` input has been removed and must not appear in setup code or raw qgrid payloads.
|
|
44
44
|
|
|
45
45
|
## Non-Negotiable Boundaries
|
|
46
46
|
|
|
47
|
-
- Use `packages/ai-sdk` as the active public SDK surface.
|
|
47
|
+
- Use `packages/ai-sdk` as the active public SDK surface. `qgrid()` implements `LanguageModelV3` for text, tools, and structured output; it has no public image-generation options and does not support general vision. `qgrid.image("openai/gpt-image-2")` implements `ImageModelV3` for standard AI SDK `generateImage`, directly calling subscription Images for generation or reference editing without a chat driver. Dashboard chat registers its image tool internally; do not add a public SDK tool factory. See `references/ai-sdk-provider-contract.md` for image options, usage, and metadata.
|
|
48
48
|
- Expose thinking only as `providerOptions.qgrid.thinking?: boolean`: omission preserves the model policy, false overrides effort, and unsupported/unverified off requests fail explicitly. See the SDK and provider runtime references for the dated support snapshot. Keep Anthropic thinking controls separate from mandatory configuration and memory isolation. Haiku/Sonnet 5.5 off uses a computed thinking-only native body override with Claude Code >=2.1.295; never inherit or expose arbitrary extra body.
|
|
49
49
|
- The old v1 SDK package (`packages/sdk`, `@cartanova/qgrid-sdk`) has been removed from the repository. It survives only as a deprecated npm artifact (1.9.0); do not resurrect it or write new code against it.
|
|
50
50
|
- Let the server infer single-turn versus tool-run request-log lifecycle. Do not recreate caller-selected logging modes in the SDK or API examples.
|
|
@@ -54,11 +54,11 @@ Request logging is enabled by default. Use `providerOptions.qgrid.logger: false`
|
|
|
54
54
|
- Prefer `getPuri()` for Sonamu model queries. Treat `getDB()` as a legacy escape hatch only when Puri cannot express the required query. Inside `@transactional` methods, all participating queries must use `getPuri()`; `getDB()` does not reuse Sonamu's ambient transaction connection.
|
|
55
55
|
- Never hand-author migration files for Sonamu-managed schema. In the qgrid source repository, change the entity definition, inspect `sonamu migrate status`, and create migration files with `mise exec -- pnpm --dir packages/api sonamu migrate generate`. Inspect the generated files before applying them. If Sonamu cannot express a required schema change, stop and ask rather than silently replacing its workflow with a custom migration.
|
|
56
56
|
- Keep OpenAI Codex built-in tools, apps, plugins, skills, web search, shell, and environment instruction blocks disabled unless the user explicitly asks for agentic Codex behavior.
|
|
57
|
-
- Treat OpenAI image generation as opt-in outside dashboard chat.
|
|
57
|
+
- Treat OpenAI image generation as opt-in outside dashboard chat. OpenAI `qgrid_chat` opts into raw API `imageGeneration: "auto"` on every turn. Auto mode registers only the server-executed `generate_image` function and calls Codex standalone Images for ordinary or transparent output. The raw explicit chat-model `imageGeneration: true` hosted route remains supported separately. The public SDK uses only `qgrid.image()` / `generateImage` for images, calling standalone Images for every background. Do not expose the raw `imageGeneration` or `imageGenerationOptions` fields as language-model options or retain deprecated shims. Do not reintroduce a client decision tool followed by another GPT generation request. Image costs use reported image usage at public token rates when available; otherwise estimate per image from the requested quality/size table. Auto or unsupported sizes use the 1536x1024 price; omitted quality uses medium. Display combined driver and image costs as dollar amounts without Cost labels. Do not show Known/Estimated labels, pricing disclaimers, or cost-source metadata in the dashboard.
|
|
58
58
|
|
|
59
59
|
## Request log IDs (2.10.0+)
|
|
60
60
|
|
|
61
|
-
- Successful raw query responses and stream `done` events expose optional top-level `requestLogId`.
|
|
61
|
+
- Successful raw query responses and stream `done` events expose optional top-level `requestLogId`. AI SDK text calls expose it as `providerMetadata.qgrid.requestLogId`; await stream metadata after consuming the stream. `generateImage` exposes each call's ID under `providerMetadata.qgrid.images[]` alongside generation metadata.
|
|
62
62
|
- The ID identifies the parent request log and stays the same across tool-run steps. It is omitted with `logger: false`. Keep `runContext.requestLogId` for tool continuation only; do not replay the top-level ID as continuation state.
|
|
63
63
|
- Update both server and AI SDK to 2.10.0 or later to consume the metadata. Older servers may omit the ID. This contract does not add IDs to delta or error events or change `createQgridLogger` for external providers.
|
|
64
64
|
|
|
@@ -66,17 +66,18 @@ Request logging is enabled by default. Use `providerOptions.qgrid.logger: false`
|
|
|
66
66
|
|
|
67
67
|
- Raw `imageGeneration: "auto"` permits text-only replies; explicit `true` still requires an image. Both use non-stream requests. Auto mode rejects caller tools and output schemas.
|
|
68
68
|
- Dashboard chat sends complete text and image history, including prior generated images as user visual references. Do not assume provider conversation state retains image bytes or send assistant `input_image` blocks.
|
|
69
|
-
-
|
|
69
|
+
- Register only `generate_image` for auto mode, with server-validated arguments `prompt: string`, `transparent_background: boolean`, and `num_last_images_to_include: integer` from 0 to 5. Use `strict: false` on the wire, matching Codex; Astra subscription requests fail with `strict: true`. Keep complete server-side validation. The driver sees the complete conversation and writes a self-contained image prompt. Zero references means fresh generation; a positive count selects the last N conversation images in chronological order for editing. Do not forward all historical images automatically.
|
|
70
|
+
- Run the function directly against subscription Images with the selected token and cancellation signal, without a second text-driver request. Preserve driver usage separately from image usage. Do not register the native hosted `image_generation` tool in auto mode.
|
|
70
71
|
- Request-log model labels show requested model to image model using serving/image-cost metadata. Do not hardcode `gpt-image-2.5` or restore image-generation/assumption badges. In auto mode, mark image generation only when an image was produced.
|
|
71
72
|
- Read `references/decision-rationale.md` and `references/openai-codex-runtime.md` before changing this routing or its cache boundary.
|
|
72
73
|
|
|
73
74
|
## Image output formats (2.9.6+)
|
|
74
75
|
|
|
75
|
-
-
|
|
76
|
+
- Raw explicit chat-model hosted Responses requests support `imageGenerationOptions.outputFormat: "png" | "jpeg" | "webp"`. Omission retains PNG. Auto chat and public `generateImage` use standalone Images and support PNG only; reject other formats before generation. Public image options use flat `QgridImageProviderOptions`.
|
|
76
77
|
- Preserve the actual returned MIME type in qgrid content, AI SDK files, and request-log image URLs; do not relabel bytes as the requested format.
|
|
77
|
-
- Transparent generation
|
|
78
|
+
- Transparent generation uses the standalone Codex Images route and accepts PNG only. Reject transparent JPEG/WebP requests before generation. Do not convert output or fall back to another route.
|
|
78
79
|
- Keep the AI SDK standard `GeneratedFile.mediaType: string` contract. Do not add a qgrid-specific image wrapper or conditional return type.
|
|
79
|
-
- A 2026-09-29
|
|
80
|
+
- A 2026-09-29 hosted-route probe returned PNG for a prompt-only WebP request and WebP when `outputFormat: "webp"` was supplied. Model prose is not format evidence; inspect file bytes. Chat does not infer output format from prose, and no post-generation conversion is performed.
|
|
80
81
|
|
|
81
82
|
## Verification
|
|
82
83
|
|
|
@@ -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
|
|
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
|
-
|
|
74
|
-
|
|
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,7 +144,7 @@ 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`;
|
|
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
149
|
- 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.
|
|
137
150
|
- Fast mode changes upstream service-tier routing only. It does not change token weights or client concurrency.
|
|
@@ -139,7 +152,7 @@ Fast mode contract:
|
|
|
139
152
|
|
|
140
153
|
## Request construction
|
|
141
154
|
|
|
142
|
-
The provider sends `POST /api/qgrid/query` for generate and `prepareStream` plus SSE for stream.
|
|
155
|
+
The language provider sends `POST /api/qgrid/query` for generate and `prepareStream` plus SSE for stream.
|
|
143
156
|
|
|
144
157
|
Payload responsibilities:
|
|
145
158
|
|
|
@@ -153,9 +166,8 @@ Payload responsibilities:
|
|
|
153
166
|
- Preserve and resend `runContext` for tool-call follow-ups.
|
|
154
167
|
- 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
168
|
- Derive/store/resend opaque OpenAI affinity coordinates by `sessionKey`.
|
|
156
|
-
-
|
|
157
|
-
-
|
|
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.
|
|
169
|
+
- Keep language-model prompts text-only. Ignore image parts with an unsupported warning; this adapter does not support general vision.
|
|
170
|
+
- 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
171
|
|
|
160
172
|
Tools and `jsonSchema` can be used together as of qgrid 2.5.4. On OpenAI the
|
|
161
173
|
server composes them into one strict action envelope enforced by constrained
|
|
@@ -194,9 +206,7 @@ rejected.
|
|
|
194
206
|
|
|
195
207
|
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
208
|
|
|
197
|
-
|
|
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.
|
|
209
|
+
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
210
|
|
|
201
211
|
## Response mapping
|
|
202
212
|
|
|
@@ -206,13 +216,12 @@ Since 2.10.0, `generateText` exposes the parent request-log ID at `result.provid
|
|
|
206
216
|
|
|
207
217
|
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
218
|
|
|
209
|
-
|
|
219
|
+
Language-model response content maps to AI SDK content:
|
|
210
220
|
|
|
211
221
|
- qgrid `text` -> AI SDK text content.
|
|
212
222
|
- 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
223
|
|
|
215
|
-
|
|
224
|
+
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
225
|
|
|
217
226
|
`finishReason` maps `tool-calls` when qgrid returns tool calls.
|
|
218
227
|
|
|
@@ -220,7 +229,7 @@ For tools plus structured output, qgrid serializes the final schema-constrained
|
|
|
220
229
|
`answer` as JSON text. AI SDK then parses and validates that text into
|
|
221
230
|
`Output.object`'s `output`. This path requires both server and SDK 2.5.4 or later.
|
|
222
231
|
|
|
223
|
-
|
|
232
|
+
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
233
|
|
|
225
234
|
For a successful Fable refusal fallback (Fable 5 or 5.1):
|
|
226
235
|
|
|
@@ -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.
|
|
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.
|
|
238
|
-
-
|
|
239
|
-
- `imageGenerationOptions` supports quality, size, and background.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
52
|
-
|
|
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,20 +181,30 @@ 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
188
|
+
### Standalone image routes
|
|
187
189
|
|
|
188
|
-
The image
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
196
|
+
### Automatic dashboard chat images
|
|
195
197
|
|
|
196
|
-
|
|
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
|
|
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`.
|
|
207
|
+
|
|
208
|
+
### Size hints and observed dimensions (2026-10-13)
|
|
209
|
+
|
|
210
|
+
`size` accepts `auto` or positive-integer WIDTHxHEIGHT hints. Keep the requested value separate from observed output metadata; do not promise exact dimensions or inject a resize. Prompt for the desired ratio. Twelve live subscription requests across hosted Responses and standalone generation/edit returned 841x1870 for 9:20, 941x1672 for 4K portrait, and 2172x724 for 4:1 (clamped to 3:1). Explicit 1152x2560 and even 1x1 hints returned 1254x1254 for a neutral prompt. These approximately 1.57MP observations are not a universal limit. Public API limits (16-pixel multiples, up to 4K) are not the private backend contract. Latest upstream Codex at `12cca49c81c8aa6a37a5cc4e39964ee3b70f9458` hardcodes `size: auto` for image generation/editing and saves bytes without resizing.
|
|
@@ -82,9 +82,11 @@ OpenAI uses the completed Responses event's per-request usage fields.
|
|
|
82
82
|
|
|
83
83
|
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
84
|
|
|
85
|
-
For image requests, keep `cost_usd` as the Codex driver model token cost. Hosted image
|
|
85
|
+
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.
|
|
86
86
|
|
|
87
|
-
For `imageGeneration: "auto"`, set `is_image_generation` only when returned content actually contains an image; offering
|
|
87
|
+
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.
|
|
88
|
+
|
|
89
|
+
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
90
|
|
|
89
91
|
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
92
|
|
|
@@ -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 (`
|
|
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
|
|
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
|
|