@cartanova/qgrid-cli 2.5.10 → 2.6.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.
- package/README.ko.md +0 -1
- package/README.md +0 -1
- package/bundle/dist/application/qgrid/oauth.js +3 -2
- package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +2 -1
- package/bundle/dist/application/qgrid/qgrid.frame.js +90 -24
- package/bundle/dist/application/qgrid/qgrid.types.js +5 -2
- package/bundle/dist/application/request-log/request-log.model.js +3 -2
- package/bundle/dist/application/request-log/request-log.types.js +4 -2
- package/bundle/dist/application/sonamu.generated.js +8 -1
- package/bundle/dist/application/sonamu.generated.sso.js +2 -1
- package/bundle/dist/i18n/sd.generated.js +2 -1
- package/bundle/dist/migrations/20260806141701_alter_request_logs_add1_alter1.js +15 -0
- package/bundle/src/application/qgrid/oauth.ts +3 -0
- package/bundle/src/application/qgrid/qgrid-oauth-redirect.test.ts +151 -0
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +14 -0
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +3 -0
- package/bundle/src/application/qgrid/qgrid.frame.ts +110 -38
- package/bundle/src/application/qgrid/qgrid.types.ts +3 -0
- package/bundle/src/application/request-log/request-log.entity.json +8 -0
- package/bundle/src/application/request-log/request-log.model.test.ts +3 -1
- package/bundle/src/application/request-log/request-log.model.ts +7 -1
- package/bundle/src/application/request-log/request-log.types.ts +5 -0
- package/bundle/src/application/sonamu.generated.http +10 -2
- package/bundle/src/application/sonamu.generated.sso.ts +1 -0
- package/bundle/src/application/sonamu.generated.ts +13 -0
- package/bundle/src/i18n/sd.generated.ts +1 -0
- package/bundle/src/migrations/20260806141701_alter_request_logs_add1_alter1.ts +15 -0
- package/bundle/web-dist/client/assets/{index-SdI2T3b8.js → index-BGGBFmzE.js} +3 -3
- package/bundle/web-dist/client/assets/index-GA9b-zy4.css +1 -0
- package/bundle/web-dist/client/assets/{logs-O-8hGStp.js → logs-Dh19u6Tk.js} +1 -1
- package/bundle/web-dist/client/assets/{monit-DA8MAY-d.js → monit-CyqMmy1G.js} +1 -1
- package/bundle/web-dist/client/assets/{routes-COPjkgNr.js → routes-Cy0gCq8c.js} +1 -1
- package/bundle/web-dist/client/assets/{sd.generated-DbBC73_z.js → sd.generated-CjW3rfJ8.js} +1 -1
- package/bundle/web-dist/client/assets/{services.generated-BWyAypSh.js → services.generated-BuT6Gp8V.js} +1 -1
- package/bundle/web-dist/client/assets/show-C5WxfoAt.js +30 -0
- package/bundle/web-dist/client/assets/tokens-Ciolqw4l.js +1 -0
- package/bundle/web-dist/client/assets/{use-update-token-mutation-DeDBiZiM.js → use-update-token-mutation-DBkxL29Y.js} +1 -1
- package/bundle/web-dist/client/index.html +3 -3
- package/bundle/web-dist/server/assets/{lazyRouteComponent-BCJr868T.js → lazyRouteComponent-PRxXWW3t.js} +1 -1
- package/bundle/web-dist/server/assets/{logs-33YNafdW.js → logs-D0z1Ghj1.js} +3 -3
- package/bundle/web-dist/server/assets/{monit--9mVsjNU.js → monit-dBXxnAyI.js} +2 -2
- package/bundle/web-dist/server/assets/{routes-BK3Xh1UB.js → routes-B3p7fuwk.js} +3 -3
- package/bundle/web-dist/server/assets/{sd.generated-C3gcvRcU.js → sd.generated-ek-tz-7H.js} +1 -0
- package/bundle/web-dist/server/assets/{services.generated-B2_Auwvk.js → services.generated-BNuxWs7K.js} +10 -1
- package/bundle/web-dist/server/assets/{show-Dxxxe608.js → show-0iNioJ2I.js} +39 -13
- package/bundle/web-dist/server/assets/{tokens-BWPVvdfi.js → tokens-CvytQ9Xy.js} +86 -18
- package/bundle/web-dist/server/assets/{use-update-token-mutation-CNBgTysy.js → use-update-token-mutation-BBCC2GKM.js} +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +8 -8
- package/package.json +1 -1
- package/skills/qgrid/references/cli-env-and-server-boot.md +1 -2
- package/skills/qgrid/references/token-auth-quota-lifecycle.md +5 -1
- package/bundle/web-dist/client/assets/index-B-cZKEyf.css +0 -1
- package/bundle/web-dist/client/assets/show-ClG4e0BP.js +0 -30
- package/bundle/web-dist/client/assets/tokens-Cc5frn5f.js +0 -1
|
@@ -19,6 +19,7 @@ import { TokenCredentials } from "../token/token.types";
|
|
|
19
19
|
import {
|
|
20
20
|
type AnthropicUsageRaw,
|
|
21
21
|
buildAuthUrl,
|
|
22
|
+
CONSOLE_CALLBACK_URL,
|
|
22
23
|
exchangeCodeForTokens,
|
|
23
24
|
fetchUsage,
|
|
24
25
|
generatePKCE,
|
|
@@ -80,12 +81,50 @@ async function deleteOAuthState(state: string): Promise<void> {
|
|
|
80
81
|
await cache.delete({ key: `${OAUTH_STATE_PREFIX}${state}` });
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// OAuth 접속 주소는 브라우저가 보낸 Origin(우선) 또는 Host 헤더에서 파생한다.
|
|
85
|
+
// HTTP context 가 없는 직접 호출(테스트 등)은 null 을 반환한다.
|
|
86
|
+
function deriveRequestBaseUrl(): string | null {
|
|
87
|
+
try {
|
|
88
|
+
const { headers, request } = Sonamu.getContext();
|
|
89
|
+
const origin = headers.origin;
|
|
90
|
+
if (typeof origin === "string" && /^https?:\/\//.test(origin)) {
|
|
91
|
+
return origin.replace(/\/+$/, "");
|
|
92
|
+
}
|
|
93
|
+
const forwardedHost = headers["x-forwarded-host"];
|
|
94
|
+
const host = (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost) ?? headers.host;
|
|
95
|
+
if (host) {
|
|
96
|
+
const forwardedProto = headers["x-forwarded-proto"];
|
|
97
|
+
const proto =
|
|
98
|
+
(Array.isArray(forwardedProto) ? forwardedProto[0] : forwardedProto)?.split(",")[0] ??
|
|
99
|
+
request.protocol;
|
|
100
|
+
return `${proto}://${host}`;
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
// direct frame call — HTTP context 없음
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
86
107
|
|
|
87
|
-
|
|
88
|
-
|
|
108
|
+
const LOOPBACK_HOSTNAMES = new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
109
|
+
|
|
110
|
+
function isLoopbackBase(base: string): boolean {
|
|
111
|
+
try {
|
|
112
|
+
return LOOPBACK_HOSTNAMES.has(new URL(base).hostname);
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Anthropic OAuth client 는 redirect_uri 허용 목록을 강제한다: 루프백(localhost)과
|
|
119
|
+
// 콘솔 콜백만 허용된다. 따라서 루프백 접속은 자동 리다이렉트 플로우를,
|
|
120
|
+
// 원격 접속은 콘솔 콜백 + 코드 붙여넣기 플로우(Claude Code CLI 와 동일)를 쓴다.
|
|
121
|
+
function resolveOAuthRedirect(): { redirectUri: string; mode: "redirect" | "code" } {
|
|
122
|
+
const base = deriveRequestBaseUrl();
|
|
123
|
+
if (base && !isLoopbackBase(base)) {
|
|
124
|
+
return { redirectUri: CONSOLE_CALLBACK_URL, mode: "code" };
|
|
125
|
+
}
|
|
126
|
+
const loopbackBase = base ?? `http://localhost:${process.env.PORT ?? "44900"}`;
|
|
127
|
+
return { redirectUri: `${loopbackBase}/callback`, mode: "redirect" };
|
|
89
128
|
}
|
|
90
129
|
|
|
91
130
|
function rejectImageGenerationStream(args: QueryInput): void {
|
|
@@ -516,54 +555,87 @@ class QgridFrameClass extends BaseFrameClass {
|
|
|
516
555
|
async oauthStart(name: string): Promise<OAuthStartResult> {
|
|
517
556
|
const { codeVerifier, codeChallenge, state } = generatePKCE();
|
|
518
557
|
|
|
519
|
-
const redirectUri =
|
|
558
|
+
const { redirectUri, mode } = resolveOAuthRedirect();
|
|
520
559
|
const authUrl = buildAuthUrl(codeChallenge, state, redirectUri);
|
|
521
560
|
|
|
522
561
|
await setOAuthState(state, { codeVerifier, name, redirectUri });
|
|
523
562
|
|
|
524
|
-
return { authUrl };
|
|
563
|
+
return { authUrl, mode };
|
|
525
564
|
}
|
|
526
565
|
|
|
527
|
-
|
|
566
|
+
// 코드 붙여넣기 플로우(원격 접속): 콘솔 콜백이 표시한 `code#state` 를 받아 교환한다.
|
|
567
|
+
@api({ httpMethod: "POST", clients: ["axios", "tanstack-mutation"] })
|
|
568
|
+
async oauthComplete(pastedCode: string): Promise<{ added: boolean; name: string }> {
|
|
569
|
+
const trimmed = pastedCode.trim();
|
|
570
|
+
const separatorAt = trimmed.indexOf("#");
|
|
571
|
+
const code = separatorAt > 0 ? trimmed.slice(0, separatorAt) : "";
|
|
572
|
+
const state = separatorAt > 0 ? trimmed.slice(separatorAt + 1) : "";
|
|
573
|
+
if (!code || !state) {
|
|
574
|
+
throw new BadRequestException(
|
|
575
|
+
"expected the code shown after login, in code#state form" as LocalizedString,
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
528
579
|
const pending = await getOAuthState(state);
|
|
529
580
|
if (!pending) {
|
|
530
|
-
|
|
531
|
-
|
|
581
|
+
throw new BadRequestException(
|
|
582
|
+
"login session not found or expired — start the login again" as LocalizedString,
|
|
583
|
+
);
|
|
532
584
|
}
|
|
533
585
|
await deleteOAuthState(state);
|
|
534
586
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
pending.codeVerifier,
|
|
539
|
-
state,
|
|
540
|
-
pending.redirectUri,
|
|
541
|
-
);
|
|
587
|
+
await this.completeAnthropicLogin(code, state, pending);
|
|
588
|
+
return { added: true, name: pending.name };
|
|
589
|
+
}
|
|
542
590
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
591
|
+
// 교환 + 계정 중복 제거 + 저장 — redirect 콜백과 코드 붙여넣기 플로우가 공유한다.
|
|
592
|
+
private async completeAnthropicLogin(
|
|
593
|
+
code: string,
|
|
594
|
+
state: string,
|
|
595
|
+
pending: PendingOAuth,
|
|
596
|
+
): Promise<void> {
|
|
597
|
+
const tokens = await exchangeCodeForTokens(
|
|
598
|
+
code,
|
|
599
|
+
pending.codeVerifier,
|
|
600
|
+
state,
|
|
601
|
+
pending.redirectUri,
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
if (tokens.accountUuid) {
|
|
605
|
+
const oldEntries = await TokenModel.findByAccountIdentifier(
|
|
606
|
+
"A",
|
|
607
|
+
"anthropic",
|
|
608
|
+
tokens.accountUuid,
|
|
609
|
+
);
|
|
610
|
+
if (oldEntries.length > 0) {
|
|
611
|
+
await TokenModel.del(oldEntries.map((o) => o.id));
|
|
552
612
|
}
|
|
613
|
+
}
|
|
553
614
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
},
|
|
563
|
-
name: pending.name,
|
|
615
|
+
await TokenModel.save([
|
|
616
|
+
{
|
|
617
|
+
provider: "anthropic",
|
|
618
|
+
credentials: {
|
|
619
|
+
accessToken: tokens.accessToken,
|
|
620
|
+
refreshToken: tokens.refreshToken,
|
|
621
|
+
expiresAt: tokens.expiresAt ?? 0,
|
|
622
|
+
accountUuid: tokens.accountUuid ?? "",
|
|
564
623
|
},
|
|
565
|
-
|
|
624
|
+
name: pending.name,
|
|
625
|
+
},
|
|
626
|
+
]);
|
|
627
|
+
}
|
|
566
628
|
|
|
629
|
+
async handleOAuthCallback(code: string, state: string, reply: FastifyReply): Promise<void> {
|
|
630
|
+
const pending = await getOAuthState(state);
|
|
631
|
+
if (!pending) {
|
|
632
|
+
logger.warn("oauth callback: invalid_state");
|
|
633
|
+
return reply.redirect("/?oauth=error&reason=invalid_state");
|
|
634
|
+
}
|
|
635
|
+
await deleteOAuthState(state);
|
|
636
|
+
|
|
637
|
+
try {
|
|
638
|
+
await this.completeAnthropicLogin(code, state, pending);
|
|
567
639
|
return reply.redirect(`/?oauth=success&name=${encodeURIComponent(pending.name)}`);
|
|
568
640
|
} catch (e) {
|
|
569
641
|
return reply.redirect(`/?oauth=error&reason=${encodeURIComponent((e as Error).message)}`);
|
|
@@ -608,7 +680,7 @@ class QgridFrameClass extends BaseFrameClass {
|
|
|
608
680
|
logger.warn(`OpenAI browser login failed: ${(e as Error).message}`);
|
|
609
681
|
});
|
|
610
682
|
|
|
611
|
-
return { authUrl };
|
|
683
|
+
return { authUrl, mode: "redirect" };
|
|
612
684
|
}
|
|
613
685
|
|
|
614
686
|
@api({ httpMethod: "GET", clients: ["axios", "tanstack-query"] })
|
|
@@ -274,6 +274,9 @@ export type TokenStats = z.infer<typeof TokenStats>;
|
|
|
274
274
|
|
|
275
275
|
export const OAuthStartResult = z.object({
|
|
276
276
|
authUrl: z.string(),
|
|
277
|
+
// redirect: 브라우저 자동 복귀(루프백 접속). code: 콘솔 콜백 + 코드 붙여넣기(원격 접속).
|
|
278
|
+
// OpenAI 경로는 codex 가 콜백을 처리하므로 항상 redirect 를 반환한다.
|
|
279
|
+
mode: z.enum(["redirect", "code"]),
|
|
277
280
|
});
|
|
278
281
|
export type OAuthStartResult = z.infer<typeof OAuthStartResult>;
|
|
279
282
|
|
|
@@ -97,6 +97,13 @@
|
|
|
97
97
|
"nullable": true,
|
|
98
98
|
"desc": "inject_items 히스토리"
|
|
99
99
|
},
|
|
100
|
+
{
|
|
101
|
+
"name": "tools",
|
|
102
|
+
"type": "json",
|
|
103
|
+
"id": "ToolDefinitions",
|
|
104
|
+
"nullable": true,
|
|
105
|
+
"desc": "요청에 장착된 tool 정의 목록"
|
|
106
|
+
},
|
|
100
107
|
{
|
|
101
108
|
"name": "status",
|
|
102
109
|
"type": "enum",
|
|
@@ -153,6 +160,7 @@
|
|
|
153
160
|
"image_cost_method",
|
|
154
161
|
"effort",
|
|
155
162
|
"history",
|
|
163
|
+
"tools",
|
|
156
164
|
"status",
|
|
157
165
|
"error_message",
|
|
158
166
|
"tool_call_count",
|
|
@@ -113,7 +113,7 @@ describe("RequestLogModel run lifecycle", () => {
|
|
|
113
113
|
vi.restoreAllMocks();
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
it("stores the requested model but no serving model when a run starts", async () => {
|
|
116
|
+
it("stores the requested model and attached tools but no serving model when a run starts", async () => {
|
|
117
117
|
const ubRegister = vi.fn();
|
|
118
118
|
const transaction = vi.fn(
|
|
119
119
|
async (cb: (trx: { ubUpsert: () => Promise<number[]> }) => Promise<number>) =>
|
|
@@ -130,6 +130,7 @@ describe("RequestLogModel run lifecycle", () => {
|
|
|
130
130
|
RequestLogModel.createRun({
|
|
131
131
|
user_prompt: "hi",
|
|
132
132
|
requested_model_name: "openai/gpt-5-codex",
|
|
133
|
+
tools: [{ name: "getWeather", description: "Get weather", inputSchema: { type: "object" } }],
|
|
133
134
|
}),
|
|
134
135
|
).resolves.toBe(17);
|
|
135
136
|
|
|
@@ -139,6 +140,7 @@ describe("RequestLogModel run lifecycle", () => {
|
|
|
139
140
|
status: "running",
|
|
140
141
|
requested_model_name: "openai/gpt-5-codex",
|
|
141
142
|
model_name: null,
|
|
143
|
+
tools: [{ name: "getWeather", description: "Get weather", inputSchema: { type: "object" } }],
|
|
142
144
|
}),
|
|
143
145
|
);
|
|
144
146
|
});
|
|
@@ -14,7 +14,11 @@ import { SD } from "../../i18n/sd.generated";
|
|
|
14
14
|
import { calculateCostUsd } from "../../utils/providers/common/model-cost";
|
|
15
15
|
import { type RequestLogSubsetKey, type RequestLogSubsetMapping } from "../sonamu.generated";
|
|
16
16
|
import { requestLogLoaderQueries, requestLogSubsetQueries } from "../sonamu.generated.sso";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
type RequestLogListParams,
|
|
19
|
+
type RequestLogSaveParams,
|
|
20
|
+
type ToolDefinitions,
|
|
21
|
+
} from "./request-log.types";
|
|
18
22
|
|
|
19
23
|
// cost_usd는 정수 micro-USD로 저장. 실제 USD = cost_usd / MICRO_USD.
|
|
20
24
|
export const MICRO_USD = 1_000_000;
|
|
@@ -287,6 +291,7 @@ class RequestLogModelClass extends BaseModelClass<
|
|
|
287
291
|
effort?: string | null;
|
|
288
292
|
project_name?: string | null;
|
|
289
293
|
history?: unknown;
|
|
294
|
+
tools?: ToolDefinitions;
|
|
290
295
|
is_image_generation?: boolean;
|
|
291
296
|
}): Promise<number> {
|
|
292
297
|
const wdb = this.getPuri("w");
|
|
@@ -309,6 +314,7 @@ class RequestLogModelClass extends BaseModelClass<
|
|
|
309
314
|
// 이미지 turn 식별(R13). run 경로(tools+image 조합)도 auto 경로와 동일하게 마킹.
|
|
310
315
|
is_image_generation: params.is_image_generation ?? false,
|
|
311
316
|
...(params.history !== undefined ? { history: params.history as { type: string }[] } : {}),
|
|
317
|
+
...(params.tools !== undefined ? { tools: params.tools } : {}),
|
|
312
318
|
});
|
|
313
319
|
return wdb.transaction(async (trx) => {
|
|
314
320
|
const ids = await trx.ubUpsert("request_logs");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
import { QgridTool } from "../qgrid/qgrid.types";
|
|
3
4
|
import { RequestLogBaseListParams, RequestLogBaseSchema } from "../sonamu.generated";
|
|
4
5
|
|
|
5
6
|
export const HistoryItems = z.array(
|
|
@@ -15,6 +16,10 @@ export const HistoryItems = z.array(
|
|
|
15
16
|
);
|
|
16
17
|
export type HistoryItems = z.infer<typeof HistoryItems>;
|
|
17
18
|
|
|
19
|
+
// 요청에 장착된 tool 정의 목록 — 저장 형태는 QgridTool 계약을 그대로 따른다.
|
|
20
|
+
export const ToolDefinitions = z.array(QgridTool);
|
|
21
|
+
export type ToolDefinitions = z.infer<typeof ToolDefinitions>;
|
|
22
|
+
|
|
18
23
|
// RequestLog - ListParams
|
|
19
24
|
// project_name NULL 분기: IS NULL만 조회(unassigned 필터) 또는 IS NOT NULL만(distinct 조회).
|
|
20
25
|
export const RequestLogListParams = RequestLogBaseListParams.extend({
|
|
@@ -199,6 +199,7 @@ Content-Type: application/json
|
|
|
199
199
|
"image_cost_method": null,
|
|
200
200
|
"effort": null,
|
|
201
201
|
"history": null,
|
|
202
|
+
"tools": null,
|
|
202
203
|
"status": "running",
|
|
203
204
|
"error_message": null,
|
|
204
205
|
"tool_call_count": 0,
|
|
@@ -252,7 +253,6 @@ Content-Type: application/json
|
|
|
252
253
|
"serviceTier": "fast",
|
|
253
254
|
"history": "HISTORY",
|
|
254
255
|
"projectName": "PROJECTNAME",
|
|
255
|
-
"isStep": false,
|
|
256
256
|
"logger": false,
|
|
257
257
|
"runContext": {
|
|
258
258
|
"requestLogId": 0,
|
|
@@ -313,7 +313,6 @@ Content-Type: application/json
|
|
|
313
313
|
"serviceTier": "fast",
|
|
314
314
|
"history": "HISTORY",
|
|
315
315
|
"projectName": "PROJECTNAME",
|
|
316
|
-
"isStep": false,
|
|
317
316
|
"logger": false,
|
|
318
317
|
"runContext": {
|
|
319
318
|
"requestLogId": 0,
|
|
@@ -501,6 +500,15 @@ Content-Type: application/json
|
|
|
501
500
|
|
|
502
501
|
###
|
|
503
502
|
|
|
503
|
+
POST {{baseUrl}}/api/qgrid/oauthComplete
|
|
504
|
+
Content-Type: application/json
|
|
505
|
+
|
|
506
|
+
{
|
|
507
|
+
"pastedCode": "PASTEDCODE"
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
###
|
|
511
|
+
|
|
504
512
|
POST {{baseUrl}}/api/qgrid/oauthStartOpenAI
|
|
505
513
|
Content-Type: application/json
|
|
506
514
|
|
|
@@ -42,6 +42,7 @@ export const requestLogSubsetQueries = {
|
|
|
42
42
|
image_cost_method: "request_logs.image_cost_method",
|
|
43
43
|
effort: "request_logs.effort",
|
|
44
44
|
history: "request_logs.history",
|
|
45
|
+
tools: "request_logs.tools",
|
|
45
46
|
status: "request_logs.status",
|
|
46
47
|
error_message: "request_logs.error_message",
|
|
47
48
|
tool_call_count: "request_logs.tool_call_count",
|
|
@@ -42,6 +42,16 @@ const TokenCredentials = z.union([
|
|
|
42
42
|
]);
|
|
43
43
|
type TokenCredentials = z.infer<typeof TokenCredentials>;
|
|
44
44
|
|
|
45
|
+
// CustomScalar: ToolDefinitions
|
|
46
|
+
const ToolDefinitions = z.array(
|
|
47
|
+
z.object({
|
|
48
|
+
name: z.string(),
|
|
49
|
+
description: z.string().optional(),
|
|
50
|
+
inputSchema: z.unknown(),
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
type ToolDefinitions = z.infer<typeof ToolDefinitions>;
|
|
54
|
+
|
|
45
55
|
// Enums: RequestLog
|
|
46
56
|
export const RequestLogOrderBy = z.enum(["id-desc"]).describe("RequestLogOrderBy");
|
|
47
57
|
export type RequestLogOrderBy = z.infer<typeof RequestLogOrderBy>;
|
|
@@ -111,6 +121,7 @@ export const RequestLogBaseSchema = z.object({
|
|
|
111
121
|
image_cost_method: z.string().max(100).nullable(),
|
|
112
122
|
effort: z.string().max(10).nullable(),
|
|
113
123
|
history: HistoryItems.nullable(),
|
|
124
|
+
tools: ToolDefinitions.nullable(),
|
|
114
125
|
status: RequestLogStatus,
|
|
115
126
|
error_message: z.string().nullable(),
|
|
116
127
|
tool_call_count: z.int(),
|
|
@@ -141,6 +152,7 @@ export type RequestLogBaseSchema = z.infer<typeof RequestLogBaseSchema> & {
|
|
|
141
152
|
"image_cost_method",
|
|
142
153
|
"effort",
|
|
143
154
|
"history",
|
|
155
|
+
"tools",
|
|
144
156
|
"status",
|
|
145
157
|
"error_message",
|
|
146
158
|
"tool_call_count",
|
|
@@ -308,6 +320,7 @@ export const RequestLogSubsetA = z.object({
|
|
|
308
320
|
image_cost_method: z.string().max(100).nullable(),
|
|
309
321
|
effort: z.string().max(10).nullable(),
|
|
310
322
|
history: HistoryItems.nullable(),
|
|
323
|
+
tools: ToolDefinitions.nullable(),
|
|
311
324
|
status: RequestLogStatus,
|
|
312
325
|
error_message: z.string().nullable(),
|
|
313
326
|
tool_call_count: z.int(),
|
|
@@ -234,6 +234,7 @@ const entityLabels = {
|
|
|
234
234
|
"entity.RequestLog.image_cost_method": "이미지 비용 추정 방식",
|
|
235
235
|
"entity.RequestLog.effort": "effort level",
|
|
236
236
|
"entity.RequestLog.history": "inject_items 히스토리",
|
|
237
|
+
"entity.RequestLog.tools": "요청에 장착된 tool 정의 목록",
|
|
237
238
|
"entity.RequestLog.status": "실행 상태",
|
|
238
239
|
"entity.RequestLog.error_message": "에러 메시지",
|
|
239
240
|
"entity.RequestLog.tool_call_count": "tool call 횟수",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Knex } from "knex";
|
|
2
|
+
|
|
3
|
+
export async function up(knex: Knex): Promise<void> {
|
|
4
|
+
await knex.schema.alterTable("request_logs", (table) => {
|
|
5
|
+
// add
|
|
6
|
+
table.jsonb("tools").nullable();
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function down(knex: Knex): Promise<void> {
|
|
11
|
+
await knex.schema.alterTable("request_logs", (table) => {
|
|
12
|
+
// rollback - add
|
|
13
|
+
table.dropColumns("tools");
|
|
14
|
+
});
|
|
15
|
+
}
|