ima2-gen 1.1.21 → 1.1.22
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.md +30 -4
- package/bin/ima2.js +14 -4
- package/bin/lib/platform.js +34 -5
- package/docs/README.ko.md +31 -0
- package/lib/agentQueueWorker.js +6 -0
- package/lib/agentRuntime.js +3 -2
- package/lib/atomicWrite.js +14 -0
- package/lib/grokProxyLauncher.js +5 -3
- package/lib/inflight.js +1 -1
- package/lib/oauthLauncher.js +5 -0
- package/lib/videoFrameExtract.js +3 -3
- package/package.json +5 -7
- package/routes/edit.js +2 -1
- package/routes/generate.js +4 -3
- package/routes/health.js +4 -3
- package/routes/multimode.js +2 -1
- package/routes/video.js +4 -2
- package/server.js +29 -2
- package/ui/dist/.vite/manifest.json +12 -12
- package/ui/dist/assets/{AgentWorkspace-B_hq9CLg.js → AgentWorkspace-COxQ5TjU.js} +1 -1
- package/ui/dist/assets/{CardNewsWorkspace-wD12J7qk.js → CardNewsWorkspace-B0OkcuVz.js} +1 -1
- package/ui/dist/assets/{NodeCanvas-CI_wuPMf.js → NodeCanvas-BSsclEBh.js} +1 -1
- package/ui/dist/assets/{PromptBuilderPanel-CUTujJUV.js → PromptBuilderPanel-DpC9A5Rz.js} +1 -1
- package/ui/dist/assets/{PromptImportDialog-CUi66jPK.js → PromptImportDialog-CVwT0rLd.js} +2 -2
- package/ui/dist/assets/{PromptImportDiscoverySection-Cm3vrjY4.js → PromptImportDiscoverySection-BDCkRCRs.js} +1 -1
- package/ui/dist/assets/{PromptImportFolderSection-DOtWTD9n.js → PromptImportFolderSection-QoKbZD83.js} +1 -1
- package/ui/dist/assets/{PromptLibraryPanel-BMjQegRa.js → PromptLibraryPanel-BhFgeKnY.js} +2 -2
- package/ui/dist/assets/SettingsWorkspace-CfjrlH5R.js +1 -0
- package/ui/dist/assets/index-C-mur7pa.css +1 -0
- package/ui/dist/assets/index-CCP5nUOj.js +42 -0
- package/ui/dist/assets/{index-31uVIdt4.js → index-Cxhzi3bs.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/bin/commands/annotate.ts +0 -119
- package/bin/commands/cancel.ts +0 -48
- package/bin/commands/canvas-versions.ts +0 -80
- package/bin/commands/capabilities.ts +0 -110
- package/bin/commands/cardnews.ts +0 -249
- package/bin/commands/comfy.ts +0 -54
- package/bin/commands/config.ts +0 -186
- package/bin/commands/defaults.ts +0 -192
- package/bin/commands/doctor.ts +0 -202
- package/bin/commands/edit.ts +0 -150
- package/bin/commands/gen.ts +0 -214
- package/bin/commands/grok.ts +0 -90
- package/bin/commands/history.ts +0 -146
- package/bin/commands/ls.ts +0 -64
- package/bin/commands/metadata.ts +0 -39
- package/bin/commands/multimode.ts +0 -196
- package/bin/commands/node.ts +0 -166
- package/bin/commands/observability.ts +0 -176
- package/bin/commands/ping.ts +0 -31
- package/bin/commands/prompt-sub/build.ts +0 -101
- package/bin/commands/prompt.ts +0 -492
- package/bin/commands/ps.ts +0 -81
- package/bin/commands/session.ts +0 -266
- package/bin/commands/show.ts +0 -72
- package/bin/commands/skill.ts +0 -70
- package/bin/commands/video.ts +0 -442
- package/bin/ima2.ts +0 -430
- package/bin/lib/args.ts +0 -92
- package/bin/lib/browser-id.ts +0 -16
- package/bin/lib/client.ts +0 -122
- package/bin/lib/config-store.ts +0 -120
- package/bin/lib/destructive-confirm.ts +0 -19
- package/bin/lib/doctor-checks.ts +0 -91
- package/bin/lib/error-hints.ts +0 -23
- package/bin/lib/files.ts +0 -39
- package/bin/lib/output.ts +0 -73
- package/bin/lib/platform.ts +0 -99
- package/bin/lib/recover-output.ts +0 -139
- package/bin/lib/sse.ts +0 -73
- package/bin/lib/star-prompt.ts +0 -97
- package/bin/lib/storage-doctor.ts +0 -39
- package/bin/lib/ui-build.ts +0 -85
- package/config.ts +0 -354
- package/lib/agentCommandParser.ts +0 -69
- package/lib/agentGenerationPlanner.ts +0 -273
- package/lib/agentQuestionResponder.ts +0 -266
- package/lib/agentQueueStore.ts +0 -270
- package/lib/agentQueueWorker.ts +0 -89
- package/lib/agentRuntime.ts +0 -604
- package/lib/agentSettings.ts +0 -72
- package/lib/agentStore.ts +0 -422
- package/lib/agentStoreRows.ts +0 -136
- package/lib/agentTypes.ts +0 -154
- package/lib/apiCachePolicy.ts +0 -11
- package/lib/assetLifecycle.ts +0 -146
- package/lib/canvasVersionStore.ts +0 -223
- package/lib/capabilities.ts +0 -126
- package/lib/cardNewsGenerator.ts +0 -271
- package/lib/cardNewsJobStore.ts +0 -142
- package/lib/cardNewsManifestStore.ts +0 -154
- package/lib/cardNewsPlanner.ts +0 -236
- package/lib/cardNewsPlannerClient.ts +0 -155
- package/lib/cardNewsPlannerPrompt.ts +0 -62
- package/lib/cardNewsPlannerSchema.ts +0 -321
- package/lib/cardNewsRoleTemplateStore.ts +0 -47
- package/lib/cardNewsTemplateStore.ts +0 -252
- package/lib/codexDetect.ts +0 -71
- package/lib/comfyBridge.ts +0 -235
- package/lib/composerSnapshot.ts +0 -33
- package/lib/configKeys.ts +0 -62
- package/lib/db.ts +0 -295
- package/lib/errInfo.ts +0 -43
- package/lib/errorClassify.ts +0 -100
- package/lib/generationCancel.ts +0 -28
- package/lib/generationErrors.ts +0 -238
- package/lib/grokImageAdapter.ts +0 -513
- package/lib/grokMultimodeAdapter.ts +0 -84
- package/lib/grokProxyLauncher.ts +0 -153
- package/lib/grokRuntime.ts +0 -23
- package/lib/grokSizeMapper.ts +0 -71
- package/lib/grokVideoAdapter.ts +0 -458
- package/lib/grokVideoCanvas.ts +0 -26
- package/lib/grokVideoDownload.ts +0 -59
- package/lib/grokVideoPlannerPrompt.ts +0 -67
- package/lib/historyIndex.ts +0 -51
- package/lib/historyList.ts +0 -181
- package/lib/imageMetadata.ts +0 -113
- package/lib/imageMetadataStore.ts +0 -67
- package/lib/imageModels.ts +0 -165
- package/lib/inflight.ts +0 -281
- package/lib/localImportStore.ts +0 -114
- package/lib/logger.ts +0 -161
- package/lib/nodeStore.ts +0 -91
- package/lib/oauthLauncher.ts +0 -94
- package/lib/oauthNormalize.ts +0 -30
- package/lib/oauthProxy/errors.ts +0 -128
- package/lib/oauthProxy/generators.ts +0 -494
- package/lib/oauthProxy/index.ts +0 -28
- package/lib/oauthProxy/prompts.ts +0 -123
- package/lib/oauthProxy/references.ts +0 -45
- package/lib/oauthProxy/runtime.ts +0 -115
- package/lib/oauthProxy/streams.ts +0 -232
- package/lib/oauthProxy/types.ts +0 -9
- package/lib/oauthProxy.ts +0 -3
- package/lib/openDirectory.ts +0 -47
- package/lib/pngInfo.ts +0 -26
- package/lib/promptBuilder/attachments.ts +0 -74
- package/lib/promptBuilder/client.ts +0 -130
- package/lib/promptBuilder/constants.ts +0 -9
- package/lib/promptBuilder/context.ts +0 -36
- package/lib/promptBuilder/errors.ts +0 -12
- package/lib/promptBuilder/requestSchema.ts +0 -56
- package/lib/promptBuilder/responseParser.ts +0 -219
- package/lib/promptBuilder/systemPrompt.ts +0 -135
- package/lib/promptBuilder/transport.ts +0 -94
- package/lib/promptBuilder/types.ts +0 -109
- package/lib/promptImport/curatedSources.ts +0 -141
- package/lib/promptImport/discoveryRegistry.ts +0 -329
- package/lib/promptImport/errors.ts +0 -18
- package/lib/promptImport/githubDiscovery.ts +0 -309
- package/lib/promptImport/githubFolder.ts +0 -397
- package/lib/promptImport/githubSource.ts +0 -257
- package/lib/promptImport/gptImageHints.ts +0 -70
- package/lib/promptImport/parsePromptCandidates.ts +0 -179
- package/lib/promptImport/promptIndex.ts +0 -326
- package/lib/promptImport/rankPromptCandidates.ts +0 -65
- package/lib/promptImport/types.ts +0 -103
- package/lib/promptSafetyPolicy.ts +0 -5
- package/lib/providerOptions.ts +0 -56
- package/lib/referenceImageCompress.ts +0 -84
- package/lib/refs.ts +0 -133
- package/lib/requestLogger.ts +0 -49
- package/lib/responsesDoctor.ts +0 -456
- package/lib/responsesErrors.ts +0 -83
- package/lib/responsesFallback.ts +0 -114
- package/lib/responsesImageAdapter.ts +0 -466
- package/lib/responsesParse.ts +0 -452
- package/lib/responsesTools.ts +0 -28
- package/lib/runtimeContext.ts +0 -146
- package/lib/runtimePorts.ts +0 -105
- package/lib/sessionStore.ts +0 -308
- package/lib/storageMigration.ts +0 -310
- package/lib/styleSheet.ts +0 -139
- package/lib/systemTrash.ts +0 -20
- package/lib/videoContinuity.ts +0 -180
- package/lib/videoFrameExtract.ts +0 -78
- package/lib/videoSeriesChain.ts +0 -29
- package/lib/visibleTextLanguagePolicy.ts +0 -7
- package/routes/agent.ts +0 -308
- package/routes/annotations.ts +0 -118
- package/routes/canvasVersions.ts +0 -69
- package/routes/capabilities.ts +0 -18
- package/routes/cardNews.ts +0 -211
- package/routes/comfy.ts +0 -43
- package/routes/edit.ts +0 -352
- package/routes/generate.ts +0 -492
- package/routes/grok.ts +0 -24
- package/routes/health.ts +0 -123
- package/routes/history.ts +0 -221
- package/routes/imageImport.ts +0 -37
- package/routes/index.ts +0 -52
- package/routes/metadata.ts +0 -77
- package/routes/multimode.ts +0 -499
- package/routes/nodes.ts +0 -578
- package/routes/promptBuilder.ts +0 -37
- package/routes/promptImport.ts +0 -379
- package/routes/prompts.ts +0 -428
- package/routes/quota.ts +0 -89
- package/routes/sessions.ts +0 -317
- package/routes/storage.ts +0 -47
- package/routes/video.ts +0 -300
- package/routes/videoExtended.ts +0 -284
- package/server.ts +0 -293
- package/ui/dist/assets/SettingsWorkspace-PiaVnsdA.js +0 -1
- package/ui/dist/assets/index-CjgnNtgt.css +0 -1
- package/ui/dist/assets/index-Da2s4_-5.js +0 -36
package/lib/responsesParse.ts
DELETED
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
import { setJobPhase } from "./inflight.js";
|
|
2
|
-
import { logEvent } from "./logger.js";
|
|
3
|
-
|
|
4
|
-
export interface ParsedImage {
|
|
5
|
-
b64: string;
|
|
6
|
-
revisedPrompt: string | null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type FinalImageHandler = (image: ParsedImage, index: number) => Promise<void> | void;
|
|
10
|
-
|
|
11
|
-
export interface ResponseOutputSummary {
|
|
12
|
-
eventType: string;
|
|
13
|
-
itemType: string | null;
|
|
14
|
-
status: string | null;
|
|
15
|
-
hasResult: boolean;
|
|
16
|
-
resultChars: number;
|
|
17
|
-
revisedPromptChars: number;
|
|
18
|
-
hasError: boolean;
|
|
19
|
-
errorCode: string | null;
|
|
20
|
-
errorType: string | null;
|
|
21
|
-
errorParam: string | null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface ResponseDiagnostics {
|
|
25
|
-
eventTypes: Record<string, number>;
|
|
26
|
-
streamStats: {
|
|
27
|
-
chunkCount: number;
|
|
28
|
-
bytesRead: number;
|
|
29
|
-
maxChunkBytes: number;
|
|
30
|
-
lfBoundaryCount: number;
|
|
31
|
-
crlfBoundaryCount: number;
|
|
32
|
-
parseSkipCount: number;
|
|
33
|
-
finalBufferChars: number;
|
|
34
|
-
sawDoneSentinel: boolean;
|
|
35
|
-
sawResponseCompleted: boolean;
|
|
36
|
-
};
|
|
37
|
-
outputItemSummary: ResponseOutputSummary[];
|
|
38
|
-
imageCallSeen: boolean;
|
|
39
|
-
imageCallCompleted: boolean;
|
|
40
|
-
imageCallFailed: boolean;
|
|
41
|
-
imageResultCount: number;
|
|
42
|
-
webSearchCallSeen: boolean;
|
|
43
|
-
messageOutputSeen: boolean;
|
|
44
|
-
outputTextChars: number;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface ParsedResponsesResult {
|
|
48
|
-
images: ParsedImage[];
|
|
49
|
-
usage: Record<string, number> | null;
|
|
50
|
-
webSearchCalls: number;
|
|
51
|
-
eventCount: number;
|
|
52
|
-
eventTypes: Record<string, number>;
|
|
53
|
-
extraIgnored: number;
|
|
54
|
-
text: string | null;
|
|
55
|
-
diagnostics: ResponseDiagnostics;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
interface SseItem {
|
|
59
|
-
type?: string;
|
|
60
|
-
partial_image_b64?: string;
|
|
61
|
-
partial_image_index?: number;
|
|
62
|
-
partial_image?: string;
|
|
63
|
-
image?: string;
|
|
64
|
-
result?: string;
|
|
65
|
-
index?: number;
|
|
66
|
-
revised_prompt?: string;
|
|
67
|
-
status?: string;
|
|
68
|
-
error?: {
|
|
69
|
-
code?: string;
|
|
70
|
-
type?: string;
|
|
71
|
-
param?: string;
|
|
72
|
-
};
|
|
73
|
-
content?: Array<{ type?: string; text?: string }>;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
interface SseData {
|
|
77
|
-
type?: string;
|
|
78
|
-
delta?: string;
|
|
79
|
-
text?: string;
|
|
80
|
-
item?: SseItem;
|
|
81
|
-
partial_image_b64?: string;
|
|
82
|
-
partial_image_index?: number;
|
|
83
|
-
partial_image?: string;
|
|
84
|
-
image?: string;
|
|
85
|
-
result?: string;
|
|
86
|
-
index?: number;
|
|
87
|
-
response?: {
|
|
88
|
-
usage?: Record<string, number>;
|
|
89
|
-
output?: SseItem[];
|
|
90
|
-
tool_usage?: { web_search?: { num_requests?: number } };
|
|
91
|
-
};
|
|
92
|
-
error?: { code?: string };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
interface ParseState {
|
|
96
|
-
images: ParsedImage[];
|
|
97
|
-
eventTypes: Record<string, number>;
|
|
98
|
-
outputItemSummary: ResponseOutputSummary[];
|
|
99
|
-
usage: Record<string, number> | null;
|
|
100
|
-
textOutput: string;
|
|
101
|
-
finalTextOutput: string | null;
|
|
102
|
-
webSearchCalls: number;
|
|
103
|
-
eventCount: number;
|
|
104
|
-
extraIgnored: number;
|
|
105
|
-
chunkCount: number;
|
|
106
|
-
bytesRead: number;
|
|
107
|
-
maxChunkBytes: number;
|
|
108
|
-
lfBoundaryCount: number;
|
|
109
|
-
crlfBoundaryCount: number;
|
|
110
|
-
parseSkipCount: number;
|
|
111
|
-
finalBufferChars: number;
|
|
112
|
-
sawDoneSentinel: boolean;
|
|
113
|
-
sawResponseCompleted: boolean;
|
|
114
|
-
imageCallSeen: boolean;
|
|
115
|
-
imageCallCompleted: boolean;
|
|
116
|
-
imageCallFailed: boolean;
|
|
117
|
-
imageResultCount: number;
|
|
118
|
-
webSearchCallSeen: boolean;
|
|
119
|
-
messageOutputSeen: boolean;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function createState(): ParseState {
|
|
123
|
-
return {
|
|
124
|
-
images: [],
|
|
125
|
-
eventTypes: {},
|
|
126
|
-
outputItemSummary: [],
|
|
127
|
-
usage: null,
|
|
128
|
-
textOutput: "",
|
|
129
|
-
finalTextOutput: null,
|
|
130
|
-
webSearchCalls: 0,
|
|
131
|
-
eventCount: 0,
|
|
132
|
-
extraIgnored: 0,
|
|
133
|
-
chunkCount: 0,
|
|
134
|
-
bytesRead: 0,
|
|
135
|
-
maxChunkBytes: 0,
|
|
136
|
-
lfBoundaryCount: 0,
|
|
137
|
-
crlfBoundaryCount: 0,
|
|
138
|
-
parseSkipCount: 0,
|
|
139
|
-
finalBufferChars: 0,
|
|
140
|
-
sawDoneSentinel: false,
|
|
141
|
-
sawResponseCompleted: false,
|
|
142
|
-
imageCallSeen: false,
|
|
143
|
-
imageCallCompleted: false,
|
|
144
|
-
imageCallFailed: false,
|
|
145
|
-
imageResultCount: 0,
|
|
146
|
-
webSearchCallSeen: false,
|
|
147
|
-
messageOutputSeen: false,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const MAX_DIAGNOSTIC_LABEL_CHARS = 120;
|
|
152
|
-
const UNSAFE_DIAGNOSTIC_LABEL = /(bearer\s+|sk-[a-z0-9_-]{4,}|data:image\/|https?:\/\/|[a-z][a-z0-9+.-]*:\/\/|@|[\r\n])/i;
|
|
153
|
-
const SAFE_DIAGNOSTIC_LABEL = /^[A-Za-z0-9_.:[\]-]+$/;
|
|
154
|
-
|
|
155
|
-
export function safeDiagnosticLabel(value: unknown, fallback: string | null = null): string | null {
|
|
156
|
-
if (typeof value !== "string" || value.length === 0) return fallback;
|
|
157
|
-
const trimmed = value.slice(0, MAX_DIAGNOSTIC_LABEL_CHARS);
|
|
158
|
-
if (UNSAFE_DIAGNOSTIC_LABEL.test(trimmed)) return "_redacted";
|
|
159
|
-
if (!SAFE_DIAGNOSTIC_LABEL.test(trimmed)) return "_redacted";
|
|
160
|
-
return trimmed;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function extractSseData(block: string): string {
|
|
164
|
-
let eventData = "";
|
|
165
|
-
for (const rawLine of block.split(/\r?\n/)) {
|
|
166
|
-
const line = rawLine.replace(/\r$/, "");
|
|
167
|
-
if (line.startsWith("data:")) eventData += line.slice(5).trimStart();
|
|
168
|
-
}
|
|
169
|
-
return eventData;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function nextSseBlock(buffer: string): { block: string; rest: string; delimiter: string } | null {
|
|
173
|
-
const match = /\r?\n\r?\n/.exec(buffer);
|
|
174
|
-
if (!match) return null;
|
|
175
|
-
return {
|
|
176
|
-
block: buffer.slice(0, match.index),
|
|
177
|
-
rest: buffer.slice(match.index + match[0].length),
|
|
178
|
-
delimiter: match[0],
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function extractPartialImage(data: SseData): { b64: string; index: number | null } | null {
|
|
183
|
-
if (typeof data?.type !== "string" || !data.type.includes("partial")) return null;
|
|
184
|
-
const item = data.item || {};
|
|
185
|
-
const b64 =
|
|
186
|
-
data.partial_image_b64 ||
|
|
187
|
-
data.partial_image ||
|
|
188
|
-
data.image ||
|
|
189
|
-
data.result ||
|
|
190
|
-
item.partial_image_b64 ||
|
|
191
|
-
item.partial_image ||
|
|
192
|
-
item.image ||
|
|
193
|
-
item.result;
|
|
194
|
-
if (typeof b64 !== "string" || b64.length === 0) return null;
|
|
195
|
-
const index =
|
|
196
|
-
typeof data.partial_image_index === "number" && Number.isFinite(data.partial_image_index)
|
|
197
|
-
? data.partial_image_index
|
|
198
|
-
: typeof data.index === "number" && Number.isFinite(data.index)
|
|
199
|
-
? data.index
|
|
200
|
-
: typeof item.partial_image_index === "number" && Number.isFinite(item.partial_image_index)
|
|
201
|
-
? item.partial_image_index
|
|
202
|
-
: typeof item.index === "number" && Number.isFinite(item.index)
|
|
203
|
-
? item.index
|
|
204
|
-
: null;
|
|
205
|
-
return { b64, index };
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function extractTextDelta(data: SseData): string | null {
|
|
209
|
-
if (data.type === "response.output_text.delta" && typeof data.delta === "string") return data.delta;
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function extractFinalText(data: SseData): string | null {
|
|
214
|
-
if (data.type === "response.output_text.done" && typeof data.text === "string") return cleanTextOutput(data.text);
|
|
215
|
-
if (data.type === "response.output_item.done" && data.item?.type === "message") {
|
|
216
|
-
return extractJsonItemText(data.item);
|
|
217
|
-
}
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function extractJsonItemText(item: { type?: string; text?: string; content?: Array<{ type?: string; text?: string }> }): string | null {
|
|
222
|
-
if (item.type === "output_text" && typeof item.text === "string") return cleanTextOutput(item.text);
|
|
223
|
-
if (!Array.isArray(item.content)) return null;
|
|
224
|
-
const text = item.content
|
|
225
|
-
.filter((part) => part.type === "output_text" && typeof part.text === "string")
|
|
226
|
-
.map((part) => part.text)
|
|
227
|
-
.join("\n\n");
|
|
228
|
-
return cleanTextOutput(text);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function cleanTextOutput(value: string): string | null {
|
|
232
|
-
const trimmed = value.trim();
|
|
233
|
-
return trimmed ? trimmed.slice(0, 4_000) : null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function summarizeItem(eventType: string, item: SseItem): ResponseOutputSummary {
|
|
237
|
-
const result = typeof item.result === "string" ? item.result : "";
|
|
238
|
-
const revised = typeof item.revised_prompt === "string" ? item.revised_prompt : "";
|
|
239
|
-
return {
|
|
240
|
-
eventType: safeDiagnosticLabel(eventType, "_unknown") || "_unknown",
|
|
241
|
-
itemType: safeDiagnosticLabel(item.type),
|
|
242
|
-
status: safeDiagnosticLabel(item.status),
|
|
243
|
-
hasResult: result.length > 0,
|
|
244
|
-
resultChars: result.length,
|
|
245
|
-
revisedPromptChars: revised.length,
|
|
246
|
-
hasError: Boolean(item.error),
|
|
247
|
-
errorCode: safeDiagnosticLabel(item.error?.code),
|
|
248
|
-
errorType: safeDiagnosticLabel(item.error?.type),
|
|
249
|
-
errorParam: safeDiagnosticLabel(item.error?.param),
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function recordOutputItem(state: ParseState, eventType: string, item: SseItem | undefined): void {
|
|
254
|
-
if (!item) return;
|
|
255
|
-
const summary = summarizeItem(eventType, item);
|
|
256
|
-
state.outputItemSummary.push(summary);
|
|
257
|
-
if (item.type === "image_generation_call") {
|
|
258
|
-
state.imageCallSeen = true;
|
|
259
|
-
state.imageCallCompleted = state.imageCallCompleted || eventType === "response.output_item.done" || item.status === "completed";
|
|
260
|
-
state.imageCallFailed = state.imageCallFailed || item.status === "failed" || Boolean(item.error);
|
|
261
|
-
if (summary.hasResult) state.imageResultCount++;
|
|
262
|
-
}
|
|
263
|
-
if (item.type === "web_search_call") state.webSearchCallSeen = true;
|
|
264
|
-
if (item.type === "message") state.messageOutputSeen = true;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function diagnosticsFromState(state: ParseState): ResponseDiagnostics {
|
|
268
|
-
const outputTextChars = (state.finalTextOutput ?? cleanTextOutput(state.textOutput) ?? "").length;
|
|
269
|
-
return {
|
|
270
|
-
eventTypes: state.eventTypes,
|
|
271
|
-
streamStats: {
|
|
272
|
-
chunkCount: state.chunkCount,
|
|
273
|
-
bytesRead: state.bytesRead,
|
|
274
|
-
maxChunkBytes: state.maxChunkBytes,
|
|
275
|
-
lfBoundaryCount: state.lfBoundaryCount,
|
|
276
|
-
crlfBoundaryCount: state.crlfBoundaryCount,
|
|
277
|
-
parseSkipCount: state.parseSkipCount,
|
|
278
|
-
finalBufferChars: state.finalBufferChars,
|
|
279
|
-
sawDoneSentinel: state.sawDoneSentinel,
|
|
280
|
-
sawResponseCompleted: state.sawResponseCompleted,
|
|
281
|
-
},
|
|
282
|
-
outputItemSummary: state.outputItemSummary,
|
|
283
|
-
imageCallSeen: state.imageCallSeen,
|
|
284
|
-
imageCallCompleted: state.imageCallCompleted,
|
|
285
|
-
imageCallFailed: state.imageCallFailed,
|
|
286
|
-
imageResultCount: state.imageResultCount,
|
|
287
|
-
webSearchCallSeen: state.webSearchCallSeen,
|
|
288
|
-
messageOutputSeen: state.messageOutputSeen,
|
|
289
|
-
outputTextChars,
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function resultFromState(state: ParseState): ParsedResponsesResult {
|
|
294
|
-
const text = state.finalTextOutput ?? cleanTextOutput(state.textOutput);
|
|
295
|
-
return {
|
|
296
|
-
images: state.images,
|
|
297
|
-
usage: state.usage,
|
|
298
|
-
webSearchCalls: state.webSearchCalls,
|
|
299
|
-
eventCount: state.eventCount,
|
|
300
|
-
eventTypes: state.eventTypes,
|
|
301
|
-
extraIgnored: state.extraIgnored,
|
|
302
|
-
text,
|
|
303
|
-
diagnostics: diagnosticsFromState(state),
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
interface ParseStreamOptions {
|
|
308
|
-
requestId?: string | null;
|
|
309
|
-
scope: string;
|
|
310
|
-
maxImages?: number;
|
|
311
|
-
onPartialImage?: ((partial: { b64: string; index: number | null | undefined }) => void) | null;
|
|
312
|
-
onFinalImage?: FinalImageHandler | null;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function makeStreamError(message: string, code: string, eventCount: number, eventType: string): Error {
|
|
316
|
-
const err = new Error(message) as Error & { code?: string; upstreamCode?: string; status?: number; eventCount?: number; eventType?: string };
|
|
317
|
-
err.code = "RESPONSES_STREAM_ERROR";
|
|
318
|
-
err.upstreamCode = code;
|
|
319
|
-
err.status = 502;
|
|
320
|
-
err.eventCount = eventCount;
|
|
321
|
-
err.eventType = eventType;
|
|
322
|
-
Object.defineProperty(err, "ima2ResponsesError", { value: true });
|
|
323
|
-
return err;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
async function appendFinalImageFromItem(
|
|
327
|
-
state: ParseState,
|
|
328
|
-
item: SseItem,
|
|
329
|
-
maxImages: number,
|
|
330
|
-
requestId: string | null | undefined,
|
|
331
|
-
onFinalImage: FinalImageHandler | null,
|
|
332
|
-
): Promise<void> {
|
|
333
|
-
if (item.type !== "image_generation_call" || typeof item.result !== "string" || !item.result) return;
|
|
334
|
-
if (state.images.some((image) => image.b64 === item.result)) return;
|
|
335
|
-
if (state.images.length < maxImages) {
|
|
336
|
-
const image = {
|
|
337
|
-
b64: item.result,
|
|
338
|
-
revisedPrompt: typeof item.revised_prompt === "string" ? item.revised_prompt : null,
|
|
339
|
-
};
|
|
340
|
-
const index = state.images.length;
|
|
341
|
-
state.images.push(image);
|
|
342
|
-
if (requestId) setJobPhase(requestId, "decoding");
|
|
343
|
-
await onFinalImage?.(image, index);
|
|
344
|
-
} else {
|
|
345
|
-
state.extraIgnored++;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
export async function parseStream(res: Response, {
|
|
350
|
-
requestId,
|
|
351
|
-
scope,
|
|
352
|
-
maxImages = 1,
|
|
353
|
-
onPartialImage = null,
|
|
354
|
-
onFinalImage = null,
|
|
355
|
-
}: ParseStreamOptions): Promise<ParsedResponsesResult> {
|
|
356
|
-
const reader = res.body!.getReader();
|
|
357
|
-
const decoder = new TextDecoder();
|
|
358
|
-
const state = createState();
|
|
359
|
-
let buffer = "";
|
|
360
|
-
while (true) {
|
|
361
|
-
const { done, value } = await reader.read();
|
|
362
|
-
if (done) break;
|
|
363
|
-
state.chunkCount++;
|
|
364
|
-
state.bytesRead += value.byteLength;
|
|
365
|
-
state.maxChunkBytes = Math.max(state.maxChunkBytes, value.byteLength);
|
|
366
|
-
buffer += decoder.decode(value, { stream: true });
|
|
367
|
-
let next;
|
|
368
|
-
while ((next = nextSseBlock(buffer)) !== null) {
|
|
369
|
-
const block = next.block;
|
|
370
|
-
buffer = next.rest;
|
|
371
|
-
if (next.delimiter.includes("\r\n")) state.crlfBoundaryCount++;
|
|
372
|
-
else state.lfBoundaryCount++;
|
|
373
|
-
const eventData = extractSseData(block);
|
|
374
|
-
if (eventData === "[DONE]") {
|
|
375
|
-
state.sawDoneSentinel = true;
|
|
376
|
-
continue;
|
|
377
|
-
}
|
|
378
|
-
if (!eventData) continue;
|
|
379
|
-
let data: SseData;
|
|
380
|
-
try { data = JSON.parse(eventData); } catch { state.parseSkipCount++; continue; }
|
|
381
|
-
state.eventCount++;
|
|
382
|
-
const eventType = safeDiagnosticLabel(data.type, "_unknown") || "_unknown";
|
|
383
|
-
state.eventTypes[eventType] = (state.eventTypes[eventType] || 0) + 1;
|
|
384
|
-
const delta = extractTextDelta(data);
|
|
385
|
-
if (delta) state.textOutput += delta;
|
|
386
|
-
const finalText = extractFinalText(data);
|
|
387
|
-
if (finalText) state.finalTextOutput = finalText;
|
|
388
|
-
if (finalText) state.messageOutputSeen = true;
|
|
389
|
-
const partial = extractPartialImage(data);
|
|
390
|
-
if (partial && typeof onPartialImage === "function") onPartialImage(partial);
|
|
391
|
-
if (data.type === "response.output_item.done") recordOutputItem(state, data.type, data.item);
|
|
392
|
-
if (data.type === "response.output_item.done" && data.item?.type === "image_generation_call") {
|
|
393
|
-
await appendFinalImageFromItem(state, data.item, maxImages, requestId, onFinalImage);
|
|
394
|
-
}
|
|
395
|
-
if (data.type === "response.output_item.done" && data.item?.type === "web_search_call") state.webSearchCalls++;
|
|
396
|
-
if (data.type === "response.completed") {
|
|
397
|
-
state.sawResponseCompleted = true;
|
|
398
|
-
state.usage = data.response?.usage || null;
|
|
399
|
-
for (const item of data.response?.output || []) {
|
|
400
|
-
recordOutputItem(state, data.type, item);
|
|
401
|
-
await appendFinalImageFromItem(state, item, maxImages, requestId, onFinalImage);
|
|
402
|
-
}
|
|
403
|
-
const wsNum = data.response?.tool_usage?.web_search?.num_requests;
|
|
404
|
-
if (typeof wsNum === "number" && wsNum > state.webSearchCalls) state.webSearchCalls = wsNum;
|
|
405
|
-
}
|
|
406
|
-
if (data.type === "error") {
|
|
407
|
-
throw makeStreamError(
|
|
408
|
-
"Responses stream returned an error",
|
|
409
|
-
safeDiagnosticLabel(data.error?.code, "RESPONSES_STREAM_ERROR") || "RESPONSES_STREAM_ERROR",
|
|
410
|
-
state.eventCount,
|
|
411
|
-
eventType,
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
state.finalBufferChars = buffer.length;
|
|
417
|
-
logEvent(scope, "stream_end", {
|
|
418
|
-
requestId,
|
|
419
|
-
events: state.eventCount,
|
|
420
|
-
imageCount: state.images.length,
|
|
421
|
-
webSearchCalls: state.webSearchCalls,
|
|
422
|
-
imageCallSeen: state.imageCallSeen,
|
|
423
|
-
messageOutputSeen: state.messageOutputSeen,
|
|
424
|
-
bytesRead: state.bytesRead,
|
|
425
|
-
parseSkipCount: state.parseSkipCount,
|
|
426
|
-
});
|
|
427
|
-
return resultFromState(state);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
export async function parseJson(res: Response, maxImages: number): Promise<ParsedResponsesResult> {
|
|
431
|
-
const json = await res.json() as {
|
|
432
|
-
output?: SseItem[];
|
|
433
|
-
usage?: Record<string, number>;
|
|
434
|
-
};
|
|
435
|
-
const state = createState();
|
|
436
|
-
state.usage = json.usage || null;
|
|
437
|
-
for (const item of json.output || []) {
|
|
438
|
-
state.eventCount++;
|
|
439
|
-
state.eventTypes["json.output"] = (state.eventTypes["json.output"] || 0) + 1;
|
|
440
|
-
recordOutputItem(state, "json.output", item);
|
|
441
|
-
if (item.type === "image_generation_call" && item.result && state.images.length < maxImages) {
|
|
442
|
-
state.images.push({
|
|
443
|
-
b64: item.result,
|
|
444
|
-
revisedPrompt: typeof item.revised_prompt === "string" ? item.revised_prompt : null,
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
if (item.type === "web_search_call") state.webSearchCalls++;
|
|
448
|
-
const itemText = extractJsonItemText(item);
|
|
449
|
-
if (itemText) state.textOutput += `${state.textOutput ? "\n\n" : ""}${itemText}`;
|
|
450
|
-
}
|
|
451
|
-
return resultFromState(state);
|
|
452
|
-
}
|
package/lib/responsesTools.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export interface ImageGenOptions {
|
|
2
|
-
quality?: string;
|
|
3
|
-
size?: string;
|
|
4
|
-
moderation?: string;
|
|
5
|
-
partial_images?: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export type ResponseTool = { type: string; quality?: string; size?: string; moderation?: string; partial_images?: number };
|
|
9
|
-
export type ImageToolChoice = "required" | { type: "image_generation" };
|
|
10
|
-
|
|
11
|
-
export function tools(webSearchEnabled: boolean, imageOptions: ImageGenOptions): ResponseTool[] {
|
|
12
|
-
return [
|
|
13
|
-
...(webSearchEnabled ? [{ type: "web_search" }] : []),
|
|
14
|
-
{ type: "image_generation", ...imageOptions },
|
|
15
|
-
];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function toolTypes(requestTools: ResponseTool[]): string[] {
|
|
19
|
-
return requestTools.map((tool) => tool.type);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function imageToolChoice(forceImageTool: boolean): ImageToolChoice {
|
|
23
|
-
return forceImageTool ? { type: "image_generation" } : "required";
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function imageToolChoiceKind(choice: ImageToolChoice): "required" | "image_generation" {
|
|
27
|
-
return choice === "required" ? "required" : "image_generation";
|
|
28
|
-
}
|
package/lib/runtimeContext.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import type OpenAI from "openai";
|
|
2
|
-
import { config as runtimeConfigDefault } from "../config.js";
|
|
3
|
-
|
|
4
|
-
export type AppConfig = typeof runtimeConfigDefault;
|
|
5
|
-
export type ApiKeySource = "env" | "oauth" | "config" | "none" | undefined;
|
|
6
|
-
export type OAuthReadyState = "starting" | "ready" | "failed" | "disabled" | undefined;
|
|
7
|
-
|
|
8
|
-
export interface RuntimeContext {
|
|
9
|
-
apiKey: string | undefined;
|
|
10
|
-
apiKeySource: ApiKeySource;
|
|
11
|
-
config: AppConfig;
|
|
12
|
-
grokActualPort: number | undefined;
|
|
13
|
-
grokPort: number;
|
|
14
|
-
grokUrl: string;
|
|
15
|
-
hasApiKey: boolean;
|
|
16
|
-
oauthActualPort: number | undefined;
|
|
17
|
-
oauthPort: number;
|
|
18
|
-
oauthReadyPromise: Promise<void> | null;
|
|
19
|
-
oauthReadyState: OAuthReadyState;
|
|
20
|
-
oauthUrl: string;
|
|
21
|
-
openai: OpenAI | null;
|
|
22
|
-
packageVersion: string;
|
|
23
|
-
rootDir: string;
|
|
24
|
-
serverActualPort: number | undefined;
|
|
25
|
-
serverConfiguredPort: number;
|
|
26
|
-
serverUrl: string;
|
|
27
|
-
startedAt: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** A partial used during boot when only some fields are known, or by callers
|
|
31
|
-
* threading ctx through layered APIs (oauth/responses adapters). */
|
|
32
|
-
export type RuntimeContextOverrides = Partial<RuntimeContext>;
|
|
33
|
-
|
|
34
|
-
/** Looser ctx shape for route registration helpers and tests, where callers
|
|
35
|
-
* often pass minimal nested config fixtures. Behaviour-preserving under the
|
|
36
|
-
* current non-strict-null tsconfig. */
|
|
37
|
-
export type RouteRuntimeContext =
|
|
38
|
-
& Omit<Partial<RuntimeContext>, "config">
|
|
39
|
-
& { config?: { [K in keyof AppConfig]?: Partial<AppConfig[K]> } };
|
|
40
|
-
|
|
41
|
-
/** Normalize a possibly-Partial RouteRuntimeContext into a strict RuntimeContext.
|
|
42
|
-
*
|
|
43
|
-
* IMPORTANT: This MUTATES `ctx` in place and returns the same object (object
|
|
44
|
-
* identity preserved). The runtime mutates fields on the original ctx after
|
|
45
|
-
* route registration — e.g. `markOAuthReady()` flips `oauthReadyState`,
|
|
46
|
-
* `startServer()` sets `serverActualPort` once the listener binds. Snapshotting
|
|
47
|
-
* here would break those updates because deep code (like `waitForOAuthReady`)
|
|
48
|
-
* re-reads `ctx.oauthReadyState`.
|
|
49
|
-
*
|
|
50
|
-
* - Live fields (oauthReadyState, oauthReadyPromise, serverActualPort, openai,
|
|
51
|
-
* apiKey, ...) keep their original references via in-place fill.
|
|
52
|
-
* - Missing config nests are merged from `runtimeConfigDefault` per top-level
|
|
53
|
-
* key, so partial fixtures (e.g. `{ config: { storage: {...} } }`) still see
|
|
54
|
-
* real `oauth`/`ids`/`limits` defaults at deep call sites.
|
|
55
|
-
*
|
|
56
|
-
* Use this at the top of any function that crosses from `RouteRuntimeContext`
|
|
57
|
-
* into deep typed code. Per GPT Pro's P05 review: RouteRuntimeContext stays
|
|
58
|
-
* boundary-only; deep lib code should operate on strict RuntimeContext. */
|
|
59
|
-
export function requireRuntimeContext(ctx: RouteRuntimeContext | undefined): RuntimeContext {
|
|
60
|
-
const target = (ctx ?? {}) as RouteRuntimeContext & Record<string, unknown>;
|
|
61
|
-
target.config = mergeRuntimeConfig(target.config);
|
|
62
|
-
if (target.apiKey === undefined && Object.prototype.hasOwnProperty.call(target, "apiKey") === false) {
|
|
63
|
-
target.apiKey = undefined;
|
|
64
|
-
}
|
|
65
|
-
if (target.hasApiKey === undefined) target.hasApiKey = false;
|
|
66
|
-
if (target.grokPort === undefined) {
|
|
67
|
-
target.grokPort = (target.config as AppConfig).grokProvider?.proxyPort ?? 18645;
|
|
68
|
-
}
|
|
69
|
-
if (target.grokUrl === undefined) {
|
|
70
|
-
const grokCfg = (target.config as AppConfig).grokProvider;
|
|
71
|
-
const host = grokCfg?.proxyHost ?? "127.0.0.1";
|
|
72
|
-
const port = target.grokActualPort ?? target.grokPort ?? grokCfg?.proxyPort ?? 18645;
|
|
73
|
-
target.grokUrl = `http://${host}:${port}/v1`;
|
|
74
|
-
}
|
|
75
|
-
if (target.oauthPort === undefined) {
|
|
76
|
-
target.oauthPort = (target.config as AppConfig).oauth?.proxyPort ?? 11782;
|
|
77
|
-
}
|
|
78
|
-
if (target.oauthReadyPromise === undefined) target.oauthReadyPromise = null;
|
|
79
|
-
if (target.oauthUrl === undefined) {
|
|
80
|
-
target.oauthUrl = `http://127.0.0.1:${(target.config as AppConfig).oauth?.proxyPort ?? target.oauthPort ?? 11782}`;
|
|
81
|
-
}
|
|
82
|
-
if (target.openai === undefined) target.openai = null;
|
|
83
|
-
if (target.packageVersion === undefined) target.packageVersion = "0.0.0";
|
|
84
|
-
if (target.rootDir === undefined) target.rootDir = process.cwd();
|
|
85
|
-
if (target.serverConfiguredPort === undefined) {
|
|
86
|
-
target.serverConfiguredPort = (target.config as AppConfig).server?.port ?? 11783;
|
|
87
|
-
}
|
|
88
|
-
if (target.serverUrl === undefined) {
|
|
89
|
-
const port = target.serverActualPort ?? target.serverConfiguredPort ?? 11783;
|
|
90
|
-
target.serverUrl = `http://localhost:${port}`;
|
|
91
|
-
}
|
|
92
|
-
if (target.startedAt === undefined) target.startedAt = Date.now();
|
|
93
|
-
return target as unknown as RuntimeContext;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Per-top-level-key merge: caller's nested config keys win, missing nests
|
|
97
|
-
* fall back to `runtimeConfigDefault`. Avoids deep-clone snapshotting so
|
|
98
|
-
* callers can still observe live-mutated config values if they exist. */
|
|
99
|
-
function mergeRuntimeConfig(
|
|
100
|
-
partial: RouteRuntimeContext["config"] | undefined,
|
|
101
|
-
): AppConfig {
|
|
102
|
-
if (!partial) return runtimeConfigDefault;
|
|
103
|
-
const merged: Record<string, unknown> = {};
|
|
104
|
-
for (const k of Object.keys(runtimeConfigDefault) as Array<keyof AppConfig>) {
|
|
105
|
-
const fromPartial = (partial as Record<string, unknown>)[k as string];
|
|
106
|
-
if (fromPartial && typeof fromPartial === "object") {
|
|
107
|
-
merged[k as string] = {
|
|
108
|
-
...(runtimeConfigDefault[k] as object),
|
|
109
|
-
...(fromPartial as object),
|
|
110
|
-
};
|
|
111
|
-
} else {
|
|
112
|
-
merged[k as string] = runtimeConfigDefault[k];
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
for (const k of Object.keys(partial)) {
|
|
116
|
-
if (!(k in merged)) merged[k] = (partial as Record<string, unknown>)[k];
|
|
117
|
-
}
|
|
118
|
-
return merged as AppConfig;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** Stub-friendly default for tests. Do NOT use in production boot paths. */
|
|
122
|
-
export function createTestRuntimeContext(over: RuntimeContextOverrides = {}): RuntimeContext {
|
|
123
|
-
const now = Date.now();
|
|
124
|
-
const base: RuntimeContext = {
|
|
125
|
-
apiKey: undefined,
|
|
126
|
-
apiKeySource: undefined,
|
|
127
|
-
config: {} as AppConfig,
|
|
128
|
-
grokActualPort: undefined,
|
|
129
|
-
grokPort: 18645,
|
|
130
|
-
grokUrl: "http://127.0.0.1:18645/v1",
|
|
131
|
-
hasApiKey: false,
|
|
132
|
-
oauthActualPort: undefined,
|
|
133
|
-
oauthPort: 11782,
|
|
134
|
-
oauthReadyPromise: null,
|
|
135
|
-
oauthReadyState: undefined,
|
|
136
|
-
oauthUrl: "http://127.0.0.1:11782",
|
|
137
|
-
openai: null,
|
|
138
|
-
packageVersion: "0.0.0-test",
|
|
139
|
-
rootDir: process.cwd(),
|
|
140
|
-
serverActualPort: undefined,
|
|
141
|
-
serverConfiguredPort: 11783,
|
|
142
|
-
serverUrl: "http://127.0.0.1:11783",
|
|
143
|
-
startedAt: now,
|
|
144
|
-
};
|
|
145
|
-
return { ...base, ...over };
|
|
146
|
-
}
|