@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
|
@@ -14,7 +14,10 @@ export interface ModelCosts {
|
|
|
14
14
|
inputTokens: number;
|
|
15
15
|
outputTokens: number;
|
|
16
16
|
cachedInputTokens: number;
|
|
17
|
+
/** TTL breakdown 이 없는 usage 에 적용할 cache write fallback 단가. */
|
|
17
18
|
cacheCreationInputTokens?: number;
|
|
19
|
+
cacheCreationInputTokens5m?: number;
|
|
20
|
+
cacheCreationInputTokens1h?: number;
|
|
18
21
|
/**
|
|
19
22
|
* long-context 할증. 전체 입력 토큰(input_tokens, cache_read 포함)이 threshold 초과 시
|
|
20
23
|
* 초과분만이 아니라 요청 전체(full session)에 배율 적용.
|
|
@@ -29,7 +32,7 @@ export interface ModelCosts {
|
|
|
29
32
|
|
|
30
33
|
// ── OpenAI — codex app-server 에서 사용 가능한 모델 ─────────────────
|
|
31
34
|
//
|
|
32
|
-
// 가격 출처: https://openai.com/api/
|
|
35
|
+
// 가격 출처: https://developers.openai.com/api/docs/models (2026-07-18 확인)
|
|
33
36
|
// 신모델 출시마다 단가가 바뀌므로(5.2→5.4→5.5) 모델 추가 시 반드시 공식 페이지 재확인해야함
|
|
34
37
|
|
|
35
38
|
// GPT-5.4에서 처음 도입된 long-context 할증 (5.2/5.3-codex는 해당 없음, 5.4-mini/nano는 공식 표에서 long-context 단가 없음)
|
|
@@ -97,105 +100,56 @@ const OPENAI_COSTS: Record<string, ModelCosts> = {
|
|
|
97
100
|
|
|
98
101
|
// ── Anthropic ───────────────────────────────────────────────────────
|
|
99
102
|
|
|
103
|
+
function anthropicCosts(inputTokens: number, outputTokens: number): ModelCosts {
|
|
104
|
+
return {
|
|
105
|
+
inputTokens,
|
|
106
|
+
outputTokens,
|
|
107
|
+
cachedInputTokens: inputTokens / 10,
|
|
108
|
+
// Claude Code subscription OAuth 는 1h TTL 을 자동 선택한다. 구버전처럼 응답에
|
|
109
|
+
// TTL breakdown 이 없을 때만 이 1h 단가를 fallback 으로 사용한다.
|
|
110
|
+
cacheCreationInputTokens: inputTokens * 2,
|
|
111
|
+
cacheCreationInputTokens5m: inputTokens * 1.25,
|
|
112
|
+
cacheCreationInputTokens1h: inputTokens * 2,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
100
116
|
const ANTHROPIC_COSTS: Record<string, ModelCosts> = {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"claude-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"claude-
|
|
116
|
-
|
|
117
|
-
outputTokens: 5,
|
|
118
|
-
cachedInputTokens: 0.1,
|
|
119
|
-
cacheCreationInputTokens: 1.25,
|
|
120
|
-
},
|
|
121
|
-
"claude-3-5-sonnet": {
|
|
122
|
-
inputTokens: 3,
|
|
123
|
-
outputTokens: 15,
|
|
124
|
-
cachedInputTokens: 0.3,
|
|
125
|
-
cacheCreationInputTokens: 3.75,
|
|
126
|
-
},
|
|
127
|
-
"claude-3-7-sonnet": {
|
|
128
|
-
inputTokens: 3,
|
|
129
|
-
outputTokens: 15,
|
|
130
|
-
cachedInputTokens: 0.3,
|
|
131
|
-
cacheCreationInputTokens: 3.75,
|
|
132
|
-
},
|
|
133
|
-
"claude-sonnet-4": {
|
|
134
|
-
inputTokens: 3,
|
|
135
|
-
outputTokens: 15,
|
|
136
|
-
cachedInputTokens: 0.3,
|
|
137
|
-
cacheCreationInputTokens: 3.75,
|
|
138
|
-
},
|
|
139
|
-
"claude-sonnet-4-5": {
|
|
140
|
-
inputTokens: 3,
|
|
141
|
-
outputTokens: 15,
|
|
142
|
-
cachedInputTokens: 0.3,
|
|
143
|
-
cacheCreationInputTokens: 3.75,
|
|
144
|
-
},
|
|
145
|
-
"claude-sonnet-4-6": {
|
|
146
|
-
inputTokens: 3,
|
|
147
|
-
outputTokens: 15,
|
|
148
|
-
cachedInputTokens: 0.3,
|
|
149
|
-
cacheCreationInputTokens: 3.75,
|
|
150
|
-
},
|
|
151
|
-
"claude-sonnet-4-7": {
|
|
152
|
-
inputTokens: 3,
|
|
153
|
-
outputTokens: 15,
|
|
154
|
-
cachedInputTokens: 0.3,
|
|
155
|
-
cacheCreationInputTokens: 3.75,
|
|
156
|
-
},
|
|
157
|
-
"claude-opus-4": {
|
|
158
|
-
inputTokens: 15,
|
|
159
|
-
outputTokens: 75,
|
|
160
|
-
cachedInputTokens: 1.5,
|
|
161
|
-
cacheCreationInputTokens: 18.75,
|
|
162
|
-
},
|
|
163
|
-
"claude-opus-4-1": {
|
|
164
|
-
inputTokens: 15,
|
|
165
|
-
outputTokens: 75,
|
|
166
|
-
cachedInputTokens: 1.5,
|
|
167
|
-
cacheCreationInputTokens: 18.75,
|
|
168
|
-
},
|
|
169
|
-
"claude-opus-4-5": {
|
|
170
|
-
inputTokens: 5,
|
|
171
|
-
outputTokens: 25,
|
|
172
|
-
cachedInputTokens: 0.5,
|
|
173
|
-
cacheCreationInputTokens: 6.25,
|
|
174
|
-
},
|
|
175
|
-
"claude-opus-4-6": {
|
|
176
|
-
inputTokens: 5,
|
|
177
|
-
outputTokens: 25,
|
|
178
|
-
cachedInputTokens: 0.5,
|
|
179
|
-
cacheCreationInputTokens: 6.25,
|
|
180
|
-
},
|
|
181
|
-
"claude-opus-4-7": {
|
|
182
|
-
inputTokens: 5,
|
|
183
|
-
outputTokens: 25,
|
|
184
|
-
cachedInputTokens: 0.5,
|
|
185
|
-
cacheCreationInputTokens: 6.25,
|
|
186
|
-
},
|
|
187
|
-
"claude-opus-4-8": {
|
|
188
|
-
inputTokens: 5,
|
|
189
|
-
outputTokens: 25,
|
|
190
|
-
cachedInputTokens: 0.5,
|
|
191
|
-
cacheCreationInputTokens: 6.25,
|
|
192
|
-
},
|
|
117
|
+
"claude-fable-5": anthropicCosts(10, 50),
|
|
118
|
+
sonnet: anthropicCosts(3, 15),
|
|
119
|
+
"claude-3-5-haiku": anthropicCosts(0.8, 4),
|
|
120
|
+
"claude-haiku-4-5": anthropicCosts(1, 5),
|
|
121
|
+
"claude-3-5-sonnet": anthropicCosts(3, 15),
|
|
122
|
+
"claude-3-7-sonnet": anthropicCosts(3, 15),
|
|
123
|
+
"claude-sonnet-4": anthropicCosts(3, 15),
|
|
124
|
+
"claude-sonnet-4-5": anthropicCosts(3, 15),
|
|
125
|
+
"claude-sonnet-4-6": anthropicCosts(3, 15),
|
|
126
|
+
"claude-sonnet-4-7": anthropicCosts(3, 15),
|
|
127
|
+
"claude-opus-4": anthropicCosts(15, 75),
|
|
128
|
+
"claude-opus-4-1": anthropicCosts(15, 75),
|
|
129
|
+
"claude-opus-4-5": anthropicCosts(5, 25),
|
|
130
|
+
"claude-opus-4-6": anthropicCosts(5, 25),
|
|
131
|
+
"claude-opus-4-7": anthropicCosts(5, 25),
|
|
132
|
+
"claude-opus-4-8": anthropicCosts(5, 25),
|
|
193
133
|
};
|
|
194
134
|
|
|
135
|
+
// Anthropic 공식 introductory pricing: 2026-08-31까지 $2/$10, 이후 $3/$15.
|
|
136
|
+
// qgrid 는 요청 시점에 계산하므로 배포를 다시 하지 않아도 2026-09-01 UTC부터 표준 단가로 전환한다.
|
|
137
|
+
const CLAUDE_SONNET_5_INTRO_PRICING_END_MS = Date.UTC(2026, 8, 1);
|
|
138
|
+
const CLAUDE_SONNET_5_INTRO_COSTS = anthropicCosts(2, 10);
|
|
139
|
+
const CLAUDE_SONNET_5_STANDARD_COSTS = anthropicCosts(3, 15);
|
|
140
|
+
|
|
141
|
+
// gpt-5.3-codex-spark 는 research preview 로 공식 token 단가가 아직 final 이 아니다.
|
|
142
|
+
// 지원 타입은 유지하되, 공식 단가가 공개될 때까지 아래 generic estimate 로 계산한다.
|
|
143
|
+
// @see https://help.openai.com/en/articles/20001106-codex-rate-card
|
|
195
144
|
const DEFAULT_COSTS: ModelCosts = { inputTokens: 3, outputTokens: 15, cachedInputTokens: 0.3 };
|
|
196
145
|
|
|
197
|
-
export function getModelCosts(model: string): ModelCosts {
|
|
198
|
-
const normalizedModel = model.replace(/\[1m\]$/i, "");
|
|
146
|
+
export function getModelCosts(model: string, atMs = Date.now()): ModelCosts {
|
|
147
|
+
const normalizedModel = (model.split("/").pop() ?? model).replace(/\[1m\]$/i, "");
|
|
148
|
+
if (normalizedModel === "claude-sonnet-5") {
|
|
149
|
+
return atMs < CLAUDE_SONNET_5_INTRO_PRICING_END_MS
|
|
150
|
+
? CLAUDE_SONNET_5_INTRO_COSTS
|
|
151
|
+
: CLAUDE_SONNET_5_STANDARD_COSTS;
|
|
152
|
+
}
|
|
199
153
|
return OPENAI_COSTS[normalizedModel] ?? ANTHROPIC_COSTS[normalizedModel] ?? DEFAULT_COSTS;
|
|
200
154
|
}
|
|
201
155
|
|
|
@@ -206,11 +160,26 @@ export function calculateCostUsd(
|
|
|
206
160
|
outputTokens: number;
|
|
207
161
|
cachedInputTokens?: number;
|
|
208
162
|
cacheCreationInputTokens?: number;
|
|
163
|
+
cacheCreationInputTokens5m?: number;
|
|
164
|
+
cacheCreationInputTokens1h?: number;
|
|
209
165
|
},
|
|
166
|
+
atMs = Date.now(),
|
|
210
167
|
): number {
|
|
211
|
-
const costs = getModelCosts(model);
|
|
168
|
+
const costs = getModelCosts(model, atMs);
|
|
212
169
|
const cachedInput = usage.cachedInputTokens ?? 0;
|
|
213
|
-
const
|
|
170
|
+
const cacheCreationInput5m = Math.max(usage.cacheCreationInputTokens5m ?? 0, 0);
|
|
171
|
+
const cacheCreationInput1h = Math.max(usage.cacheCreationInputTokens1h ?? 0, 0);
|
|
172
|
+
const cacheCreationInput = Math.max(
|
|
173
|
+
usage.cacheCreationInputTokens ?? 0,
|
|
174
|
+
cacheCreationInput5m + cacheCreationInput1h,
|
|
175
|
+
);
|
|
176
|
+
const classifiedCacheCreationInput5m = Math.min(cacheCreationInput5m, cacheCreationInput);
|
|
177
|
+
const classifiedCacheCreationInput1h = Math.min(
|
|
178
|
+
cacheCreationInput1h,
|
|
179
|
+
cacheCreationInput - classifiedCacheCreationInput5m,
|
|
180
|
+
);
|
|
181
|
+
const unclassifiedCacheCreationInput =
|
|
182
|
+
cacheCreationInput - classifiedCacheCreationInput5m - classifiedCacheCreationInput1h;
|
|
214
183
|
const nonCachedInput = Math.max(usage.inputTokens - cachedInput - cacheCreationInput, 0);
|
|
215
184
|
|
|
216
185
|
// long-context 할증: 전체 입력(input_tokens, cache 포함)이 threshold 초과 시 요청 전체에 배율 적용
|
|
@@ -218,15 +187,23 @@ export function calculateCostUsd(
|
|
|
218
187
|
const isLongContext = lc !== undefined && usage.inputTokens > lc.threshold;
|
|
219
188
|
const inputRate = costs.inputTokens * (isLongContext ? lc.inputMultiplier : 1);
|
|
220
189
|
const cachedRate = costs.cachedInputTokens * (isLongContext ? lc.cachedInputMultiplier : 1);
|
|
221
|
-
const
|
|
190
|
+
const cacheCreationFallbackRate =
|
|
222
191
|
(costs.cacheCreationInputTokens ?? costs.inputTokens) *
|
|
223
192
|
(isLongContext ? lc.inputMultiplier : 1);
|
|
193
|
+
const cacheCreation5mRate =
|
|
194
|
+
(costs.cacheCreationInputTokens5m ?? costs.cacheCreationInputTokens ?? costs.inputTokens) *
|
|
195
|
+
(isLongContext ? lc.inputMultiplier : 1);
|
|
196
|
+
const cacheCreation1hRate =
|
|
197
|
+
(costs.cacheCreationInputTokens1h ?? costs.cacheCreationInputTokens ?? costs.inputTokens) *
|
|
198
|
+
(isLongContext ? lc.inputMultiplier : 1);
|
|
224
199
|
const outputRate = costs.outputTokens * (isLongContext ? lc.outputMultiplier : 1);
|
|
225
200
|
|
|
226
201
|
return (
|
|
227
202
|
(nonCachedInput / 1_000_000) * inputRate +
|
|
228
203
|
(usage.outputTokens / 1_000_000) * outputRate +
|
|
229
204
|
(cachedInput / 1_000_000) * cachedRate +
|
|
230
|
-
(
|
|
205
|
+
(unclassifiedCacheCreationInput / 1_000_000) * cacheCreationFallbackRate +
|
|
206
|
+
(classifiedCacheCreationInput5m / 1_000_000) * cacheCreation5mRate +
|
|
207
|
+
(classifiedCacheCreationInput1h / 1_000_000) * cacheCreation1hRate
|
|
231
208
|
);
|
|
232
209
|
}
|
|
@@ -13,6 +13,8 @@ import { type UserInput } from "../../../codex-protocol/v2/UserInput";
|
|
|
13
13
|
// provider 전용 cache write 세부 필드는 여기서 확장해 보존한다.
|
|
14
14
|
export type ProviderTokenUsageBreakdown = TokenUsageBreakdown & {
|
|
15
15
|
cacheCreationInputTokens?: number;
|
|
16
|
+
cacheCreationInputTokens5m?: number;
|
|
17
|
+
cacheCreationInputTokens1h?: number;
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
// thread 재사용 라우팅 좌표. 상위(qgrid.dispatcher)에서 conv 핸들 검증을 통과한 경우에만 전달.
|
|
@@ -30,6 +32,19 @@ export interface GeneratedImage {
|
|
|
30
32
|
revisedPrompt: string | null;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
// Provider/런타임이 요청 모델 대신 다른 모델로 실제 응답을 생성한 이력.
|
|
36
|
+
// 현재 Claude Code 의 Fable refusal → Opus 안전 fallback 을 표현하며, 향후 provider
|
|
37
|
+
// fallback 도 같은 계약으로 올릴 수 있다.
|
|
38
|
+
export interface ModelFallback {
|
|
39
|
+
trigger: "refusal";
|
|
40
|
+
fromModel: string;
|
|
41
|
+
toModel: string;
|
|
42
|
+
category?: string;
|
|
43
|
+
explanation?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type CostSource = "provider" | "pricing_table" | "mixed";
|
|
47
|
+
|
|
33
48
|
export interface GenerateRequest {
|
|
34
49
|
// 미지정이면 dispatcher 가 provider 별 default 를 적용한다(Anthropic: ANTHROPIC_DEFAULT_MODEL).
|
|
35
50
|
// OpenAI 경로는 항상 prefix split 후 canonical model 을 넘기므로 영향 없음.
|
|
@@ -65,7 +80,10 @@ export interface GenerateResult {
|
|
|
65
80
|
// Provider 가 직접 산출한 비용. Anthropic Claude Code 는 total_cost_usd 를 주므로 이 값을
|
|
66
81
|
// 우선 사용하고, 없으면 상위가 모델별 가격표로 계산한다.
|
|
67
82
|
costUsd?: number;
|
|
83
|
+
// model 은 실제 응답을 생성한 serving model. fallback 이 없으면 requestedModel 과 같다.
|
|
68
84
|
model: string;
|
|
85
|
+
requestedModel?: string;
|
|
86
|
+
modelFallbacks?: Array<ModelFallback>;
|
|
69
87
|
// 이번 turn 이 사용한 thread 좌표. 상위가 conv 핸들을 발급/갱신하는 데 쓴다.
|
|
70
88
|
threadCoord: ReuseThreadCoord;
|
|
71
89
|
// 이미지 turn 에서만 채워짐(OpenAI 경로). 완성 이미지가 없으면 undefined.
|