@cartanova/qgrid-cli 2.11.0 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/bundle/web-dist/client/assets/index-C96h3feE.js +32 -0
  2. package/bundle/web-dist/client/assets/logs-DBM6U0QD.js +1 -0
  3. package/bundle/web-dist/client/assets/monit-BBjXa7vB.js +1 -0
  4. package/bundle/web-dist/client/assets/plus-BsQdY7PS.js +1 -0
  5. package/bundle/web-dist/client/assets/refresh-cw-D9IUmT23.js +1 -0
  6. package/bundle/web-dist/client/assets/request-model-Bpp_rkK8.js +1 -0
  7. package/bundle/web-dist/client/assets/routes-Crz_PQzi.js +5 -0
  8. package/bundle/web-dist/client/assets/{services.generated-CefiDXEv.js → services.generated-B5lc3aIC.js} +1 -1
  9. package/bundle/web-dist/client/assets/settings-Cdwm3OBB.js +1 -0
  10. package/bundle/web-dist/client/assets/show-DBUhALT1.js +2 -0
  11. package/bundle/web-dist/client/assets/tokens-CVQAVJTo.js +1 -0
  12. package/bundle/web-dist/client/assets/use-oauth-login-flow-BfxYI4Oh.js +1 -0
  13. package/bundle/web-dist/client/assets/use-update-token-mutation-qKBC2sn7.js +1 -0
  14. package/bundle/web-dist/client/assets/vendor-react-DH4kUfK2.js +9 -0
  15. package/bundle/web-dist/client/assets/vendor-tanstack-DRPs09ai.js +1 -0
  16. package/bundle/web-dist/client/index.html +4 -4
  17. package/bundle/web-dist/server/assets/{lazyRouteComponent-D8iTIHyn.js → lazyRouteComponent-AKiHfZWp.js} +2 -2
  18. package/bundle/web-dist/server/assets/logs-B7QmGJq3.js +513 -0
  19. package/bundle/web-dist/server/assets/{monit-B2hqorPe.js → monit-DFt7x3bh.js} +117 -375
  20. package/bundle/web-dist/server/assets/plus-DaYQRpBt.js +19 -0
  21. package/bundle/web-dist/server/assets/{react-dom-BIYQzBqG.js → react-dom-Cv23mm1K.js} +1 -1
  22. package/bundle/web-dist/server/assets/refresh-cw-BpXNsAy-.js +23 -0
  23. package/bundle/web-dist/server/assets/{request-model-I6OdODSH.js → request-model-eHE6YbaF.js} +6 -15
  24. package/bundle/web-dist/server/assets/{routes-B95YlUv2.js → routes-_Y-PJfgO.js} +203 -679
  25. package/bundle/web-dist/server/assets/{services.generated-DQATJ6qf.js → services.generated-CMJ8AHbO.js} +3796 -4012
  26. package/bundle/web-dist/server/assets/settings-JwJKxn2e.js +461 -0
  27. package/bundle/web-dist/server/assets/{show-Bxaj3rhH.js → show-BEv6z1v_.js} +368 -1270
  28. package/bundle/web-dist/server/assets/tokens-CNT4mI-f.js +560 -0
  29. package/bundle/web-dist/server/assets/{use-oauth-login-flow-CKDHgc3U.js → use-oauth-login-flow-jkAkT3X6.js} +16 -50
  30. package/bundle/web-dist/server/assets/{use-update-token-mutation-BEbgOzaa.js → use-update-token-mutation-Sm6kh02m.js} +1 -1
  31. package/bundle/web-dist/server/entry-server.generated.js +1130 -2626
  32. package/package.json +1 -1
  33. package/bundle/web-dist/client/assets/index-BA7uQ-6c.js +0 -31
  34. package/bundle/web-dist/client/assets/logs-C47OJGEI.js +0 -1
  35. package/bundle/web-dist/client/assets/monit-BS9no0dU.js +0 -4
  36. package/bundle/web-dist/client/assets/plus-sdACTfH0.js +0 -1
  37. package/bundle/web-dist/client/assets/refresh-cw-CY8o_1cT.js +0 -1
  38. package/bundle/web-dist/client/assets/request-model-BRtFuZvj.js +0 -1
  39. package/bundle/web-dist/client/assets/routes-kqgkUxE8.js +0 -5
  40. package/bundle/web-dist/client/assets/settings-DNDvFGSw.js +0 -1
  41. package/bundle/web-dist/client/assets/show-u8Np69q3.js +0 -2
  42. package/bundle/web-dist/client/assets/tokens-QvP410NW.js +0 -1
  43. package/bundle/web-dist/client/assets/use-oauth-login-flow-Bzdh28Ws.js +0 -1
  44. package/bundle/web-dist/client/assets/use-update-token-mutation-CfJKaxmR.js +0 -1
  45. package/bundle/web-dist/client/assets/vendor-react-CUFFrVN9.js +0 -244
  46. package/bundle/web-dist/client/assets/vendor-tanstack-Bf_jag9G.js +0 -1
  47. package/bundle/web-dist/server/assets/logs-B3OTd9hy.js +0 -825
  48. package/bundle/web-dist/server/assets/plus-DwG7h9kq.js +0 -28
  49. package/bundle/web-dist/server/assets/refresh-cw-mMBkwOq0.js +0 -44
  50. package/bundle/web-dist/server/assets/settings-C3gbGk0m.js +0 -830
  51. package/bundle/web-dist/server/assets/tokens-CZjH3cB7.js +0 -1038
