@cartanova/qgrid-cli 2.4.6 → 2.4.8
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/qgrid/qgrid-run-lifecycle.js +15 -1
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +12 -4
- package/bundle/dist/application/qgrid/qgrid.frame.js +20 -1
- package/bundle/dist/application/qgrid/qgrid.types.js +38 -2
- package/bundle/dist/application/request-log/request-log.model.js +49 -22
- package/bundle/dist/application/request-log-step/request-log-step.types.js +8 -1
- package/bundle/dist/application/sonamu.generated.js +37 -1
- package/bundle/dist/application/sonamu.generated.sso.js +25 -1
- package/bundle/dist/i18n/sd.generated.js +13 -1
- package/bundle/dist/migrations/20260718180824_alter_request_logs_add5_alter1.js +20 -0
- package/bundle/dist/migrations/20260718180825_alter_request_log_steps_add7_alter1.js +22 -0
- package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +8 -2
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +6 -3
- package/bundle/dist/utils/providers/anthropic/claude-session.js +11 -7
- package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +117 -7
- package/bundle/dist/utils/providers/common/model-cost.js +45 -98
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +68 -0
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +14 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +81 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +16 -9
- package/bundle/src/application/qgrid/qgrid.frame.ts +19 -0
- package/bundle/src/application/qgrid/qgrid.types.ts +36 -0
- package/bundle/src/application/qgrid/tool-emulation.test.ts +1 -0
- package/bundle/src/application/request-log/request-log.entity.json +42 -0
- package/bundle/src/application/request-log/request-log.model.test.ts +58 -0
- package/bundle/src/application/request-log/request-log.model.ts +107 -18
- package/bundle/src/application/request-log-step/request-log-step.entity.json +54 -0
- package/bundle/src/application/request-log-step/request-log-step.types.ts +7 -0
- package/bundle/src/application/sonamu.generated.http +26 -0
- package/bundle/src/application/sonamu.generated.sso.ts +24 -0
- package/bundle/src/application/sonamu.generated.ts +48 -0
- package/bundle/src/i18n/sd.generated.ts +12 -0
- package/bundle/src/migrations/20260718180824_alter_request_logs_add5_alter1.ts +25 -0
- package/bundle/src/migrations/20260718180825_alter_request_log_steps_add7_alter1.ts +29 -0
- package/bundle/src/utils/providers/anthropic/anthropic-constants.test.ts +15 -0
- package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +11 -2
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +7 -2
- package/bundle/src/utils/providers/anthropic/claude-session.test.ts +26 -0
- package/bundle/src/utils/providers/anthropic/claude-session.ts +16 -4
- package/bundle/src/utils/providers/anthropic/stream-json-adapter.test.ts +131 -0
- package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +194 -11
- package/bundle/src/utils/providers/common/model-cost.test.ts +136 -2
- package/bundle/src/utils/providers/common/model-cost.ts +76 -99
- package/bundle/src/utils/providers/common/provider-dispatcher.ts +18 -0
- package/bundle/web-dist/client/assets/{index-BOXhNo0k.js → index-CmuPjbCl.js} +3 -3
- package/bundle/web-dist/client/assets/{logs-BShH_sut.js → logs-cXOsDli3.js} +1 -1
- package/bundle/web-dist/client/assets/{monit-CoEAp5S0.js → monit-B5Edhvy-.js} +1 -1
- package/bundle/web-dist/client/assets/{routes-Dr3cGnV3.js → routes-UPWScORY.js} +1 -1
- package/bundle/web-dist/client/assets/{sd.generated-D2Y9QeiN.js → sd.generated-DYuKzBY6.js} +1 -1
- package/bundle/web-dist/client/assets/{services.generated-CmxXl220.js → services.generated-D_SkWOo-.js} +1 -1
- package/bundle/web-dist/client/assets/show-pRp-eZIK.js +30 -0
- package/bundle/web-dist/client/assets/{tokens-hP1NecIg.js → tokens-DJlttxCM.js} +1 -1
- package/bundle/web-dist/client/assets/{use-update-token-mutation-6J1T9U2-.js → use-update-token-mutation-CpFGLWUl.js} +1 -1
- package/bundle/web-dist/client/index.html +2 -2
- package/bundle/web-dist/server/assets/{lazyRouteComponent-BlfOh9S3.js → lazyRouteComponent-Cu6IkL8A.js} +1 -1
- package/bundle/web-dist/server/assets/{logs-DYly4ipQ.js → logs-Dx2knN2T.js} +10 -7
- package/bundle/web-dist/server/assets/{monit-CYyY2S2f.js → monit-DU6PLatf.js} +2 -2
- package/bundle/web-dist/server/assets/{routes-Dqu3osXt.js → routes-CTco1oJZ.js} +3 -3
- package/bundle/web-dist/server/assets/{sd.generated-CE6D5clm.js → sd.generated-qzuRe35X.js} +12 -0
- package/bundle/web-dist/server/assets/{services.generated-DyZUzXiq.js → services.generated-C8q1M41a.js} +1 -1
- package/bundle/web-dist/server/assets/{show-BDul8xLb.js → show-B3q8_GDh.js} +47 -4
- package/bundle/web-dist/server/assets/{tokens-fzMW4G74.js → tokens-Bvjn15h9.js} +3 -3
- package/bundle/web-dist/server/assets/{use-update-token-mutation-BejUW5sw.js → use-update-token-mutation-BlZo6swF.js} +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +8 -8
- package/package.json +2 -2
- package/skills/qgrid/SKILL.md +4 -0
- package/skills/qgrid/references/ai-sdk-provider-contract.md +30 -2
- package/skills/qgrid/references/anthropic-claude-code-runtime.md +43 -5
- package/skills/qgrid/references/decision-rationale.md +2 -0
- package/skills/qgrid/references/prompt-cache-and-usage.md +4 -1
- package/skills/qgrid/references/request-log-run-lifecycle.md +6 -2
- package/skills/qgrid/references/sonamu-api-web-flow.md +13 -0
- package/bundle/web-dist/client/assets/show-CXII6Iy7.js +0 -30
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/sonamu.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Qgrid</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CmuPjbCl.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Dw2cE7zH.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-ca-nBW7U.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-DH4kUfK2.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-DJRQAbrY.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/sd.generated-
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/sd.generated-DYuKzBY6.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/index-yZcjDoV0.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { J as __commonJSMin, Z as __toESM, _ as require_jsx_runtime, m as require_shim, p as require_react_dom, v as require_react } from "./sd.generated-
|
|
1
|
+
import { J as __commonJSMin, Z as __toESM, _ as require_jsx_runtime, m as require_shim, p as require_react_dom, v as require_react } from "./sd.generated-qzuRe35X.js";
|
|
2
2
|
//#region ../../node_modules/.pnpm/@tanstack+router-core@1.143.6/node_modules/@tanstack/router-core/dist/esm/utils.js
|
|
3
3
|
function last(arr) {
|
|
4
4
|
return arr[arr.length - 1];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { _ as require_jsx_runtime, d as string, l as number, u as object, v as require_react } from "./sd.generated-
|
|
2
|
-
import { a as TokenService, n as QgridService, r as RequestLogService } from "./services.generated-
|
|
3
|
-
import { a as Link, o as useNavigate } from "./lazyRouteComponent-
|
|
1
|
+
import { _ as require_jsx_runtime, d as string, l as number, u as object, v as require_react } from "./sd.generated-qzuRe35X.js";
|
|
2
|
+
import { a as TokenService, n as QgridService, r as RequestLogService } from "./services.generated-C8q1M41a.js";
|
|
3
|
+
import { a as Link, o as useNavigate } from "./lazyRouteComponent-Cu6IkL8A.js";
|
|
4
4
|
import { n as Route } from "../entry-server.generated.js";
|
|
5
5
|
import { n as formatMicroUsd, r as formatUsd, t as cacheHitRate } from "./cost-CSH_t17I.js";
|
|
6
6
|
require_react();
|
|
@@ -271,12 +271,15 @@ function RequestLogTable({ search, onSearchChange }) {
|
|
|
271
271
|
children: row.token_name
|
|
272
272
|
})
|
|
273
273
|
}),
|
|
274
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
274
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", {
|
|
275
275
|
className: "px-4 py-1.5 whitespace-nowrap",
|
|
276
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
276
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
277
277
|
className: "text-xs text-sand-500",
|
|
278
|
-
children: row.model_name ?? "—"
|
|
279
|
-
})
|
|
278
|
+
children: row.requested_model_name && row.model_name && row.requested_model_name !== row.model_name ? `${row.requested_model_name} → ${row.model_name}` : row.model_name ?? "—"
|
|
279
|
+
}), (row.fallback_count ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
280
|
+
className: "ml-1 text-[9px] text-caution-500",
|
|
281
|
+
children: ["×", row.fallback_count]
|
|
282
|
+
})]
|
|
280
283
|
}),
|
|
281
284
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
|
|
282
285
|
className: "px-4 py-1.5 text-left tabular-nums text-sand-500 whitespace-nowrap",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Z as __toESM, _ as require_jsx_runtime, c as _enum, p as require_react_dom, u as object, v as require_react } from "./sd.generated-
|
|
2
|
-
import { s as useQuery, t as MonitService } from "./services.generated-
|
|
1
|
+
import { Z as __toESM, _ as require_jsx_runtime, c as _enum, p as require_react_dom, u as object, v as require_react } from "./sd.generated-qzuRe35X.js";
|
|
2
|
+
import { s as useQuery, t as MonitService } from "./services.generated-C8q1M41a.js";
|
|
3
3
|
import { a as clsx, r as Route } from "../entry-server.generated.js";
|
|
4
4
|
//#region ../../node_modules/.pnpm/@tanstack+virtual-core@3.17.4/node_modules/@tanstack/virtual-core/dist/esm/lazy-measurements.js
|
|
5
5
|
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Z as __toESM, _ as require_jsx_runtime, g as useQueryClient, p as require_react_dom, v as require_react } from "./sd.generated-
|
|
2
|
-
import { a as TokenService, n as QgridService } from "./services.generated-
|
|
1
|
+
import { Z as __toESM, _ as require_jsx_runtime, g as useQueryClient, p as require_react_dom, v as require_react } from "./sd.generated-qzuRe35X.js";
|
|
2
|
+
import { a as TokenService, n as QgridService } from "./services.generated-C8q1M41a.js";
|
|
3
3
|
import { i as Q } from "../entry-server.generated.js";
|
|
4
4
|
import { r as formatUsd } from "./cost-CSH_t17I.js";
|
|
5
|
-
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-
|
|
5
|
+
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-BlZo6swF.js";
|
|
6
6
|
//#region ../../node_modules/.pnpm/@dnd-kit+utilities@3.2.2_react@19.2.4/node_modules/@dnd-kit/utilities/dist/utilities.esm.js
|
|
7
7
|
var import_react_dom = /* @__PURE__ */ __toESM(require_react_dom());
|
|
8
8
|
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
@@ -22539,6 +22539,8 @@ var entityLabels = {
|
|
|
22539
22539
|
"entity.RequestLog.token_name": "토큰 이름",
|
|
22540
22540
|
"entity.RequestLog.project_name": "프로젝트 이름",
|
|
22541
22541
|
"entity.RequestLog.model_name": "모델 이름",
|
|
22542
|
+
"entity.RequestLog.requested_model_name": "요청 모델 이름 (fallback 전)",
|
|
22543
|
+
"entity.RequestLog.fallback_count": "모델 fallback 횟수",
|
|
22542
22544
|
"entity.RequestLog.user_prompt": "사용자 프롬프트",
|
|
22543
22545
|
"entity.RequestLog.system_prompt": "시스템 프롬프트",
|
|
22544
22546
|
"entity.RequestLog.response": "llm 응답 전문",
|
|
@@ -22546,9 +22548,12 @@ var entityLabels = {
|
|
|
22546
22548
|
"entity.RequestLog.output_tokens": "출력 토큰",
|
|
22547
22549
|
"entity.RequestLog.cache_read_tokens": "캐시 읽기 토큰",
|
|
22548
22550
|
"entity.RequestLog.cache_creation_tokens": "캐시 생성 토큰",
|
|
22551
|
+
"entity.RequestLog.cache_creation_5m_tokens": "5분 TTL 캐시 생성 토큰",
|
|
22552
|
+
"entity.RequestLog.cache_creation_1h_tokens": "1시간 TTL 캐시 생성 토큰",
|
|
22549
22553
|
"entity.RequestLog.duration_ms": "응답 소요시간(ms)",
|
|
22550
22554
|
"entity.RequestLog.ttft_ms": "first gen TTFT(ms)",
|
|
22551
22555
|
"entity.RequestLog.cost_usd": "API 비용",
|
|
22556
|
+
"entity.RequestLog.cost_source": "비용 출처(provider/pricing_table/mixed)",
|
|
22552
22557
|
"entity.RequestLog.image_cost_usd": "이미지 생성 추정 비용",
|
|
22553
22558
|
"entity.RequestLog.image_cost_method": "이미지 비용 추정 방식",
|
|
22554
22559
|
"entity.RequestLog.effort": "effort level",
|
|
@@ -22571,10 +22576,17 @@ var entityLabels = {
|
|
|
22571
22576
|
"entity.RequestLogStep.request_log": "부모 request log ID",
|
|
22572
22577
|
"entity.RequestLogStep.step_index": "ai-sdk step number (0-based)",
|
|
22573
22578
|
"entity.RequestLogStep.type": "step 타입",
|
|
22579
|
+
"entity.RequestLogStep.model_name": "실제 serving 모델",
|
|
22580
|
+
"entity.RequestLogStep.requested_model_name": "요청 모델 (fallback 전)",
|
|
22581
|
+
"entity.RequestLogStep.fallback_count": "모델 fallback 횟수",
|
|
22574
22582
|
"entity.RequestLogStep.input_tokens": "입력 토큰 (generate)",
|
|
22575
22583
|
"entity.RequestLogStep.output_tokens": "출력 토큰 (generate)",
|
|
22576
22584
|
"entity.RequestLogStep.cache_read_tokens": "캐시 읽기 토큰",
|
|
22577
22585
|
"entity.RequestLogStep.cache_creation_tokens": "캐시 생성 토큰",
|
|
22586
|
+
"entity.RequestLogStep.cache_creation_5m_tokens": "5분 TTL 캐시 생성 토큰",
|
|
22587
|
+
"entity.RequestLogStep.cache_creation_1h_tokens": "1시간 TTL 캐시 생성 토큰",
|
|
22588
|
+
"entity.RequestLogStep.cost_usd": "step 비용 (micro-USD)",
|
|
22589
|
+
"entity.RequestLogStep.cost_source": "비용 출처(provider/pricing_table)",
|
|
22578
22590
|
"entity.RequestLogStep.duration_ms": "소요시간(ms)",
|
|
22579
22591
|
"entity.RequestLogStep.ttft_ms": "Gen TTFT(ms)",
|
|
22580
22592
|
"entity.RequestLogStep.finish_reason": "완료 사유",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as resolveStaleTime, C as infiniteQueryBehavior, D as notifyManager, G as timeoutManager, H as shouldThrowError, I as noop, K as focusManager, N as isValidTimeout, O as pendingThenable, R as replaceData, S as hasPreviousPage, T as fetchState, V as shallowEqualObjects, W as timeUntilStale, Z as __toESM, _ as require_jsx_runtime, a as fetch, b as getDefaultState, f as require_lib, g as useQueryClient, i as dedupeAndFlatten, j as hashKey, k as environmentManager, o as useRefreshable, q as Subscribable, s as useSSEStream, v as require_react, x as hasNextPage, z as resolveEnabled } from "./sd.generated-
|
|
1
|
+
import { B as resolveStaleTime, C as infiniteQueryBehavior, D as notifyManager, G as timeoutManager, H as shouldThrowError, I as noop, K as focusManager, N as isValidTimeout, O as pendingThenable, R as replaceData, S as hasPreviousPage, T as fetchState, V as shallowEqualObjects, W as timeUntilStale, Z as __toESM, _ as require_jsx_runtime, a as fetch, b as getDefaultState, f as require_lib, g as useQueryClient, i as dedupeAndFlatten, j as hashKey, k as environmentManager, o as useRefreshable, q as Subscribable, s as useSSEStream, v as require_react, x as hasNextPage, z as resolveEnabled } from "./sd.generated-qzuRe35X.js";
|
|
2
2
|
//#region ../../node_modules/.pnpm/@tanstack+query-core@5.95.2/node_modules/@tanstack/query-core/build/modern/queryObserver.js
|
|
3
3
|
var QueryObserver = class extends Subscribable {
|
|
4
4
|
constructor(client, options) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { J as __commonJSMin, Y as __exportAll, Z as __toESM, _ as require_jsx_runtime, v as require_react } from "./sd.generated-
|
|
2
|
-
import { i as RequestLogStepService, r as RequestLogService } from "./services.generated-
|
|
3
|
-
import { a as Link } from "./lazyRouteComponent-
|
|
1
|
+
import { J as __commonJSMin, Y as __exportAll, Z as __toESM, _ as require_jsx_runtime, v as require_react } from "./sd.generated-qzuRe35X.js";
|
|
2
|
+
import { i as RequestLogStepService, r as RequestLogService } from "./services.generated-C8q1M41a.js";
|
|
3
|
+
import { a as Link } from "./lazyRouteComponent-Cu6IkL8A.js";
|
|
4
4
|
import { t as Route } from "../entry-server.generated.js";
|
|
5
5
|
import { n as formatMicroUsd, t as cacheHitRate } from "./cost-CSH_t17I.js";
|
|
6
6
|
import minpath from "node:path";
|
|
@@ -25308,7 +25308,11 @@ function HeaderBar({ data }) {
|
|
|
25308
25308
|
children: [
|
|
25309
25309
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
25310
25310
|
className: "text-[15px] font-semibold text-sand-900",
|
|
25311
|
-
children: data.model_name ?? "Unknown model"
|
|
25311
|
+
children: data.requested_model_name !== null && data.model_name !== null && data.requested_model_name !== data.model_name ? `${data.requested_model_name} → ${data.model_name}` : data.model_name ?? "Unknown model"
|
|
25312
|
+
}),
|
|
25313
|
+
(data.fallback_count ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
25314
|
+
className: "text-[10px] px-1.5 py-0.5 rounded-full bg-caution-400/15 text-caution-500 font-medium uppercase",
|
|
25315
|
+
children: ["fallback ×", data.fallback_count]
|
|
25312
25316
|
}),
|
|
25313
25317
|
status && status !== "succeeded" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
25314
25318
|
className: `text-[10px] px-1.5 py-0.5 rounded-full font-medium uppercase ${STATUS_STYLE[status] ?? "bg-sand-100 text-sand-500"}`,
|
|
@@ -25352,6 +25356,10 @@ function MetricsPanel({ data, toolCallCount }) {
|
|
|
25352
25356
|
label: "Driver Cost",
|
|
25353
25357
|
value: data.cost_usd !== null ? formatMicroUsd(data.cost_usd) : "—"
|
|
25354
25358
|
}),
|
|
25359
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
25360
|
+
label: "Cost Source",
|
|
25361
|
+
value: data.cost_source ?? "legacy"
|
|
25362
|
+
}),
|
|
25355
25363
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
25356
25364
|
label: "Image Cost",
|
|
25357
25365
|
value: data.image_cost_usd !== null ? formatMicroUsd(data.image_cost_usd) : "—"
|
|
@@ -25380,6 +25388,14 @@ function MetricsPanel({ data, toolCallCount }) {
|
|
|
25380
25388
|
label: "Cache Write",
|
|
25381
25389
|
value: formatNum(data.cache_creation_tokens)
|
|
25382
25390
|
}),
|
|
25391
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
25392
|
+
label: "Cache Write (5m)",
|
|
25393
|
+
value: data.cache_creation_5m_tokens === null ? "—" : formatNum(data.cache_creation_5m_tokens)
|
|
25394
|
+
}),
|
|
25395
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
25396
|
+
label: "Cache Write (1h)",
|
|
25397
|
+
value: data.cache_creation_1h_tokens === null ? "—" : formatNum(data.cache_creation_1h_tokens)
|
|
25398
|
+
}),
|
|
25383
25399
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
|
|
25384
25400
|
label: "Cache Hit",
|
|
25385
25401
|
value: cacheHitRate(data)
|
|
@@ -25614,6 +25630,14 @@ function StepTreeItem({ entry, tokensPerSecEnabled }) {
|
|
|
25614
25630
|
className: "text-[10px] px-1.5 py-0.5 rounded bg-sand-100 text-sand-500 font-mono shrink-0",
|
|
25615
25631
|
children: generate.finishReason
|
|
25616
25632
|
}),
|
|
25633
|
+
generate && (generate.fallbackCount ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
25634
|
+
className: "text-[10px] px-1.5 py-0.5 rounded-full bg-caution-400/15 text-caution-500 font-medium uppercase shrink-0",
|
|
25635
|
+
children: [
|
|
25636
|
+
generate.requestedModelName,
|
|
25637
|
+
" → ",
|
|
25638
|
+
generate.modelName
|
|
25639
|
+
]
|
|
25640
|
+
}),
|
|
25617
25641
|
hasReasoning && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
25618
25642
|
className: "text-[10px] px-1.5 py-0.5 rounded bg-blue-100 text-blue-600 font-medium shrink-0",
|
|
25619
25643
|
children: "reasoning"
|
|
@@ -25662,6 +25686,18 @@ function StepTreeItem({ entry, tokensPerSecEnabled }) {
|
|
|
25662
25686
|
label: "Cache Write",
|
|
25663
25687
|
value: formatNum(generate.cacheCreationTokens ?? 0)
|
|
25664
25688
|
}),
|
|
25689
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
|
|
25690
|
+
label: "Cache 5m / 1h",
|
|
25691
|
+
value: `${generate.cacheCreation5mTokens === null ? "—" : formatNum(generate.cacheCreation5mTokens)} / ${generate.cacheCreation1hTokens === null ? "—" : formatNum(generate.cacheCreation1hTokens)}`
|
|
25692
|
+
}),
|
|
25693
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
|
|
25694
|
+
label: "Cost",
|
|
25695
|
+
value: generate.costUsd === null ? "—" : formatMicroUsd(generate.costUsd)
|
|
25696
|
+
}),
|
|
25697
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
|
|
25698
|
+
label: "Cost Source",
|
|
25699
|
+
value: generate.costSource ?? "legacy"
|
|
25700
|
+
}),
|
|
25665
25701
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
|
|
25666
25702
|
label: "Reasoning",
|
|
25667
25703
|
value: generate.reasoningTokens !== null && generate.reasoningTokens !== void 0 ? formatNum(generate.reasoningTokens) : "—"
|
|
@@ -25708,10 +25744,17 @@ function buildStepTree(steps) {
|
|
|
25708
25744
|
group.generate = {
|
|
25709
25745
|
id: step.id,
|
|
25710
25746
|
stepIndex: step.step_index,
|
|
25747
|
+
modelName: step.model_name,
|
|
25748
|
+
requestedModelName: step.requested_model_name,
|
|
25749
|
+
fallbackCount: step.fallback_count,
|
|
25711
25750
|
inputTokens: step.input_tokens,
|
|
25712
25751
|
outputTokens: step.output_tokens,
|
|
25713
25752
|
cacheReadTokens: step.cache_read_tokens,
|
|
25714
25753
|
cacheCreationTokens: step.cache_creation_tokens,
|
|
25754
|
+
cacheCreation5mTokens: step.cache_creation_5m_tokens,
|
|
25755
|
+
cacheCreation1hTokens: step.cache_creation_1h_tokens,
|
|
25756
|
+
costUsd: step.cost_usd,
|
|
25757
|
+
costSource: step.cost_source,
|
|
25715
25758
|
durationMs: step.duration_ms,
|
|
25716
25759
|
ttftMs: step.ttft_ms,
|
|
25717
25760
|
finishReason: step.finish_reason,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Z as __toESM, _ as require_jsx_runtime, g as useQueryClient, v as require_react } from "./sd.generated-
|
|
2
|
-
import { a as TokenService, n as QgridService } from "./services.generated-
|
|
1
|
+
import { Z as __toESM, _ as require_jsx_runtime, g as useQueryClient, v as require_react } from "./sd.generated-qzuRe35X.js";
|
|
2
|
+
import { a as TokenService, n as QgridService } from "./services.generated-C8q1M41a.js";
|
|
3
3
|
import { i as Q, o as lucideKeyRound } from "../entry-server.generated.js";
|
|
4
|
-
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-
|
|
4
|
+
import { t as useUpdateTokenMutation } from "./use-update-token-mutation-BlZo6swF.js";
|
|
5
5
|
//#region ~icons/lucide/plus.jsx
|
|
6
6
|
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
7
7
|
var import_jsx_runtime = require_jsx_runtime();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as QgridService, o as useMutation } from "./services.generated-
|
|
1
|
+
import { n as QgridService, o as useMutation } from "./services.generated-C8q1M41a.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 }) => QgridService.updateToken(id, name, quotaThreshold, weight) });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as functionalUpdate, B as resolveStaleTime, C as infiniteQueryBehavior, D as notifyManager, E as onlineManager, F as matchQuery, I as noop$1, J as __commonJSMin, K as focusManager, L as partialMatchKey, M as hashQueryKeyByOptions, P as matchMutation, U as skipToken, X as __require, Z as __toESM, _ as require_jsx_runtime, c as _enum, d as string, h as QueryClientProvider, j as hashKey, l as number, m as require_shim, n as SUPPORTED_LOCALES, p as require_react_dom, q as Subscribable, r as setLocale, t as SD, u as object, v as require_react, w as Query, y as Mutation } from "./assets/sd.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-
|
|
1
|
+
import { A as functionalUpdate, B as resolveStaleTime, C as infiniteQueryBehavior, D as notifyManager, E as onlineManager, F as matchQuery, I as noop$1, J as __commonJSMin, K as focusManager, L as partialMatchKey, M as hashQueryKeyByOptions, P as matchMutation, U as skipToken, X as __require, Z as __toESM, _ as require_jsx_runtime, c as _enum, d as string, h as QueryClientProvider, j as hashKey, l as number, m as require_shim, n as SUPPORTED_LOCALES, p as require_react_dom, q as Subscribable, r as setLocale, t as SD, u as object, v as require_react, w as Query, y as Mutation } from "./assets/sd.generated-qzuRe35X.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-Cu6IkL8A.js";
|
|
3
3
|
//#region ../../node_modules/.pnpm/@tanstack+query-core@5.95.2/node_modules/@tanstack/query-core/build/modern/hydration.js
|
|
4
4
|
function defaultTransformerFn(data) {
|
|
5
5
|
return data;
|
|
@@ -22703,7 +22703,7 @@ var sections = [{
|
|
|
22703
22703
|
}
|
|
22704
22704
|
]
|
|
22705
22705
|
}];
|
|
22706
|
-
var qgridVersion = "2.4.
|
|
22706
|
+
var qgridVersion = "2.4.8";
|
|
22707
22707
|
function Sidebar({ className }) {
|
|
22708
22708
|
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
22709
22709
|
const isActive = (path) => {
|
|
@@ -44584,11 +44584,11 @@ function RootComponent() {
|
|
|
44584
44584
|
}
|
|
44585
44585
|
//#endregion
|
|
44586
44586
|
//#region src/routes/tokens.tsx
|
|
44587
|
-
var $$splitComponentImporter$4 = () => import("./assets/tokens-
|
|
44587
|
+
var $$splitComponentImporter$4 = () => import("./assets/tokens-Bvjn15h9.js");
|
|
44588
44588
|
var Route$4 = createFileRoute("/tokens")({ component: lazyRouteComponent($$splitComponentImporter$4, "component") });
|
|
44589
44589
|
//#endregion
|
|
44590
44590
|
//#region src/routes/monit.tsx
|
|
44591
|
-
var $$splitComponentImporter$3 = () => import("./assets/monit-
|
|
44591
|
+
var $$splitComponentImporter$3 = () => import("./assets/monit-DU6PLatf.js");
|
|
44592
44592
|
var monitSearchSchema = object({ level: _enum([
|
|
44593
44593
|
"all",
|
|
44594
44594
|
"scaling",
|
|
@@ -44601,7 +44601,7 @@ var Route$3 = createFileRoute("/monit")({
|
|
|
44601
44601
|
});
|
|
44602
44602
|
//#endregion
|
|
44603
44603
|
//#region src/routes/logs.tsx
|
|
44604
|
-
var $$splitComponentImporter$2 = () => import("./assets/logs-
|
|
44604
|
+
var $$splitComponentImporter$2 = () => import("./assets/logs-Dx2knN2T.js");
|
|
44605
44605
|
var logsSearchSchema = object({
|
|
44606
44606
|
token: string().optional(),
|
|
44607
44607
|
project: string().optional(),
|
|
@@ -44613,11 +44613,11 @@ var Route$2 = createFileRoute("/logs")({
|
|
|
44613
44613
|
});
|
|
44614
44614
|
//#endregion
|
|
44615
44615
|
//#region src/routes/index.tsx
|
|
44616
|
-
var $$splitComponentImporter$1 = () => import("./assets/routes-
|
|
44616
|
+
var $$splitComponentImporter$1 = () => import("./assets/routes-CTco1oJZ.js");
|
|
44617
44617
|
var Route$1 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter$1, "component") });
|
|
44618
44618
|
//#endregion
|
|
44619
44619
|
//#region src/routes/requests/show.tsx
|
|
44620
|
-
var $$splitComponentImporter = () => import("./assets/show-
|
|
44620
|
+
var $$splitComponentImporter = () => import("./assets/show-B3q8_GDh.js");
|
|
44621
44621
|
var showSearchSchema = object({ id: number() });
|
|
44622
44622
|
var Route = createFileRoute("/requests/show")({
|
|
44623
44623
|
validateSearch: showSearchSchema,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cartanova/qgrid-cli",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.8",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/cartanova-ai/qgrid"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"knex": "^3.1.0",
|
|
41
41
|
"pg": "^8.16.3",
|
|
42
42
|
"radashi": "^12.2.0",
|
|
43
|
-
"sonamu": "
|
|
43
|
+
"sonamu": "0.9.9",
|
|
44
44
|
"typescript": "5.9.3",
|
|
45
45
|
"zod": "^4.3.6"
|
|
46
46
|
},
|
package/skills/qgrid/SKILL.md
CHANGED
|
@@ -32,12 +32,16 @@ When helping a user set up local `@cartanova/qgrid-ai-sdk`, `createQgridLogger`,
|
|
|
32
32
|
|
|
33
33
|
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.
|
|
34
34
|
|
|
35
|
+
When writing AI SDK examples or setup code, import the public `QgridProviderOptions` type and apply `satisfies QgridProviderOptions` to the value 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. `QgridProviderOptions` is the inner qgrid namespace type, not the outer record type.
|
|
36
|
+
|
|
35
37
|
## Non-Negotiable Boundaries
|
|
36
38
|
|
|
37
39
|
- Use `packages/ai-sdk` as the active public SDK surface.
|
|
38
40
|
- Treat `packages/sdk` as deprecated. Read it only for legacy context or migration clues. Do not add new examples or features on top of it unless explicitly asked for legacy work.
|
|
39
41
|
- Route provider models by prefix: `openai/*` goes to the OpenAI Codex runtime, `anthropic/*` goes to the Anthropic Claude Code runtime. Prefix-less model fallback is not implemented.
|
|
42
|
+
- Treat Fable 5 safety fallback as upstream Claude Code behavior, not qgrid routing: a classifier refusal can retry on Opus 4.8. Do not add a second qgrid retry. Preserve requested Fable versus actual serving Opus, fallback history, and provider-reported cost; a fresh Claude Code process means the fallback is not sticky across qgrid requests.
|
|
40
43
|
- Treat dashboard work as Sonamu API/model/generated-client/web work, not isolated frontend work.
|
|
44
|
+
- Never hand-author migration files for Sonamu-managed schema. Change the entity definition, inspect `sonamu migrate status`, and create migration files with `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.
|
|
41
45
|
- 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.
|
|
42
46
|
- Treat OpenAI image generation as an opt-in Codex `image_generation` tool path. Inspect current code before modifying it; it is not a direct Images API call and its image cost is an estimate.
|
|
43
47
|
|
|
@@ -36,6 +36,25 @@ Setup agents should treat a missing project label as an actionable configuration
|
|
|
36
36
|
|
|
37
37
|
qgrid-specific behavior is controlled through `providerOptions.qgrid`. Explain these options precisely because they are qgrid's extension point beyond normal AI SDK usage.
|
|
38
38
|
|
|
39
|
+
AI SDK declares the outer `providerOptions` as a generic JSON record, so it does not infer qgrid-specific keys or literal values. qgrid exports `QgridProviderOptions` for the inner `providerOptions.qgrid` value. Import it and put the `satisfies` check at that inner boundary:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { generateText } from "ai";
|
|
43
|
+
import { qgrid, type QgridProviderOptions } from "@cartanova/qgrid-ai-sdk";
|
|
44
|
+
|
|
45
|
+
const result = await generateText({
|
|
46
|
+
model: qgrid("anthropic/claude-fable-5"),
|
|
47
|
+
prompt,
|
|
48
|
+
providerOptions: {
|
|
49
|
+
qgrid: {
|
|
50
|
+
effort: "high",
|
|
51
|
+
} satisfies QgridProviderOptions,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
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
|
+
|
|
39
58
|
`providerOptions.qgrid` supports:
|
|
40
59
|
|
|
41
60
|
- `sessionKey`: OpenAI thread reuse key. Disabled for Anthropic models.
|
|
@@ -43,7 +62,7 @@ qgrid-specific behavior is controlled through `providerOptions.qgrid`. Explain t
|
|
|
43
62
|
- `verbosity`: OpenAI/Codex route only.
|
|
44
63
|
- `reasoningSummary`: OpenAI/Codex route only.
|
|
45
64
|
- `serviceTier`: OpenAI/Codex route only.
|
|
46
|
-
- `fallbackModels`: reserved for future fallback routing.
|
|
65
|
+
- `fallbackModels`: reserved for future qgrid server-side fallback routing. It is not the Fable 5 safety-refusal fallback, which is owned by Claude Code upstream.
|
|
47
66
|
- `imageGeneration`: OpenAI/Codex non-stream only. Enables Codex's built-in `image_generation` tool for that request.
|
|
48
67
|
- `imageGenerationOptions`: optional image quality/size hints and cost-estimation basis. Current supported values are `quality: "low" | "medium" | "high"` and `size: "1024x1024" | "1024x1536" | "1536x1024"`.
|
|
49
68
|
|
|
@@ -60,7 +79,7 @@ const result = await generateText({
|
|
|
60
79
|
providerOptions: {
|
|
61
80
|
qgrid: {
|
|
62
81
|
serviceTier: "fast",
|
|
63
|
-
},
|
|
82
|
+
} satisfies QgridProviderOptions,
|
|
64
83
|
},
|
|
65
84
|
});
|
|
66
85
|
```
|
|
@@ -112,6 +131,15 @@ qgrid response content maps to AI SDK content:
|
|
|
112
131
|
|
|
113
132
|
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.
|
|
114
133
|
|
|
134
|
+
For a successful Fable 5 refusal fallback:
|
|
135
|
+
|
|
136
|
+
- `response.modelId` and `providerMetadata.qgrid.model` are the actual serving model, normally `claude-opus-4-8`.
|
|
137
|
+
- `providerMetadata.qgrid.requestedModel` remains `claude-fable-5`.
|
|
138
|
+
- `providerMetadata.qgrid.modelFallbacks` preserves the refusal route and optional category/explanation.
|
|
139
|
+
- `providerMetadata.qgrid.costSource` reports whether cost came from Claude Code or qgrid's pricing table. Prefer the provider-reported combined cost for this path.
|
|
140
|
+
|
|
141
|
+
Do not expose the requested Fable model as `response.modelId` after Opus served the answer, and do not map this upstream safety behavior onto the reserved `providerOptions.qgrid.fallbackModels` option.
|
|
142
|
+
|
|
115
143
|
Usage maps qgrid standard usage into AI SDK V3 usage:
|
|
116
144
|
|
|
117
145
|
- `inputTokens.total = input_tokens`
|
|
@@ -12,6 +12,7 @@ Use this reference before changing Anthropic provider behavior, Claude Code spaw
|
|
|
12
12
|
- Config isolation
|
|
13
13
|
- Input adaptation
|
|
14
14
|
- Output adaptation
|
|
15
|
+
- Fable refusal fallback
|
|
15
16
|
- Structured output
|
|
16
17
|
- 1M context
|
|
17
18
|
|
|
@@ -70,7 +71,7 @@ claude -p
|
|
|
70
71
|
--model <canonical-model-or-1m-suffix>
|
|
71
72
|
--system-prompt <text> # small system prompt
|
|
72
73
|
--system-prompt-file <path> # large system prompt
|
|
73
|
-
--thinking disabled
|
|
74
|
+
--thinking disabled # omitted for Fable 5
|
|
74
75
|
--effort <effort-or-low>
|
|
75
76
|
--disable-slash-commands
|
|
76
77
|
--session-id <uuid>
|
|
@@ -83,7 +84,7 @@ Important details:
|
|
|
83
84
|
- `--setting-sources project` plus seeded settings isolates user configuration.
|
|
84
85
|
- `--system-prompt` or `--system-prompt-file` is always supplied. Omitting it would allow Claude Code default system prompt injection.
|
|
85
86
|
- Large system prompts over 64 KiB are written to a temporary file to avoid argv `E2BIG`.
|
|
86
|
-
- `--thinking disabled`, `MAX_THINKING_TOKENS=0`, and adaptive thinking env suppression keep thinking off.
|
|
87
|
+
- `--thinking disabled`, `MAX_THINKING_TOKENS=0`, and adaptive thinking env suppression keep thinking off for existing models. Fable 5 requires always-on adaptive thinking, so qgrid omits all three suppressors for it.
|
|
87
88
|
|
|
88
89
|
## Spawn env
|
|
89
90
|
|
|
@@ -96,13 +97,13 @@ Included env:
|
|
|
96
97
|
- `CLAUDE_CODE_OAUTH_TOKEN`
|
|
97
98
|
- `CLAUDE_CONFIG_DIR`
|
|
98
99
|
- `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1`
|
|
99
|
-
- `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1`
|
|
100
|
+
- `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` except for Fable 5
|
|
100
101
|
- `CLAUDE_CODE_DISABLE_CLAUDE_MDS=1`
|
|
101
102
|
- `CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1`
|
|
102
103
|
- `CLAUDE_CODE_DISABLE_BUNDLED_SKILLS=1`
|
|
103
104
|
- `CLAUDE_CODE_DISABLE_WORKFLOWS=1`
|
|
104
105
|
- `CLAUDE_CODE_ATTRIBUTION_HEADER=0`
|
|
105
|
-
- `MAX_THINKING_TOKENS=0`
|
|
106
|
+
- `MAX_THINKING_TOKENS=0` except for Fable 5
|
|
106
107
|
- `CLAUDE_CODE_DISABLE_1M_CONTEXT=1` when model does not support qgrid's 1M path
|
|
107
108
|
- `MAX_STRUCTURED_OUTPUT_RETRIES` for structured output only
|
|
108
109
|
|
|
@@ -144,8 +145,43 @@ Anthropic native usage categories are mutually exclusive. qgrid normalizes them
|
|
|
144
145
|
inputTokens = input_tokens + cache_creation_input_tokens + cache_read_input_tokens
|
|
145
146
|
cachedInputTokens = cache_read_input_tokens
|
|
146
147
|
cacheCreationInputTokens = cache_creation_input_tokens
|
|
148
|
+
cacheCreationInputTokens5m = cache_creation.ephemeral_5m_input_tokens
|
|
149
|
+
cacheCreationInputTokens1h = cache_creation.ephemeral_1h_input_tokens
|
|
147
150
|
```
|
|
148
151
|
|
|
152
|
+
## Fable refusal fallback
|
|
153
|
+
|
|
154
|
+
Fable 5 runs upstream safety classifiers. A refusal is a successful HTTP response with
|
|
155
|
+
`stop_reason: "refusal"`, not an HTTP/provider error, and can happen before output or after
|
|
156
|
+
partial streamed output. Claude Code handles the refusal fallback and can retry on Opus 4.8.
|
|
157
|
+
This is distinct from the CLI `--fallback-model` flag, which is for overload on the default
|
|
158
|
+
model. qgrid does not configure either mechanism or add another retry.
|
|
159
|
+
|
|
160
|
+
The stream adapter must preserve all observable routing evidence:
|
|
161
|
+
|
|
162
|
+
- `system.subtype = "model_refusal_fallback"` provides the original/fallback models and refusal details.
|
|
163
|
+
- `assistant.message.model` identifies the model that produced that message.
|
|
164
|
+
- `usage.iterations` contains the original `message` and `fallback_message` when available.
|
|
165
|
+
- Treat `stop_reason`, rather than `stop_details` or empty content, as the authoritative refusal signal. Category and explanation are optional metadata.
|
|
166
|
+
- `model_refusal_no_fallback` or a terminal `stop_reason: "refusal"` is an error, even if the outer result subtype says `success`. A successfully served fallback has a non-refusal terminal reason and a `fallback_message` iteration.
|
|
167
|
+
|
|
168
|
+
`QueryOutput.model` is the actual serving model. `requestedModel` and `modelFallbacks` preserve
|
|
169
|
+
the caller's requested model and routing history. Because qgrid uses a fresh Claude Code process
|
|
170
|
+
for each request, any Claude Code session-sticky fallback state does not carry to the next request;
|
|
171
|
+
each new Fable request may independently refuse and fall back again.
|
|
172
|
+
|
|
173
|
+
Cost and usage must follow the upstream combined result. Prefer Claude Code's `total_cost_usd` and
|
|
174
|
+
preserve `usage.iterations` only as attribution evidence; do not independently price every iteration
|
|
175
|
+
and add them together. An upstream pre-output refusal is not billed, while work completed before a
|
|
176
|
+
mid-stream refusal can be billed. Upstream fallback handling applies fallback credit where applicable,
|
|
177
|
+
so a qgrid-side retry would risk duplicate work and incorrect cache-write billing.
|
|
178
|
+
|
|
179
|
+
Upstream references:
|
|
180
|
+
|
|
181
|
+
- [Refusals and fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback)
|
|
182
|
+
- [Fallback credit](https://platform.claude.com/docs/en/build-with-claude/fallback-credit)
|
|
183
|
+
- [Claude Code CLI `--fallback-model`](https://code.claude.com/docs/en/cli-reference)
|
|
184
|
+
|
|
149
185
|
## Structured output
|
|
150
186
|
|
|
151
187
|
qgrid strictifies schemas before provider dispatch. Claude Code `--json-schema` is not grammar-constrained decoding; it is a `StructuredOutput` tool plus post-validation behavior. Keep schemas strict and preserve error signaling for failed structured-output attempts.
|
|
@@ -156,8 +192,10 @@ qgrid strictifies schemas before provider dispatch. Claude Code `--json-schema`
|
|
|
156
192
|
|
|
157
193
|
Model normalization strips provider prefix and `[1m]` for canonical model/cost keys.
|
|
158
194
|
|
|
159
|
-
qgrid's
|
|
195
|
+
qgrid's exact 1M support set currently includes:
|
|
160
196
|
|
|
197
|
+
- `claude-fable-5`
|
|
198
|
+
- `claude-sonnet-5`
|
|
161
199
|
- `claude-sonnet-4-6`
|
|
162
200
|
- `claude-opus-4-6`
|
|
163
201
|
- `claude-opus-4-8`
|
|
@@ -29,8 +29,10 @@ Key decisions:
|
|
|
29
29
|
- `packages/ai-sdk` is the active public SDK. `packages/sdk` is deprecated and should be read only for legacy context.
|
|
30
30
|
- qgrid's AI SDK provider follows the AI SDK `LanguageModelV3` contract. Generic usage belongs to AI SDK docs; qgrid-specific docs should focus on qgrid routing, logging, cache/session behavior, and `providerOptions.qgrid`.
|
|
31
31
|
- qgrid-specific options live under `providerOptions.qgrid`, not under provider-specific namespaces such as `providerOptions.openai`. Consumers should not need to branch on Codex versus Claude internals.
|
|
32
|
+
- AI SDK exposes the outer `providerOptions` as a generic JSON record. `QgridProviderOptions` intentionally types the nested `providerOptions.qgrid` value, and examples should apply `satisfies` there to restore qgrid-specific inference without replacing AI SDK's outer contract.
|
|
32
33
|
- `projectName` is provider config/payload camelCase. `project_name` is the Sonamu/database request-log column. Prefer `QGRID_PROJECT_NAME` as the project-wide default so request logs and metrics remain filterable at volume.
|
|
33
34
|
- `fallbackModels` may exist as a typed future option, but it is not a server wire contract until the server implements fallback routing. Do not forward it as behavior just because the type exists.
|
|
35
|
+
- Fable 5 refusal fallback is an upstream Claude Code safety path to Opus 4.8, not qgrid's reserved `fallbackModels` feature or Claude Code's overload-only `--fallback-model` flag. qgrid observes and reports the actual route and provider cost; it must not retry again.
|
|
34
36
|
- The SDK should stay a thin adapter. Server APIs own request-log lifecycle, provider dispatch, structured-output emulation, and provider runtime details.
|
|
35
37
|
|
|
36
38
|
## Tool Calling And Request-Log Lifecycle
|
|
@@ -97,7 +97,10 @@ Do not double-count cache subfields. This was a real dashboard bug: OpenAI cache
|
|
|
97
97
|
- OpenAI cost normally uses qgrid's price table.
|
|
98
98
|
- OpenAI image generation stores a separate `image_cost_usd` estimate. It assumes Codex's image tool is priced as `gpt-image-2` for the selected/default quality and size, because qgrid does not receive exact image tool usage from Codex.
|
|
99
99
|
- Anthropic Claude Code may return `total_cost_usd`; qgrid prefers provider cost when present and positive.
|
|
100
|
-
- Anthropic cache creation
|
|
100
|
+
- Anthropic responses can split cache creation into `ephemeral_5m_input_tokens` and `ephemeral_1h_input_tokens`; preserve that split and price each TTL separately.
|
|
101
|
+
- Claude Code automatically requests 1-hour cache TTL for subscription OAuth and 5-minute TTL for API-key or usage-credit paths. Only legacy responses without a TTL breakdown use the subscription path's 1-hour rate as qgrid's fallback.
|
|
102
|
+
- Persist per-step `cost_usd`, `cost_source`, and the 5m/1h split. New request logs use that exact stored cost; only legacy rows with no `cost_source` are repriced from the current table.
|
|
103
|
+
- When Fable refuses and Claude Code serves Opus, prefer Claude Code's positive `total_cost_usd`. Persist Fable as the requested model and Opus as the serving model instead of pricing the combined turn as Fable.
|
|
101
104
|
- OpenAI long-context surcharge applies to the whole request when input exceeds the model threshold, not just the excess tokens.
|
|
102
105
|
|
|
103
106
|
When changing models or prices, verify against current official pricing before editing the table.
|
|
@@ -47,14 +47,18 @@ Stored request log usage uses qgrid-standard semantics:
|
|
|
47
47
|
- `input_tokens`: total input, including cache read/write.
|
|
48
48
|
- `cache_read_tokens`: cached input read.
|
|
49
49
|
- `cache_creation_tokens`: prompt cache write input.
|
|
50
|
+
- `cache_creation_5m_tokens` / `cache_creation_1h_tokens`: nullable Anthropic TTL breakdown.
|
|
50
51
|
- `output_tokens`: output tokens.
|
|
51
52
|
- `cost_usd`: integer micro-USD in DB; displayed USD is `cost_usd / 1_000_000`.
|
|
53
|
+
- `cost_source`: `provider`, `pricing_table`, or `mixed`. New rows keep the cost calculated at request time.
|
|
54
|
+
- `requested_model_name` / `model_name`: requested and actual serving models; they differ on Fable refusal fallback.
|
|
55
|
+
- `fallback_count`: number of observed model fallbacks in the run/step.
|
|
52
56
|
- `image_cost_usd`: integer micro-USD estimate for Codex image generation output.
|
|
53
57
|
- `image_cost_method`: string such as `assumed:gpt-image-2:medium:1536x1024:png`.
|
|
54
58
|
|
|
55
59
|
OpenAI/Codex: use per-turn `.last` usage from `thread/tokenUsage/updated`.
|
|
56
60
|
|
|
57
|
-
Anthropic: normalize native mutually exclusive categories by summing input + cache creation + cache read into total input.
|
|
61
|
+
Anthropic: normalize 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.
|
|
58
62
|
|
|
59
63
|
For image requests, keep `cost_usd` as the Codex driver model token cost. Image output cost is separate because qgrid observes Codex's `image_generation` result, not the OpenAI Images API usage object. Treat `image_cost_usd` as a price-table estimate that may be inaccurate if Codex changes its underlying image accounting.
|
|
60
64
|
|
|
@@ -62,7 +66,7 @@ Reference images for image generation are not stored in `request_logs.user_promp
|
|
|
62
66
|
|
|
63
67
|
## Legacy normalization
|
|
64
68
|
|
|
65
|
-
`RequestLogModel` normalizes legacy Anthropic rows where stored `input_tokens` may not include cache read/write.
|
|
69
|
+
`RequestLogModel` normalizes legacy Anthropic rows where stored `input_tokens` may not include cache read/write. Rows with `cost_source = NULL` are legacy and can be repriced from the current table; rows with a source retain their exact stored cost across price/promotion changes.
|
|
66
70
|
|
|
67
71
|
## TTFT
|
|
68
72
|
|
|
@@ -21,6 +21,19 @@ qgrid dashboard work is usually a Sonamu API/model/generated-client/web change,
|
|
|
21
21
|
- Web components under `packages/web/src/components/qgrid` consume generated API shapes.
|
|
22
22
|
- The main dashboard sidebar version should stay synced to `packages/cli/package.json`. `packages/web/vite.config.ts` reads that package version and exposes it as `__QGRID_CLI_VERSION__`; `Sidebar.tsx` displays it.
|
|
23
23
|
|
|
24
|
+
## Migration workflow
|
|
25
|
+
|
|
26
|
+
Sonamu entity definitions are the source of truth for managed database schema.
|
|
27
|
+
|
|
28
|
+
1. Change the relevant `*.entity.json` files.
|
|
29
|
+
2. Run `pnpm --dir packages/api sonamu migrate status` and inspect `preparedCodes`.
|
|
30
|
+
3. Run `pnpm --dir packages/api sonamu migrate generate`.
|
|
31
|
+
4. Inspect the generated, table-scoped migration files without rewriting them by hand.
|
|
32
|
+
5. Apply them through Sonamu's migration target flow or qgrid's startup migration runner.
|
|
33
|
+
6. Re-run migration status and require `pending: []` and `preparedCodes: []`.
|
|
34
|
+
|
|
35
|
+
Do not hand-author a migration that Sonamu can derive from entity changes. If generation cannot represent the required operation, stop and make the exception explicit before changing migration code or database state. Never delete or edit a migration that has already shipped to another environment.
|
|
36
|
+
|
|
24
37
|
## Request log dashboard
|
|
25
38
|
|
|
26
39
|
Request log changes usually touch:
|