@@ -0,0 +1,513 @@
1
+ import { M as require_jsx_runtime, N as require_react, T as string, n as QgridService, o as TokenService, r as RequestLogService, w as object } from "./services.generated-CMJ8AHbO.js";
2
+ import { a as Link, o as useNavigate } from "./lazyRouteComponent-AKiHfZWp.js";
3
+ import { i as RequestLogOrderBy, n as Route, r as RequestLogBaseListParams } from "../entry-server.generated.js";
4
+ import { i as formatUsd, r as formatRequestCost, t as cacheHitRate } from "./cost-BC9yzyA9.js";
5
+ import { n as unpricedRequestTitle, t as unpricedRequestLabel } from "./total-cost-fTE9Xy82.js";
6
+ import { t as lucideRefreshCw } from "./refresh-cw-BpXNsAy-.js";
7
+ import { n as lucideZap, t as requestModelDisplay } from "./request-model-eHE6YbaF.js";
8
+ require_react();
9
+ var import_jsx_runtime = require_jsx_runtime();
10
+ var lucideChevronLeft = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
11
+ viewBox: "0 0 24 24",
12
+ width: "1.2em",
13
+ height: "1.2em",
14
+ ...props,
15
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
16
+ fill: "none",
17
+ stroke: "currentColor",
18
+ strokeLinecap: "round",
19
+ strokeLinejoin: "round",
20
+ strokeWidth: 2,
21
+ d: "m15 18l-6-6l6-6"
22
+ })
23
+ });
24
+ //#endregion
25
+ //#region ~icons/lucide/chevron-right.jsx
26
+ var lucideChevronRight = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
27
+ viewBox: "0 0 24 24",
28
+ width: "1.2em",
29
+ height: "1.2em",
30
+ ...props,
31
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
32
+ fill: "none",
33
+ stroke: "currentColor",
34
+ strokeLinecap: "round",
35
+ strokeLinejoin: "round",
36
+ strokeWidth: 2,
37
+ d: "m9 18l6-6l-6-6"
38
+ })
39
+ });
40
+ //#endregion
41
+ //#region src/components/qgrid/RequestLogTable.tsx
42
+ var PAGE_SIZE = 50;
43
+ var UNASSIGNED = "__unassigned__";
44
+ function formatDateTime(value) {
45
+ const d = new Date(value);
46
+ return `${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
47
+ }
48
+ function formatNum(n) {
49
+ return n.toLocaleString();
50
+ }
51
+ function formatDuration(ms) {
52
+ const total = Math.round(ms / 1e3);
53
+ const m = Math.floor(total / 60);
54
+ const s = total % 60;
55
+ return m > 0 ? `${m}m ${s}s` : `${s}s`;
56
+ }
57
+ function RequestModel({ status, requestedModel, servedModel, imageCostMethod, fallbackCount }) {
58
+ if (status === "running") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
59
+ className: "text-[9px] px-1.5 py-0.5 rounded-full bg-blue-100 text-blue-600 font-medium whitespace-nowrap",
60
+ children: "실행 중"
61
+ });
62
+ const modelDisplay = requestModelDisplay({
63
+ status,
64
+ requestedModel,
65
+ servedModel,
66
+ imageCostMethod
67
+ });
68
+ const hasRequestedOnly = requestedModel !== null && servedModel === null;
69
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
71
+ className: "text-xs text-sand-500",
72
+ children: modelDisplay.label
73
+ }),
74
+ hasRequestedOnly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
75
+ className: "ml-1 text-[9px] text-sand-400",
76
+ children: "요청"
77
+ }),
78
+ (fallbackCount ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
79
+ className: "ml-1 text-[9px] text-caution-500",
80
+ children: ["×", fallbackCount]
81
+ })
82
+ ] });
83
+ }
84
+ function formatTtft(ms) {
85
+ if (ms <= 0) return "—";
86
+ return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${ms}ms`;
87
+ }
88
+ var COLUMNS = [
89
+ {
90
+ label: "ID",
91
+ align: "left",
92
+ width: "w-12",
93
+ sortKey: "id"
94
+ },
95
+ {
96
+ label: "Date",
97
+ align: "left",
98
+ width: "w-20",
99
+ sortKey: "created_at"
100
+ },
101
+ {
102
+ label: "Project",
103
+ align: "left",
104
+ width: "w-20"
105
+ },
106
+ {
107
+ label: "Token",
108
+ align: "left",
109
+ width: "w-24"
110
+ },
111
+ {
112
+ label: "Model",
113
+ align: "left",
114
+ width: "w-20"
115
+ },
116
+ {
117
+ label: "Fmt",
118
+ align: "left",
119
+ width: "w-12"
120
+ },
121
+ {
122
+ label: "TTFT",
123
+ align: "right",
124
+ width: "w-16",
125
+ sortKey: "ttft_ms"
126
+ },
127
+ {
128
+ label: "Duration",
129
+ align: "right",
130
+ width: "w-20",
131
+ sortKey: "duration_ms"
132
+ },
133
+ {
134
+ label: "In",
135
+ align: "right",
136
+ width: "w-16",
137
+ sortKey: "input_tokens"
138
+ },
139
+ {
140
+ label: "Out",
141
+ align: "right",
142
+ width: "w-20",
143
+ sortKey: "output_tokens"
144
+ },
145
+ {
146
+ label: "C.Read",
147
+ align: "right",
148
+ width: "w-20",
149
+ sortKey: "cache_read_tokens"
150
+ },
151
+ {
152
+ label: "C.Write",
153
+ align: "right",
154
+ width: "w-20",
155
+ sortKey: "cache_creation_tokens"
156
+ },
157
+ {
158
+ label: "Hit",
159
+ align: "right",
160
+ width: "w-14"
161
+ },
162
+ {
163
+ label: "Tools",
164
+ align: "right",
165
+ width: "w-14"
166
+ },
167
+ {
168
+ label: "$",
169
+ align: "right",
170
+ width: "w-20",
171
+ sortKey: "cost_usd"
172
+ }
173
+ ];
174
+ function RequestLogTable({ search, onSearchChange }) {
175
+ const navigate = useNavigate();
176
+ const page = search.page ?? 1;
177
+ const tokenFilter = search.token ?? "";
178
+ const projectFilter = search.project ?? "";
179
+ const modelFilter = search.model ?? "";
180
+ const { data: tokensData } = TokenService.useTokens("A");
181
+ const tokenNames = (tokensData?.rows ?? []).map((t) => t.name).filter(Boolean);
182
+ const { data: projectData } = QgridService.useProjectNames();
183
+ const projectNames = projectData?.names ?? [];
184
+ const { data: modelData } = QgridService.useModelNames();
185
+ const modelNames = modelData?.names ?? [];
186
+ const projectFilterParam = (() => {
187
+ if (projectFilter === UNASSIGNED) return { project_name_is_null: true };
188
+ if (projectFilter) return { project_name: projectFilter };
189
+ return {};
190
+ })();
191
+ const listFilters = {};
192
+ if (tokenFilter) listFilters.token_name = tokenFilter;
193
+ Object.assign(listFilters, projectFilterParam);
194
+ if (modelFilter) listFilters.model_name = modelFilter;
195
+ const sort = search.sort ?? "id-desc";
196
+ const separator = sort.lastIndexOf("-");
197
+ const sortColumn = sort.slice(0, separator);
198
+ const sortDirection = sort.slice(separator + 1) === "asc" ? "asc" : "desc";
199
+ const { data, isLoading, isError, refresh, isRefreshing } = RequestLogService.useRequestLogs("P", {
200
+ num: PAGE_SIZE,
201
+ page,
202
+ orderBy: sort,
203
+ ...listFilters
204
+ });
205
+ const { data: costData, isError: isCostError, refresh: refreshCost } = QgridService.useTotalCost({
206
+ num: 0,
207
+ page: 1,
208
+ ...listFilters
209
+ });
210
+ const rows = data?.rows ?? [];
211
+ const total = data?.total ?? 0;
212
+ const totalPages = Math.ceil(total / PAGE_SIZE);
213
+ const updateFilter = (patch) => {
214
+ onSearchChange({
215
+ ...search,
216
+ ...patch,
217
+ page: 1
218
+ });
219
+ };
220
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
221
+ className: "panel overflow-hidden",
222
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
223
+ className: "panel-header flex items-center gap-2 px-3 py-2.5 flex-wrap sm:gap-3 sm:px-5",
224
+ children: [
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
226
+ value: tokenFilter,
227
+ onChange: (e) => updateFilter({ token: e.target.value || void 0 }),
228
+ disabled: tokenNames.length === 0,
229
+ className: "border border-sand-200/80 rounded-lg px-2.5 py-1.5 text-[11px] text-sand-700 bg-sand-50/50 focus:outline-none focus:border-sienna-300 disabled:text-sand-300 disabled:cursor-not-allowed",
230
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
231
+ value: "",
232
+ children: tokenNames.length > 0 ? "All Tokens" : "Tokens —"
233
+ }), tokenNames.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
234
+ value: name,
235
+ children: name
236
+ }, name))]
237
+ }),
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
239
+ value: projectFilter,
240
+ onChange: (e) => updateFilter({ project: e.target.value || void 0 }),
241
+ className: "border border-sand-200/80 rounded-lg px-2.5 py-1.5 text-[11px] text-sand-700 bg-sand-50/50 focus:outline-none focus:border-sienna-300",
242
+ children: [
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
244
+ value: "",
245
+ children: "All Projects"
246
+ }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
248
+ value: UNASSIGNED,
249
+ children: "(unassigned)"
250
+ }),
251
+ projectNames.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
252
+ value: name,
253
+ children: name
254
+ }, name))
255
+ ]
256
+ }),
257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
258
+ value: modelFilter,
259
+ onChange: (e) => updateFilter({ model: e.target.value || void 0 }),
260
+ disabled: modelNames.length === 0,
261
+ className: "border border-sand-200/80 rounded-lg px-2.5 py-1.5 text-[11px] text-sand-700 bg-sand-50/50 focus:outline-none focus:border-sienna-300 disabled:text-sand-300 disabled:cursor-not-allowed",
262
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
263
+ value: "",
264
+ children: modelNames.length > 0 ? "All Models" : "Models —"
265
+ }), modelNames.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
266
+ value: name,
267
+ children: name
268
+ }, name))]
269
+ }),
270
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
271
+ type: "button",
272
+ onClick: () => void Promise.all([refresh(), refreshCost()]),
273
+ disabled: isRefreshing,
274
+ "aria-label": "Reload",
275
+ title: "Reload",
276
+ className: "p-1 rounded text-sand-400 hover:text-sand-600 disabled:opacity-30 transition-colors",
277
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideRefreshCw, { className: isRefreshing ? "size-3.5 animate-spin" : "size-3.5" })
278
+ }),
279
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1" }),
280
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
281
+ className: "text-[11px] text-sand-400",
282
+ children: [isError || !data ? "—" : total, " results"]
283
+ }),
284
+ isCostError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
285
+ role: "alert",
286
+ className: "text-[11px] text-danger-500",
287
+ children: "Failed to load total cost."
288
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
289
+ className: "text-[11px] tabular-nums font-medium text-sienna-600",
290
+ children: [costData ? formatUsd(costData.usd) : "—", costData && unpricedRequestLabel(costData) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
291
+ className: "ml-1 font-normal text-sand-400",
292
+ title: unpricedRequestTitle(costData) ?? void 0,
293
+ children: unpricedRequestLabel(costData)
294
+ }) : null]
295
+ })
296
+ ]
297
+ }), isError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
298
+ role: "alert",
299
+ className: "text-danger-500 text-center py-12 text-sm",
300
+ children: "Failed to load request logs."
301
+ }) : isLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
302
+ className: "space-y-2",
303
+ children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-8 bg-sand-100 rounded animate-pulse" }, `skel-${i}`))
304
+ }) : rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
305
+ className: "text-sand-400 text-center py-12 text-sm",
306
+ children: "No requests yet."
307
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
308
+ className: "overflow-x-auto px-3 py-3 sm:px-5",
309
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", {
310
+ className: "w-full min-w-[900px] text-[13px]",
311
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", {
312
+ className: "border-b border-sand-100/60",
313
+ children: [COLUMNS.map((col) => {
314
+ const padX = col.label === "Duration" ? "pl-5 pr-3" : col.width ? "px-4" : "px-3";
315
+ const active = col.sortKey === sortColumn;
316
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
317
+ className: `${col.align === "right" ? "text-right" : "text-left"} ${col.width ?? ""} ${col.width ? "whitespace-nowrap" : ""} ${padX} py-1.5 text-[10px] uppercase font-medium ${active ? "text-sienna-600" : "text-sand-400"}`,
318
+ children: col.sortKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
319
+ type: "button",
320
+ onClick: () => updateFilter({ sort: RequestLogOrderBy.parse(`${col.sortKey}-${active && sortDirection === "desc" ? "asc" : "desc"}`) }),
321
+ className: `inline-flex items-center gap-0.5 text-[10px] uppercase font-medium hover:text-sienna-500 transition-colors duration-150 ${col.align === "right" ? "justify-end w-full" : ""}`,
322
+ children: [col.label, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
323
+ className: active ? "" : "text-sand-300",
324
+ children: active ? sortDirection === "desc" ? "↓" : "↑" : "↕"
325
+ })]
326
+ }) : col.label
327
+ }, col.label);
328
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: "w-8" })]
329
+ }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", {
330
+ className: "divide-y divide-sand-100/60",
331
+ children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", {
332
+ className: "transition-colors duration-150 hover:bg-sand-100/60 cursor-pointer",
333
+ onClick: (e) => {
334
+ if (e.metaKey || e.ctrlKey) {
335
+ window.open(`/requests/show?id=${row.id}`, "_blank");
336
+ return;
337
+ }
338
+ navigate({
339
+ to: "/requests/show",
340
+ search: { id: row.id }
341
+ });
342
+ },
343
+ children: [
344
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
345
+ className: "px-4 py-1.5 whitespace-nowrap",
346
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, {
347
+ to: "/requests/show",
348
+ search: { id: row.id },
349
+ onClick: (e) => e.stopPropagation(),
350
+ className: "text-xs text-sand-500 tabular-nums hover:text-sienna-500",
351
+ children: row.id
352
+ })
353
+ }),
354
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
355
+ className: "px-4 py-1.5 whitespace-nowrap",
356
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
357
+ className: "text-xs text-sand-400 tabular-nums",
358
+ children: formatDateTime(row.created_at)
359
+ })
360
+ }),
361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
362
+ className: "px-4 py-1.5 whitespace-nowrap",
363
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
364
+ className: "text-xs text-sand-600",
365
+ children: row.project_name ?? "—"
366
+ })
367
+ }),
368
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
369
+ className: "px-4 py-1.5 whitespace-nowrap",
370
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
371
+ className: "text-xs text-sand-500",
372
+ children: row.token_name
373
+ })
374
+ }),
375
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", {
376
+ className: "px-4 py-1.5 whitespace-nowrap",
377
+ children: [
378
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RequestModel, {
379
+ status: row.status,
380
+ requestedModel: row.requested_model_name,
381
+ servedModel: row.model_name,
382
+ imageCostMethod: row.image_cost_method,
383
+ fallbackCount: row.fallback_count
384
+ }),
385
+ row.service_tier === "fast" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
386
+ title: "Fast 모드",
387
+ className: "ml-1 inline-flex align-middle",
388
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideZap, {
389
+ "aria-label": "Fast 모드",
390
+ className: "size-3 text-[#1877F2] [&_path]:fill-current"
391
+ })
392
+ }),
393
+ row.response_json_ok === false && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
394
+ title: "structured 요청의 응답이 JSON 파싱에 실패했습니다",
395
+ className: "ml-1.5 rounded-full bg-caution-400/15 px-1.5 py-0.5 text-[9px] font-semibold text-caution-500",
396
+ children: "broken"
397
+ })
398
+ ]
399
+ }),
400
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
401
+ className: "px-4 py-1.5 whitespace-nowrap",
402
+ children: row.is_image_generation ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
403
+ className: "rounded bg-sage-100 px-1.5 py-0.5 font-mono text-[10px] text-sage-700",
404
+ children: "img"
405
+ }) : row.is_structured ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
406
+ className: "rounded bg-sienna-100 px-1.5 py-0.5 font-mono text-[10px] text-sienna-700",
407
+ children: "json"
408
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
409
+ className: "font-mono text-[10px] text-sand-400",
410
+ children: "text"
411
+ })
412
+ }),
413
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
414
+ className: "px-4 py-1.5 text-right tabular-nums text-sand-500 whitespace-nowrap",
415
+ children: formatTtft(row.ttft_ms)
416
+ }),
417
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
418
+ className: "pl-5 pr-3 py-1.5 text-right tabular-nums text-sand-500 whitespace-nowrap",
419
+ children: formatDuration(row.duration_ms)
420
+ }),
421
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
422
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
423
+ children: formatNum(row.input_tokens)
424
+ }),
425
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
426
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
427
+ children: formatNum(row.output_tokens)
428
+ }),
429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
430
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
431
+ children: formatNum(row.cache_read_tokens)
432
+ }),
433
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
434
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
435
+ children: formatNum(row.cache_creation_tokens)
436
+ }),
437
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
438
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700",
439
+ children: cacheHitRate(row)
440
+ }),
441
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
442
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-500",
443
+ children: row.tool_call_count > 0 ? row.tool_call_count : "—"
444
+ }),
445
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
446
+ className: "px-3 py-1.5 text-right tabular-nums text-sand-700 whitespace-nowrap",
447
+ children: formatRequestCost(row)
448
+ }),
449
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
450
+ className: "px-2 py-1.5",
451
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideChevronRight, { className: "size-4 text-sand-400" })
452
+ })
453
+ ]
454
+ }, row.id))
455
+ })]
456
+ })
457
+ }), totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
458
+ className: "flex items-center justify-center gap-3 px-5 py-2.5 border-t border-sand-100/60",
459
+ children: [
460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
461
+ type: "button",
462
+ className: "p-1 rounded text-sand-400 hover:text-sand-600 disabled:opacity-30 transition-colors",
463
+ disabled: page === 1,
464
+ onClick: () => onSearchChange({
465
+ ...search,
466
+ page: page - 1
467
+ }),
468
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideChevronLeft, { className: "size-4" })
469
+ }),
470
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
471
+ className: "text-[11px] text-sand-400 tabular-nums",
472
+ children: [
473
+ page,
474
+ " / ",
475
+ totalPages
476
+ ]
477
+ }),
478
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
479
+ type: "button",
480
+ className: "p-1 rounded text-sand-400 hover:text-sand-600 disabled:opacity-30 transition-colors",
481
+ disabled: page === totalPages,
482
+ onClick: () => onSearchChange({
483
+ ...search,
484
+ page: page + 1
485
+ }),
486
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(lucideChevronRight, { className: "size-4" })
487
+ })
488
+ ]
489
+ })] })]
490
+ });
491
+ }
492
+ //#endregion
493
+ //#region src/routes/logs.tsx?tsr-split=component
494
+ object({
495
+ token: string().optional(),
496
+ project: string().optional(),
497
+ model: string().optional(),
498
+ sort: RequestLogOrderBy.optional(),
499
+ page: RequestLogBaseListParams.shape.page.default(1)
500
+ });
501
+ function LogsPage() {
502
+ const search = Route.useSearch();
503
+ const navigate = Route.useNavigate();
504
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
505
+ className: "max-w-[100rem] mx-auto",
506
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RequestLogTable, {
507
+ search,
508
+ onSearchChange: (next) => navigate({ search: next })
509
+ })
510
+ });
511
+ }
512
+ //#endregion
513
+ export { LogsPage as component };