@midscene/core 1.17.5 → 1.17.7
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/dist/es/agent/agent-report-manager.mjs +1 -1
- package/dist/es/agent/agent-report-manager.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/ai-model/model-adapter/locate-prompt-order.mjs +80 -0
- package/dist/es/ai-model/model-adapter/locate-prompt-order.mjs.map +1 -0
- package/dist/es/ai-model/model-adapter/locate.mjs +2 -1
- package/dist/es/ai-model/model-adapter/locate.mjs.map +1 -1
- package/dist/es/ai-model/models/jev/locate.mjs +2 -12
- package/dist/es/ai-model/models/jev/locate.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/call-ai.mjs +4 -1
- package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/siwc/authorization.mjs +4 -3
- package/dist/es/ai-model/service-caller/openai/siwc/authorization.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/siwc/network.mjs +36 -0
- package/dist/es/ai-model/service-caller/openai/siwc/network.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/siwc/refresh.mjs +5 -3
- package/dist/es/ai-model/service-caller/openai/siwc/refresh.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/siwc/token-manager.mjs +7 -4
- package/dist/es/ai-model/service-caller/openai/siwc/token-manager.mjs.map +1 -1
- package/dist/es/ai-model/shared/json.mjs +6 -2
- package/dist/es/ai-model/shared/json.mjs.map +1 -1
- package/dist/es/ai-model/workflows/grounding/locate.mjs +1 -1
- package/dist/es/ai-model/workflows/grounding/locate.mjs.map +1 -1
- package/dist/es/ai-model/workflows/grounding/section-locate.mjs +1 -1
- package/dist/es/ai-model/workflows/grounding/section-locate.mjs.map +1 -1
- package/dist/es/ai-model/workflows/grounding/utils.mjs +8 -26
- package/dist/es/ai-model/workflows/grounding/utils.mjs.map +1 -1
- package/dist/es/dump/report-image-export.mjs +58 -0
- package/dist/es/dump/report-image-export.mjs.map +1 -0
- package/dist/es/dump/screenshot-store.mjs +5 -0
- package/dist/es/dump/screenshot-store.mjs.map +1 -1
- package/dist/es/report-cli.mjs +13 -29
- package/dist/es/report-cli.mjs.map +1 -1
- package/dist/es/report-generator.mjs +22 -8
- package/dist/es/report-generator.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/report-markdown.mjs +50 -53
- package/dist/es/report-markdown.mjs.map +1 -1
- package/dist/es/report.mjs +8 -24
- package/dist/es/report.mjs.map +1 -1
- package/dist/es/siwc.mjs +4 -0
- package/dist/es/task-runner.mjs +7 -2
- package/dist/es/task-runner.mjs.map +1 -1
- package/dist/es/utils.mjs +1 -1
- package/dist/lib/agent/agent-report-manager.js +1 -1
- package/dist/lib/agent/agent-report-manager.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/ai-model/model-adapter/locate-prompt-order.js +117 -0
- package/dist/lib/ai-model/model-adapter/locate-prompt-order.js.map +1 -0
- package/dist/lib/ai-model/model-adapter/locate.js +2 -1
- package/dist/lib/ai-model/model-adapter/locate.js.map +1 -1
- package/dist/lib/ai-model/models/jev/locate.js +2 -12
- package/dist/lib/ai-model/models/jev/locate.js.map +1 -1
- package/dist/lib/ai-model/service-caller/call-ai.js +4 -1
- package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/siwc/authorization.js +3 -2
- package/dist/lib/ai-model/service-caller/openai/siwc/authorization.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/siwc/network.js +73 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/network.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/siwc/refresh.js +4 -2
- package/dist/lib/ai-model/service-caller/openai/siwc/refresh.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/siwc/token-manager.js +7 -4
- package/dist/lib/ai-model/service-caller/openai/siwc/token-manager.js.map +1 -1
- package/dist/lib/ai-model/shared/json.js +6 -2
- package/dist/lib/ai-model/shared/json.js.map +1 -1
- package/dist/lib/ai-model/workflows/grounding/locate.js +1 -1
- package/dist/lib/ai-model/workflows/grounding/locate.js.map +1 -1
- package/dist/lib/ai-model/workflows/grounding/section-locate.js +1 -1
- package/dist/lib/ai-model/workflows/grounding/section-locate.js.map +1 -1
- package/dist/lib/ai-model/workflows/grounding/utils.js +8 -26
- package/dist/lib/ai-model/workflows/grounding/utils.js.map +1 -1
- package/dist/lib/dump/report-image-export.js +92 -0
- package/dist/lib/dump/report-image-export.js.map +1 -0
- package/dist/lib/dump/screenshot-store.js +5 -0
- package/dist/lib/dump/screenshot-store.js.map +1 -1
- package/dist/lib/report-cli.js +12 -28
- package/dist/lib/report-cli.js.map +1 -1
- package/dist/lib/report-generator.js +22 -8
- package/dist/lib/report-generator.js.map +1 -1
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/report-markdown.js +50 -53
- package/dist/lib/report-markdown.js.map +1 -1
- package/dist/lib/report.js +6 -22
- package/dist/lib/report.js.map +1 -1
- package/dist/lib/siwc.js +52 -0
- package/dist/lib/siwc.js.map +1 -0
- package/dist/lib/task-runner.js +7 -2
- package/dist/lib/task-runner.js.map +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/types/ai-model/model-adapter/locate-prompt-order.d.ts +24 -0
- package/dist/types/ai-model/model-adapter/locate-prompt-order.d.ts.map +1 -0
- package/dist/types/ai-model/model-adapter/locate.d.ts.map +1 -1
- package/dist/types/ai-model/model-adapter/types.d.ts +9 -3
- package/dist/types/ai-model/model-adapter/types.d.ts.map +1 -1
- package/dist/types/ai-model/models/jev/locate.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/siwc/authorization.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/siwc/network.d.ts +8 -0
- package/dist/types/ai-model/service-caller/openai/siwc/network.d.ts.map +1 -0
- package/dist/types/ai-model/service-caller/openai/siwc/refresh.d.ts +1 -1
- package/dist/types/ai-model/service-caller/openai/siwc/refresh.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/siwc/token-manager.d.ts +2 -1
- package/dist/types/ai-model/service-caller/openai/siwc/token-manager.d.ts.map +1 -1
- package/dist/types/ai-model/shared/json.d.ts +4 -15
- package/dist/types/ai-model/shared/json.d.ts.map +1 -1
- package/dist/types/ai-model/workflows/grounding/utils.d.ts +4 -9
- package/dist/types/ai-model/workflows/grounding/utils.d.ts.map +1 -1
- package/dist/types/dump/report-image-export.d.ts +12 -0
- package/dist/types/dump/report-image-export.d.ts.map +1 -0
- package/dist/types/dump/screenshot-store.d.ts +2 -0
- package/dist/types/dump/screenshot-store.d.ts.map +1 -1
- package/dist/types/report-cli.d.ts.map +1 -1
- package/dist/types/report-generator.d.ts +4 -2
- package/dist/types/report-generator.d.ts.map +1 -1
- package/dist/types/report-markdown.d.ts +4 -2
- package/dist/types/report-markdown.d.ts.map +1 -1
- package/dist/types/report.d.ts.map +1 -1
- package/dist/types/siwc.d.ts +5 -0
- package/dist/types/siwc.d.ts.map +1 -0
- package/dist/types/task-runner.d.ts.map +1 -1
- package/package.json +10 -2
|
@@ -58,7 +58,7 @@ class AgentReportManager {
|
|
|
58
58
|
report.session = session;
|
|
59
59
|
report.file = void 0;
|
|
60
60
|
}
|
|
61
|
-
report.generator.enableSourceSnapshots?.();
|
|
61
|
+
report.generator.enableSourceSnapshots?.(report.yamlScopeId);
|
|
62
62
|
}
|
|
63
63
|
async _createReportSource(scopeId, executionIds) {
|
|
64
64
|
return this.reportState.generator.createSourceSnapshot?.(scopeId, executionIds);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent/agent-report-manager.mjs","sources":["../../../src/agent/agent-report-manager.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, uuid } from '@midscene/shared/utils';\nimport { IS_REPORT_BUILD } from '../constants';\nimport { generateTestRunReportScriptTag } from '../dump/html-utils';\nimport {\n type ReportSession,\n currentReportSession,\n} from '../dump/report-session';\nimport {\n type IReportGenerator,\n ReportGenerator,\n assertReportGenerationOptions,\n nullReportGenerator,\n} from '../report-generator';\nimport type { TaskRunner } from '../task-runner';\nimport { WorkflowError } from '../test-runner/errors';\nimport type { WorkflowExecutionRecord } from '../test-runner/execution-record';\nimport { buildAgentTestRunReportDump } from '../test-runner/reporting/agent-report';\nimport { executionRecordsToReportInput } from '../test-runner/reporting/execution-record';\nimport {\n type AgentOpt,\n type ExecutionDump,\n ReportActionDump,\n type ReportMeta,\n} from '../types';\nimport { getVersion, reportHTMLContent } from '../utils';\nimport { getReportFileName } from './utils';\n\nconst warn = getDebug('agent', { console: true });\n\ntype ReportOptions = Pick<\n AgentOpt,\n | 'groupName'\n | 'groupDescription'\n | 'generateReport'\n | 'persistExecutionDump'\n | 'outputFormat'\n | 'autoPrintReportMsg'\n | 'reportAttributes'\n | 'reportFileName'\n | 'testId'\n>;\n\ninterface AgentReportContext {\n getOptions(): ReportOptions;\n getDeviceType(): ReportActionDump['deviceType'];\n isDestroyed(): boolean;\n isExternallyBusy(): boolean;\n}\n\ninterface AgentReportState {\n dump: ReportActionDump;\n file?: string | null;\n fileName?: string;\n generator: IReportGenerator;\n session?: ReportSession;\n finalization?: Promise<string | undefined>;\n lastExecution?: ExecutionDump;\n executionIndexByRunner: WeakMap<TaskRunner, number>;\n yamlScopeId: string;\n yamlRecords: WorkflowExecutionRecord[];\n activeOperations: Set<Promise<unknown>>;\n}\n\n/** Owns report state and persistence; execution and device ownership stay with Agent. */\nexport class AgentReportManager {\n private reportState: AgentReportState;\n private reportReset?: Promise<ReportActionDump>;\n private readonly reportByRunner = new WeakMap<TaskRunner, AgentReportState>();\n private dumpUpdateListeners: Array<\n (dump: string, executionDump?: ExecutionDump) => void\n > = [];\n\n constructor(private readonly context: AgentReportContext) {\n const opts = this.opts;\n const fileName =\n opts.reportFileName ??\n getReportFileName(opts.testId || context.getDeviceType() || 'web');\n this.reportState = this.createReportState(\n fileName,\n opts.reportAttributes?.['data-group-id'] === fileName,\n );\n }\n\n private get opts(): ReportOptions {\n return this.context.getOptions();\n }\n\n get dump(): ReportActionDump {\n return this.reportState.dump;\n }\n set dump(value: ReportActionDump) {\n this.reportState.dump = value;\n }\n\n get reportFile(): string | null | undefined {\n return this.reportState.file;\n }\n set reportFile(value: string | null | undefined) {\n this.reportState.file = value;\n }\n\n get reportFileName(): string | undefined {\n return this.reportState.fileName;\n }\n set reportFileName(value: string | undefined) {\n this.reportState.fileName = value;\n }\n\n /** @internal Runner snapshots retain immutable data and shared image files. */\n async _prepareForTestRunner(): Promise<void> {\n const report = this.reportState;\n const session = currentReportSession();\n if (session && session !== report.session) {\n if (report.session)\n throw new Error(\n 'An Agent cannot be shared by different Test report sessions. Await resetDump() between runs.',\n );\n this.assertReportIdle();\n await report.generator.finalize();\n const recorder = session.createRecorder(() => this.isReportBusy(report));\n // The previous standalone report owns its history. The Test stream starts\n // at acquisition, so previous script operations do not enter this run.\n this.clearDumpInMemory(report);\n report.generator = recorder;\n report.session = session;\n report.file = undefined;\n }\n report.generator.enableSourceSnapshots?.();\n }\n async _createReportSource(scopeId: string, executionIds?: readonly string[]) {\n return this.reportState.generator.createSourceSnapshot?.(\n scopeId,\n executionIds,\n );\n }\n\n /** @internal The Agent owns one report stream across root YAML invocations. */\n async _writeYamlExecutionReport(\n record: WorkflowExecutionRecord,\n ): Promise<string | undefined> {\n const report = this.reportState;\n const existingIndex = report.yamlRecords.findIndex(\n (entry) => entry.runId === record.runId,\n );\n const recordIndex =\n existingIndex < 0 ? report.yamlRecords.length : existingIndex;\n report.yamlRecords[recordIndex] = record;\n try {\n const path = await report.generator.writeRunnerReport?.(\n this.buildYamlReportInput(),\n report.yamlScopeId,\n this.getReportMeta(),\n );\n report.file = path ?? report.file;\n return path;\n } catch (error) {\n report.yamlRecords[recordIndex] = Object.freeze({\n ...record,\n status: 'failed',\n reportError: error,\n });\n throw error;\n }\n }\n\n private buildYamlReportInput() {\n const scopedRecords = this.reportState.yamlRecords.map((entry) => ({\n ...entry,\n ...(entry.execution\n ? {\n execution: {\n document: {\n ...entry.execution.document,\n reportScopeId: this.reportState.yamlScopeId,\n },\n cases: entry.execution.cases.map((outcome) => ({\n ...outcome,\n ...(outcome.run\n ? {\n run: {\n ...outcome.run,\n reportScopeId: this.reportState.yamlScopeId,\n },\n }\n : {}),\n ...(outcome.attempts\n ? {\n attempts: outcome.attempts.map((attempt) => ({\n ...attempt,\n reportScopeId: this.reportState.yamlScopeId,\n })),\n }\n : {}),\n })),\n },\n }\n : {}),\n }));\n return executionRecordsToReportInput(scopedRecords, {\n runId: this.reportState.yamlScopeId,\n });\n }\n\n private createEmptyDump(): ReportActionDump {\n return new ReportActionDump({\n sdkVersion: getVersion(),\n groupName: this.opts.groupName!,\n groupDescription: this.opts.groupDescription,\n executions: [],\n modelBriefs: [],\n deviceType: this.context.getDeviceType(),\n });\n }\n\n private createReportState(\n fileName: string,\n reuseExistingReport = false,\n ): AgentReportState {\n assertReportGenerationOptions(this.opts);\n const session = currentReportSession();\n const state: AgentReportState = {\n dump: this.createEmptyDump(),\n fileName,\n generator: session\n ? nullReportGenerator\n : ReportGenerator.create(fileName, {\n generateReport: this.opts.generateReport,\n persistExecutionDump: this.opts.persistExecutionDump,\n outputFormat: this.opts.outputFormat,\n autoPrintReportMsg: this.opts.autoPrintReportMsg,\n reuseExistingReport,\n }),\n executionIndexByRunner: new WeakMap(),\n yamlScopeId: uuid(),\n yamlRecords: [],\n activeOperations: new Set(),\n };\n if (session) {\n state.generator = session.createRecorder(() => this.isReportBusy(state));\n state.session = session;\n }\n return state;\n }\n\n private isReportBusy(report: AgentReportState): boolean {\n return report.activeOperations.size > 0 || this.context.isExternallyBusy();\n }\n\n private clearDumpInMemory(report = this.reportState): void {\n report.dump = this.createEmptyDump();\n report.executionIndexByRunner = new WeakMap();\n report.lastExecution = undefined;\n report.yamlRecords.length = 0;\n }\n\n private assertReportIdle(): void {\n assert(\n !this.reportState.activeOperations.size &&\n !this.context.isExternallyBusy(),\n 'Cannot reset or finalize a report while the Agent is executing. Await the active operation first.',\n );\n }\n\n assertReportWritable(): AgentReportState {\n const report = this.reportState;\n assert(\n !this.context.isDestroyed(),\n 'Cannot execute using a destroyed Agent.',\n );\n assert(\n !this.reportReset,\n 'Report reset is in progress. Await resetDump() first.',\n );\n assert(\n !report.finalization,\n 'The report has been finalized. Call and await resetDump() before executing again.',\n );\n return report;\n }\n\n async withReportOperation<T>(operation: () => Promise<T>): Promise<T> {\n const report = this.assertReportWritable();\n const pending = Promise.resolve().then(operation);\n report.activeOperations.add(pending);\n try {\n return await pending;\n } finally {\n report.activeOperations.delete(pending);\n }\n }\n\n /**\n * Finalize the preceding report and reset execution records for a new report.\n * Historical artifacts are retained; this Agent and its device stay connected.\n * Await this method before executing again. Concurrent resets share one reset.\n */\n async resetDump(): Promise<ReportActionDump> {\n assert(\n !this.context.isDestroyed(),\n 'Cannot reset a report using a destroyed Agent.',\n );\n if (this.reportReset) return this.reportReset;\n this.assertReportIdle();\n this.reportReset = (async () => {\n try {\n await this.finalizeReport();\n } catch (error) {\n // Failure belongs to the preceding report, not to the next write queue.\n warn(\n `Failed to finalize previous report; resetting dump: ${String(error)}`,\n );\n }\n assert(\n !this.context.isDestroyed(),\n 'Cannot reset a report using a destroyed Agent.',\n );\n const fileName = getReportFileName(\n this.opts.testId || this.context.getDeviceType() || 'web',\n );\n this.reportState = this.createReportState(fileName);\n return this.dump;\n })();\n try {\n return await this.reportReset;\n } finally {\n this.reportReset = undefined;\n }\n }\n\n /**\n * Seal the current report and release its in-memory records without\n * disconnecting the device. Export in-memory data before calling this method.\n * Repeated calls return the same result and never rewrite published artifacts.\n */\n async finalizeReport(): Promise<string | undefined> {\n const report = this.reportState;\n if (!report.finalization) {\n this.assertReportIdle();\n report.finalization = (async () => {\n await report.generator.flush();\n const path = await report.generator.finalize();\n report.file = path;\n this.clearDumpInMemory(report);\n return path;\n })();\n }\n return report.finalization;\n }\n\n appendExecutionDump(execution: ExecutionDump, runner?: TaskRunner) {\n assert(\n !this.reportState.finalization,\n 'Cannot append to a finalized report. Await resetDump() first.',\n );\n const currentDump = this.dump;\n if (runner) {\n const existingIndex = this.reportState.executionIndexByRunner.get(runner);\n if (existingIndex !== undefined) {\n currentDump.executions[existingIndex] = execution;\n return;\n }\n currentDump.executions.push(execution);\n this.reportState.executionIndexByRunner.set(\n runner,\n currentDump.executions.length - 1,\n );\n return;\n }\n currentDump.executions.push(execution);\n }\n\n dumpDataString(opt?: { inlineScreenshots?: boolean }) {\n // update dump info\n this.dump.groupName = this.opts.groupName!;\n this.dump.groupDescription = this.opts.groupDescription;\n // In browser environment, use inline screenshots since file system is not available\n if (ifInBrowser || opt?.inlineScreenshots) {\n return this.dump.serializeWithInlineScreenshots();\n }\n return this.dump.serialize();\n }\n\n reportHTMLString(getDumpString: () => string) {\n // Short-circuit at the call site because JavaScript evaluates function\n // arguments first. This avoids serializing the dump (including inline\n // screenshots) when the Report Viewer build does not need report HTML.\n if (IS_REPORT_BUILD) {\n return '';\n }\n\n // dumpDataString() handles browser environment with inline screenshots\n const dumpString = getDumpString();\n if (!this.reportState.yamlRecords.length)\n return reportHTMLContent(dumpString);\n\n // Browser exports have no file writer, so the execution record must own\n // the hierarchy independently of whether file report generation is enabled.\n const reportId = this.reportState.yamlScopeId;\n const runnerDump = buildAgentTestRunReportDump(\n this.buildYamlReportInput(),\n this.dump,\n {\n reportId,\n scopeId: this.reportState.yamlScopeId,\n sourcePath: this.reportFile ?? '<inline-agent-report>',\n },\n );\n return `${reportHTMLContent({\n dumpString,\n attributes: { 'data-group-id': reportId, 'data-report-id': reportId },\n })}\\n${generateTestRunReportScriptTag(runnerDump)}`;\n }\n\n writeOutActionDumps(executionDump?: ExecutionDump) {\n const report = this.reportState;\n if (report.finalization) {\n if (executionDump) {\n throw new WorkflowError(\n 'Cannot append to a finalized report. Await resetDump() first.',\n {\n code: 'REPORT_FINALIZED',\n },\n );\n }\n return;\n }\n const exec = executionDump || report.lastExecution;\n if (exec) {\n report.lastExecution = exec;\n report.generator.onExecutionUpdate(\n exec,\n this.getReportMeta(),\n this.opts.reportAttributes,\n );\n }\n report.file = report.generator.getReportPath();\n }\n\n /** Persist current report data without disposing this Agent or its device. */\n async flushReport(): Promise<string | undefined> {\n if (this.reportReset) await this.reportReset;\n const report = this.reportState;\n if (report.finalization) return report.finalization;\n this.writeOutActionDumps();\n await report.generator.flush();\n return report.lastExecution ? (report.file ?? undefined) : undefined;\n }\n\n private getReportMeta(): ReportMeta {\n return {\n groupName: this.dump.groupName,\n groupDescription: this.dump.groupDescription,\n sdkVersion: this.dump.sdkVersion,\n modelBriefs: this.dump.modelBriefs,\n deviceType: this.dump.deviceType,\n };\n }\n\n get onDumpUpdate():\n | ((dump: string, executionDump?: ExecutionDump) => void)\n | undefined {\n return this.dumpUpdateListeners[0];\n }\n\n set onDumpUpdate(callback:\n | ((dump: string, executionDump?: ExecutionDump) => void)\n | undefined) {\n // Clear existing listeners\n this.dumpUpdateListeners = [];\n // Add callback to array if provided\n if (callback) {\n this.dumpUpdateListeners.push(callback);\n }\n }\n\n addDumpUpdateListener(\n listener: (dump: string, executionDump?: ExecutionDump) => void,\n ): () => void {\n this.dumpUpdateListeners.push(listener);\n\n // Return remove function\n return () => {\n this.removeDumpUpdateListener(listener);\n };\n }\n\n /**\n * Remove a dump update listener\n * @param listener The listener function to remove\n */\n removeDumpUpdateListener(\n listener: (dump: string, executionDump?: ExecutionDump) => void,\n ): void {\n const index = this.dumpUpdateListeners.indexOf(listener);\n if (index > -1) {\n this.dumpUpdateListeners.splice(index, 1);\n }\n }\n\n /**\n * Clear all dump update listeners\n */\n clearDumpUpdateListeners(): void {\n this.dumpUpdateListeners = [];\n }\n\n notifyDumpUpdateListeners(executionDump?: ExecutionDump) {\n if (this.dumpUpdateListeners.length === 0) return;\n const dumpString = this.dumpDataString();\n for (const listener of this.dumpUpdateListeners) {\n try {\n listener(dumpString, executionDump);\n } catch (error) {\n console.error('Error in onDumpUpdate listener', error);\n }\n }\n }\n\n bindRunner(runner: TaskRunner): void {\n this.reportByRunner.set(runner, this.reportState);\n }\n\n assertRunnerCurrent(runner: TaskRunner): void {\n const report = this.reportByRunner.get(runner);\n assert(\n report === this.reportState && !report.finalization,\n 'Cannot write a late execution update into a different or finalized report.',\n );\n }\n\n async flushPendingWrites(): Promise<void> {\n await this.reportState.generator.flush();\n }\n\n async waitForOperations(): Promise<void> {\n while (this.reportState.activeOperations.size) {\n await Promise.allSettled([...this.reportState.activeOperations]);\n }\n }\n\n async waitForReset(): Promise<void> {\n if (this.reportReset) await this.reportReset.catch(() => {});\n }\n}\n"],"names":["warn","getDebug","AgentReportManager","value","report","session","currentReportSession","Error","recorder","undefined","scopeId","executionIds","record","existingIndex","entry","recordIndex","path","error","Object","scopedRecords","outcome","attempt","executionRecordsToReportInput","ReportActionDump","getVersion","fileName","reuseExistingReport","assertReportGenerationOptions","state","nullReportGenerator","ReportGenerator","WeakMap","uuid","Set","assert","operation","pending","Promise","String","getReportFileName","execution","runner","currentDump","opt","ifInBrowser","getDumpString","IS_REPORT_BUILD","dumpString","reportHTMLContent","reportId","runnerDump","buildAgentTestRunReportDump","generateTestRunReportScriptTag","executionDump","WorkflowError","exec","callback","listener","index","console","context","opts"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent/agent-report-manager.mjs","sources":["../../../src/agent/agent-report-manager.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser, uuid } from '@midscene/shared/utils';\nimport { IS_REPORT_BUILD } from '../constants';\nimport { generateTestRunReportScriptTag } from '../dump/html-utils';\nimport {\n type ReportSession,\n currentReportSession,\n} from '../dump/report-session';\nimport {\n type IReportGenerator,\n ReportGenerator,\n assertReportGenerationOptions,\n nullReportGenerator,\n} from '../report-generator';\nimport type { TaskRunner } from '../task-runner';\nimport { WorkflowError } from '../test-runner/errors';\nimport type { WorkflowExecutionRecord } from '../test-runner/execution-record';\nimport { buildAgentTestRunReportDump } from '../test-runner/reporting/agent-report';\nimport { executionRecordsToReportInput } from '../test-runner/reporting/execution-record';\nimport {\n type AgentOpt,\n type ExecutionDump,\n ReportActionDump,\n type ReportMeta,\n} from '../types';\nimport { getVersion, reportHTMLContent } from '../utils';\nimport { getReportFileName } from './utils';\n\nconst warn = getDebug('agent', { console: true });\n\ntype ReportOptions = Pick<\n AgentOpt,\n | 'groupName'\n | 'groupDescription'\n | 'generateReport'\n | 'persistExecutionDump'\n | 'outputFormat'\n | 'autoPrintReportMsg'\n | 'reportAttributes'\n | 'reportFileName'\n | 'testId'\n>;\n\ninterface AgentReportContext {\n getOptions(): ReportOptions;\n getDeviceType(): ReportActionDump['deviceType'];\n isDestroyed(): boolean;\n isExternallyBusy(): boolean;\n}\n\ninterface AgentReportState {\n dump: ReportActionDump;\n file?: string | null;\n fileName?: string;\n generator: IReportGenerator;\n session?: ReportSession;\n finalization?: Promise<string | undefined>;\n lastExecution?: ExecutionDump;\n executionIndexByRunner: WeakMap<TaskRunner, number>;\n yamlScopeId: string;\n yamlRecords: WorkflowExecutionRecord[];\n activeOperations: Set<Promise<unknown>>;\n}\n\n/** Owns report state and persistence; execution and device ownership stay with Agent. */\nexport class AgentReportManager {\n private reportState: AgentReportState;\n private reportReset?: Promise<ReportActionDump>;\n private readonly reportByRunner = new WeakMap<TaskRunner, AgentReportState>();\n private dumpUpdateListeners: Array<\n (dump: string, executionDump?: ExecutionDump) => void\n > = [];\n\n constructor(private readonly context: AgentReportContext) {\n const opts = this.opts;\n const fileName =\n opts.reportFileName ??\n getReportFileName(opts.testId || context.getDeviceType() || 'web');\n this.reportState = this.createReportState(\n fileName,\n opts.reportAttributes?.['data-group-id'] === fileName,\n );\n }\n\n private get opts(): ReportOptions {\n return this.context.getOptions();\n }\n\n get dump(): ReportActionDump {\n return this.reportState.dump;\n }\n set dump(value: ReportActionDump) {\n this.reportState.dump = value;\n }\n\n get reportFile(): string | null | undefined {\n return this.reportState.file;\n }\n set reportFile(value: string | null | undefined) {\n this.reportState.file = value;\n }\n\n get reportFileName(): string | undefined {\n return this.reportState.fileName;\n }\n set reportFileName(value: string | undefined) {\n this.reportState.fileName = value;\n }\n\n /** @internal Runner snapshots retain immutable data and shared image files. */\n async _prepareForTestRunner(): Promise<void> {\n const report = this.reportState;\n const session = currentReportSession();\n if (session && session !== report.session) {\n if (report.session)\n throw new Error(\n 'An Agent cannot be shared by different Test report sessions. Await resetDump() between runs.',\n );\n this.assertReportIdle();\n await report.generator.finalize();\n const recorder = session.createRecorder(() => this.isReportBusy(report));\n // The previous standalone report owns its history. The Test stream starts\n // at acquisition, so previous script operations do not enter this run.\n this.clearDumpInMemory(report);\n report.generator = recorder;\n report.session = session;\n report.file = undefined;\n }\n report.generator.enableSourceSnapshots?.(report.yamlScopeId);\n }\n async _createReportSource(scopeId: string, executionIds?: readonly string[]) {\n return this.reportState.generator.createSourceSnapshot?.(\n scopeId,\n executionIds,\n );\n }\n\n /** @internal The Agent owns one report stream across root YAML invocations. */\n async _writeYamlExecutionReport(\n record: WorkflowExecutionRecord,\n ): Promise<string | undefined> {\n const report = this.reportState;\n const existingIndex = report.yamlRecords.findIndex(\n (entry) => entry.runId === record.runId,\n );\n const recordIndex =\n existingIndex < 0 ? report.yamlRecords.length : existingIndex;\n report.yamlRecords[recordIndex] = record;\n try {\n const path = await report.generator.writeRunnerReport?.(\n this.buildYamlReportInput(),\n report.yamlScopeId,\n this.getReportMeta(),\n );\n report.file = path ?? report.file;\n return path;\n } catch (error) {\n report.yamlRecords[recordIndex] = Object.freeze({\n ...record,\n status: 'failed',\n reportError: error,\n });\n throw error;\n }\n }\n\n private buildYamlReportInput() {\n const scopedRecords = this.reportState.yamlRecords.map((entry) => ({\n ...entry,\n ...(entry.execution\n ? {\n execution: {\n document: {\n ...entry.execution.document,\n reportScopeId: this.reportState.yamlScopeId,\n },\n cases: entry.execution.cases.map((outcome) => ({\n ...outcome,\n ...(outcome.run\n ? {\n run: {\n ...outcome.run,\n reportScopeId: this.reportState.yamlScopeId,\n },\n }\n : {}),\n ...(outcome.attempts\n ? {\n attempts: outcome.attempts.map((attempt) => ({\n ...attempt,\n reportScopeId: this.reportState.yamlScopeId,\n })),\n }\n : {}),\n })),\n },\n }\n : {}),\n }));\n return executionRecordsToReportInput(scopedRecords, {\n runId: this.reportState.yamlScopeId,\n });\n }\n\n private createEmptyDump(): ReportActionDump {\n return new ReportActionDump({\n sdkVersion: getVersion(),\n groupName: this.opts.groupName!,\n groupDescription: this.opts.groupDescription,\n executions: [],\n modelBriefs: [],\n deviceType: this.context.getDeviceType(),\n });\n }\n\n private createReportState(\n fileName: string,\n reuseExistingReport = false,\n ): AgentReportState {\n assertReportGenerationOptions(this.opts);\n const session = currentReportSession();\n const state: AgentReportState = {\n dump: this.createEmptyDump(),\n fileName,\n generator: session\n ? nullReportGenerator\n : ReportGenerator.create(fileName, {\n generateReport: this.opts.generateReport,\n persistExecutionDump: this.opts.persistExecutionDump,\n outputFormat: this.opts.outputFormat,\n autoPrintReportMsg: this.opts.autoPrintReportMsg,\n reuseExistingReport,\n }),\n executionIndexByRunner: new WeakMap(),\n yamlScopeId: uuid(),\n yamlRecords: [],\n activeOperations: new Set(),\n };\n if (session) {\n state.generator = session.createRecorder(() => this.isReportBusy(state));\n state.session = session;\n }\n return state;\n }\n\n private isReportBusy(report: AgentReportState): boolean {\n return report.activeOperations.size > 0 || this.context.isExternallyBusy();\n }\n\n private clearDumpInMemory(report = this.reportState): void {\n report.dump = this.createEmptyDump();\n report.executionIndexByRunner = new WeakMap();\n report.lastExecution = undefined;\n report.yamlRecords.length = 0;\n }\n\n private assertReportIdle(): void {\n assert(\n !this.reportState.activeOperations.size &&\n !this.context.isExternallyBusy(),\n 'Cannot reset or finalize a report while the Agent is executing. Await the active operation first.',\n );\n }\n\n assertReportWritable(): AgentReportState {\n const report = this.reportState;\n assert(\n !this.context.isDestroyed(),\n 'Cannot execute using a destroyed Agent.',\n );\n assert(\n !this.reportReset,\n 'Report reset is in progress. Await resetDump() first.',\n );\n assert(\n !report.finalization,\n 'The report has been finalized. Call and await resetDump() before executing again.',\n );\n return report;\n }\n\n async withReportOperation<T>(operation: () => Promise<T>): Promise<T> {\n const report = this.assertReportWritable();\n const pending = Promise.resolve().then(operation);\n report.activeOperations.add(pending);\n try {\n return await pending;\n } finally {\n report.activeOperations.delete(pending);\n }\n }\n\n /**\n * Finalize the preceding report and reset execution records for a new report.\n * Historical artifacts are retained; this Agent and its device stay connected.\n * Await this method before executing again. Concurrent resets share one reset.\n */\n async resetDump(): Promise<ReportActionDump> {\n assert(\n !this.context.isDestroyed(),\n 'Cannot reset a report using a destroyed Agent.',\n );\n if (this.reportReset) return this.reportReset;\n this.assertReportIdle();\n this.reportReset = (async () => {\n try {\n await this.finalizeReport();\n } catch (error) {\n // Failure belongs to the preceding report, not to the next write queue.\n warn(\n `Failed to finalize previous report; resetting dump: ${String(error)}`,\n );\n }\n assert(\n !this.context.isDestroyed(),\n 'Cannot reset a report using a destroyed Agent.',\n );\n const fileName = getReportFileName(\n this.opts.testId || this.context.getDeviceType() || 'web',\n );\n this.reportState = this.createReportState(fileName);\n return this.dump;\n })();\n try {\n return await this.reportReset;\n } finally {\n this.reportReset = undefined;\n }\n }\n\n /**\n * Seal the current report and release its in-memory records without\n * disconnecting the device. Export in-memory data before calling this method.\n * Repeated calls return the same result and never rewrite published artifacts.\n */\n async finalizeReport(): Promise<string | undefined> {\n const report = this.reportState;\n if (!report.finalization) {\n this.assertReportIdle();\n report.finalization = (async () => {\n await report.generator.flush();\n const path = await report.generator.finalize();\n report.file = path;\n this.clearDumpInMemory(report);\n return path;\n })();\n }\n return report.finalization;\n }\n\n appendExecutionDump(execution: ExecutionDump, runner?: TaskRunner) {\n assert(\n !this.reportState.finalization,\n 'Cannot append to a finalized report. Await resetDump() first.',\n );\n const currentDump = this.dump;\n if (runner) {\n const existingIndex = this.reportState.executionIndexByRunner.get(runner);\n if (existingIndex !== undefined) {\n currentDump.executions[existingIndex] = execution;\n return;\n }\n currentDump.executions.push(execution);\n this.reportState.executionIndexByRunner.set(\n runner,\n currentDump.executions.length - 1,\n );\n return;\n }\n currentDump.executions.push(execution);\n }\n\n dumpDataString(opt?: { inlineScreenshots?: boolean }) {\n // update dump info\n this.dump.groupName = this.opts.groupName!;\n this.dump.groupDescription = this.opts.groupDescription;\n // In browser environment, use inline screenshots since file system is not available\n if (ifInBrowser || opt?.inlineScreenshots) {\n return this.dump.serializeWithInlineScreenshots();\n }\n return this.dump.serialize();\n }\n\n reportHTMLString(getDumpString: () => string) {\n // Short-circuit at the call site because JavaScript evaluates function\n // arguments first. This avoids serializing the dump (including inline\n // screenshots) when the Report Viewer build does not need report HTML.\n if (IS_REPORT_BUILD) {\n return '';\n }\n\n // dumpDataString() handles browser environment with inline screenshots\n const dumpString = getDumpString();\n if (!this.reportState.yamlRecords.length)\n return reportHTMLContent(dumpString);\n\n // Browser exports have no file writer, so the execution record must own\n // the hierarchy independently of whether file report generation is enabled.\n const reportId = this.reportState.yamlScopeId;\n const runnerDump = buildAgentTestRunReportDump(\n this.buildYamlReportInput(),\n this.dump,\n {\n reportId,\n scopeId: this.reportState.yamlScopeId,\n sourcePath: this.reportFile ?? '<inline-agent-report>',\n },\n );\n return `${reportHTMLContent({\n dumpString,\n attributes: { 'data-group-id': reportId, 'data-report-id': reportId },\n })}\\n${generateTestRunReportScriptTag(runnerDump)}`;\n }\n\n writeOutActionDumps(executionDump?: ExecutionDump) {\n const report = this.reportState;\n if (report.finalization) {\n if (executionDump) {\n throw new WorkflowError(\n 'Cannot append to a finalized report. Await resetDump() first.',\n {\n code: 'REPORT_FINALIZED',\n },\n );\n }\n return;\n }\n const exec = executionDump || report.lastExecution;\n if (exec) {\n report.lastExecution = exec;\n report.generator.onExecutionUpdate(\n exec,\n this.getReportMeta(),\n this.opts.reportAttributes,\n );\n }\n report.file = report.generator.getReportPath();\n }\n\n /** Persist current report data without disposing this Agent or its device. */\n async flushReport(): Promise<string | undefined> {\n if (this.reportReset) await this.reportReset;\n const report = this.reportState;\n if (report.finalization) return report.finalization;\n this.writeOutActionDumps();\n await report.generator.flush();\n return report.lastExecution ? (report.file ?? undefined) : undefined;\n }\n\n private getReportMeta(): ReportMeta {\n return {\n groupName: this.dump.groupName,\n groupDescription: this.dump.groupDescription,\n sdkVersion: this.dump.sdkVersion,\n modelBriefs: this.dump.modelBriefs,\n deviceType: this.dump.deviceType,\n };\n }\n\n get onDumpUpdate():\n | ((dump: string, executionDump?: ExecutionDump) => void)\n | undefined {\n return this.dumpUpdateListeners[0];\n }\n\n set onDumpUpdate(callback:\n | ((dump: string, executionDump?: ExecutionDump) => void)\n | undefined) {\n // Clear existing listeners\n this.dumpUpdateListeners = [];\n // Add callback to array if provided\n if (callback) {\n this.dumpUpdateListeners.push(callback);\n }\n }\n\n addDumpUpdateListener(\n listener: (dump: string, executionDump?: ExecutionDump) => void,\n ): () => void {\n this.dumpUpdateListeners.push(listener);\n\n // Return remove function\n return () => {\n this.removeDumpUpdateListener(listener);\n };\n }\n\n /**\n * Remove a dump update listener\n * @param listener The listener function to remove\n */\n removeDumpUpdateListener(\n listener: (dump: string, executionDump?: ExecutionDump) => void,\n ): void {\n const index = this.dumpUpdateListeners.indexOf(listener);\n if (index > -1) {\n this.dumpUpdateListeners.splice(index, 1);\n }\n }\n\n /**\n * Clear all dump update listeners\n */\n clearDumpUpdateListeners(): void {\n this.dumpUpdateListeners = [];\n }\n\n notifyDumpUpdateListeners(executionDump?: ExecutionDump) {\n if (this.dumpUpdateListeners.length === 0) return;\n const dumpString = this.dumpDataString();\n for (const listener of this.dumpUpdateListeners) {\n try {\n listener(dumpString, executionDump);\n } catch (error) {\n console.error('Error in onDumpUpdate listener', error);\n }\n }\n }\n\n bindRunner(runner: TaskRunner): void {\n this.reportByRunner.set(runner, this.reportState);\n }\n\n assertRunnerCurrent(runner: TaskRunner): void {\n const report = this.reportByRunner.get(runner);\n assert(\n report === this.reportState && !report.finalization,\n 'Cannot write a late execution update into a different or finalized report.',\n );\n }\n\n async flushPendingWrites(): Promise<void> {\n await this.reportState.generator.flush();\n }\n\n async waitForOperations(): Promise<void> {\n while (this.reportState.activeOperations.size) {\n await Promise.allSettled([...this.reportState.activeOperations]);\n }\n }\n\n async waitForReset(): Promise<void> {\n if (this.reportReset) await this.reportReset.catch(() => {});\n }\n}\n"],"names":["warn","getDebug","AgentReportManager","value","report","session","currentReportSession","Error","recorder","undefined","scopeId","executionIds","record","existingIndex","entry","recordIndex","path","error","Object","scopedRecords","outcome","attempt","executionRecordsToReportInput","ReportActionDump","getVersion","fileName","reuseExistingReport","assertReportGenerationOptions","state","nullReportGenerator","ReportGenerator","WeakMap","uuid","Set","assert","operation","pending","Promise","String","getReportFileName","execution","runner","currentDump","opt","ifInBrowser","getDumpString","IS_REPORT_BUILD","dumpString","reportHTMLContent","reportId","runnerDump","buildAgentTestRunReportDump","generateTestRunReportScriptTag","executionDump","WorkflowError","exec","callback","listener","index","console","context","opts"],"mappings":"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"}
|
package/dist/es/agent/utils.mjs
CHANGED
|
@@ -153,7 +153,7 @@ async function matchElementFromCache(context, cacheEntry, cachePrompt, cacheable
|
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
const getMidsceneVersion = ()=>"1.17.
|
|
156
|
+
const getMidsceneVersion = ()=>"1.17.7";
|
|
157
157
|
const parsePrompt = (prompt)=>{
|
|
158
158
|
if ('string' == typeof prompt) return {
|
|
159
159
|
textPrompt: prompt,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const parts = [
|
|
2
|
+
'instructions',
|
|
3
|
+
'screenshot',
|
|
4
|
+
'task',
|
|
5
|
+
'references'
|
|
6
|
+
];
|
|
7
|
+
function parseLocatePromptOrder(order = 'system(instructions):userTogether(screenshot:task):userEach(references)') {
|
|
8
|
+
const groups = order.split(/(?<=\)):/);
|
|
9
|
+
const remaining = new Set(parts);
|
|
10
|
+
function invalid(reason) {
|
|
11
|
+
return new Error(`Invalid locate promptOrder "${order}": ${reason}`);
|
|
12
|
+
}
|
|
13
|
+
const promptGroups = groups.map((group)=>{
|
|
14
|
+
const match = /^(system|userTogether|userEach)\(([^()]+)\)$/.exec(group);
|
|
15
|
+
if (!match) throw invalid(`expected system(...), userTogether(...) or userEach(...), got "${group}"`);
|
|
16
|
+
const groupParts = match[2].split(':');
|
|
17
|
+
for (const part of groupParts){
|
|
18
|
+
if (!remaining.delete(part)) throw invalid(`unknown or repeated part "${part}"`);
|
|
19
|
+
if ('system' === match[1] && ('screenshot' === part || 'references' === part)) throw invalid(`system cannot contain image part "${part}"`);
|
|
20
|
+
}
|
|
21
|
+
if ('system' === match[1]) return {
|
|
22
|
+
mode: 'system',
|
|
23
|
+
parts: groupParts
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
mode: match[1],
|
|
27
|
+
parts: groupParts
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
if (remaining.size) throw invalid(`missing ${[
|
|
31
|
+
...remaining
|
|
32
|
+
].join(', ')}`);
|
|
33
|
+
return {
|
|
34
|
+
groups: promptGroups
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function buildLocateMessages(order, input) {
|
|
38
|
+
const messages = [];
|
|
39
|
+
for (const group of order.groups){
|
|
40
|
+
if ('system' === group.mode) {
|
|
41
|
+
messages.push({
|
|
42
|
+
role: 'system',
|
|
43
|
+
content: group.parts.map((part)=>input[part]).join('\n\n')
|
|
44
|
+
});
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const expanded = group.parts.flatMap((part)=>{
|
|
48
|
+
if ('references' === part) return input.references;
|
|
49
|
+
const content = 'screenshot' === part ? [
|
|
50
|
+
{
|
|
51
|
+
type: 'image',
|
|
52
|
+
url: input.screenshot
|
|
53
|
+
}
|
|
54
|
+
] : input[part];
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
role: 'user',
|
|
58
|
+
content
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
});
|
|
62
|
+
if ('userEach' === group.mode) messages.push(...expanded);
|
|
63
|
+
else if (expanded.length) {
|
|
64
|
+
const content = expanded.flatMap((message)=>'string' == typeof message.content ? [
|
|
65
|
+
{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: message.content
|
|
68
|
+
}
|
|
69
|
+
] : message.content);
|
|
70
|
+
messages.push({
|
|
71
|
+
role: 'user',
|
|
72
|
+
content
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return messages;
|
|
77
|
+
}
|
|
78
|
+
export { buildLocateMessages, parseLocatePromptOrder };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=locate-prompt-order.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/model-adapter/locate-prompt-order.mjs","sources":["../../../../src/ai-model/model-adapter/locate-prompt-order.ts"],"sourcesContent":["import type {\n ConversationSystemMessage,\n ConversationUserMessage,\n MessageContent,\n} from '../service-caller/types';\n\nconst parts = ['instructions', 'screenshot', 'task', 'references'] as const;\ntype Part = (typeof parts)[number];\n\nexport type LocateMessages = (\n | ConversationSystemMessage\n | ConversationUserMessage\n)[];\n\ntype TextPart = Extract<Part, 'instructions' | 'task'>;\ntype PromptGroup =\n | { mode: 'system'; parts: TextPart[] }\n | { mode: 'userTogether' | 'userEach'; parts: Part[] };\n\nexport interface LocatePromptOrder {\n groups: readonly PromptGroup[];\n}\n\nexport function parseLocatePromptOrder(\n order = 'system(instructions):userTogether(screenshot:task):userEach(references)',\n): LocatePromptOrder {\n const groups = order.split(/(?<=\\)):/);\n const remaining = new Set<string>(parts);\n\n function invalid(reason: string): Error {\n return new Error(`Invalid locate promptOrder \"${order}\": ${reason}`);\n }\n\n const promptGroups = groups.map((group): PromptGroup => {\n const match = /^(system|userTogether|userEach)\\(([^()]+)\\)$/.exec(group);\n if (!match) {\n throw invalid(\n `expected system(...), userTogether(...) or userEach(...), got \"${group}\"`,\n );\n }\n const groupParts = match[2].split(':');\n for (const part of groupParts) {\n if (!remaining.delete(part)) {\n throw invalid(`unknown or repeated part \"${part}\"`);\n }\n if (\n match[1] === 'system' &&\n (part === 'screenshot' || part === 'references')\n ) {\n throw invalid(`system cannot contain image part \"${part}\"`);\n }\n }\n if (match[1] === 'system') {\n return { mode: 'system', parts: groupParts as TextPart[] };\n }\n return {\n mode: match[1] as 'userTogether' | 'userEach',\n parts: groupParts as Part[],\n };\n });\n if (remaining.size) {\n throw invalid(`missing ${[...remaining].join(', ')}`);\n }\n return { groups: promptGroups };\n}\n\nexport function buildLocateMessages(\n order: LocatePromptOrder,\n input: {\n instructions: string;\n screenshot: string;\n task: string;\n references: ConversationUserMessage[];\n },\n): LocateMessages {\n const messages: LocateMessages = [];\n for (const group of order.groups) {\n if (group.mode === 'system') {\n messages.push({\n role: 'system',\n content: group.parts.map((part) => input[part]).join('\\n\\n'),\n });\n continue;\n }\n const expanded = group.parts.flatMap((part): ConversationUserMessage[] => {\n if (part === 'references') {\n return input.references;\n }\n const content =\n part === 'screenshot'\n ? [{ type: 'image' as const, url: input.screenshot }]\n : input[part];\n return [{ role: 'user', content }];\n });\n if (group.mode === 'userEach') {\n messages.push(...expanded);\n } else if (expanded.length) {\n const content = expanded.flatMap((message): MessageContent[] =>\n typeof message.content === 'string'\n ? [{ type: 'text', text: message.content }]\n : message.content,\n );\n messages.push({ role: 'user', content });\n }\n }\n return messages;\n}\n"],"names":["parts","parseLocatePromptOrder","order","groups","remaining","Set","invalid","reason","Error","promptGroups","group","match","groupParts","part","buildLocateMessages","input","messages","expanded","content","message"],"mappings":"AAMA,MAAMA,QAAQ;IAAC;IAAgB;IAAc;IAAQ;CAAa;AAiB3D,SAASC,uBACdC,QAAQ,yEAAyE;IAEjF,MAAMC,SAASD,MAAM,KAAK,CAAC;IAC3B,MAAME,YAAY,IAAIC,IAAYL;IAElC,SAASM,QAAQC,MAAc;QAC7B,OAAO,IAAIC,MAAM,CAAC,4BAA4B,EAAEN,MAAM,GAAG,EAAEK,QAAQ;IACrE;IAEA,MAAME,eAAeN,OAAO,GAAG,CAAC,CAACO;QAC/B,MAAMC,QAAQ,+CAA+C,IAAI,CAACD;QAClE,IAAI,CAACC,OACH,MAAML,QACJ,CAAC,+DAA+D,EAAEI,MAAM,CAAC,CAAC;QAG9E,MAAME,aAAaD,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;QAClC,KAAK,MAAME,QAAQD,WAAY;YAC7B,IAAI,CAACR,UAAU,MAAM,CAACS,OACpB,MAAMP,QAAQ,CAAC,0BAA0B,EAAEO,KAAK,CAAC,CAAC;YAEpD,IACEF,AAAa,aAAbA,KAAK,CAAC,EAAE,IACPE,CAAAA,AAAS,iBAATA,QAAyBA,AAAS,iBAATA,IAAoB,GAE9C,MAAMP,QAAQ,CAAC,kCAAkC,EAAEO,KAAK,CAAC,CAAC;QAE9D;QACA,IAAIF,AAAa,aAAbA,KAAK,CAAC,EAAE,EACV,OAAO;YAAE,MAAM;YAAU,OAAOC;QAAyB;QAE3D,OAAO;YACL,MAAMD,KAAK,CAAC,EAAE;YACd,OAAOC;QACT;IACF;IACA,IAAIR,UAAU,IAAI,EAChB,MAAME,QAAQ,CAAC,QAAQ,EAAE;WAAIF;KAAU,CAAC,IAAI,CAAC,OAAO;IAEtD,OAAO;QAAE,QAAQK;IAAa;AAChC;AAEO,SAASK,oBACdZ,KAAwB,EACxBa,KAKC;IAED,MAAMC,WAA2B,EAAE;IACnC,KAAK,MAAMN,SAASR,MAAM,MAAM,CAAE;QAChC,IAAIQ,AAAe,aAAfA,MAAM,IAAI,EAAe;YAC3BM,SAAS,IAAI,CAAC;gBACZ,MAAM;gBACN,SAASN,MAAM,KAAK,CAAC,GAAG,CAAC,CAACG,OAASE,KAAK,CAACF,KAAK,EAAE,IAAI,CAAC;YACvD;YACA;QACF;QACA,MAAMI,WAAWP,MAAM,KAAK,CAAC,OAAO,CAAC,CAACG;YACpC,IAAIA,AAAS,iBAATA,MACF,OAAOE,MAAM,UAAU;YAEzB,MAAMG,UACJL,AAAS,iBAATA,OACI;gBAAC;oBAAE,MAAM;oBAAkB,KAAKE,MAAM,UAAU;gBAAC;aAAE,GACnDA,KAAK,CAACF,KAAK;YACjB,OAAO;gBAAC;oBAAE,MAAM;oBAAQK;gBAAQ;aAAE;QACpC;QACA,IAAIR,AAAe,eAAfA,MAAM,IAAI,EACZM,SAAS,IAAI,IAAIC;aACZ,IAAIA,SAAS,MAAM,EAAE;YAC1B,MAAMC,UAAUD,SAAS,OAAO,CAAC,CAACE,UAChC,AAA2B,YAA3B,OAAOA,QAAQ,OAAO,GAClB;oBAAC;wBAAE,MAAM;wBAAQ,MAAMA,QAAQ,OAAO;oBAAC;iBAAE,GACzCA,QAAQ,OAAO;YAErBH,SAAS,IAAI,CAAC;gBAAE,MAAM;gBAAQE;YAAQ;QACxC;IACF;IACA,OAAOF;AACT"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createLocateResultCodec } from "../shared/model-locate-result/factory.mjs";
|
|
2
2
|
import { resolvePlanningTapLocator } from "../workflows/grounding/planning-action-locate.mjs";
|
|
3
3
|
import { createDefaultElementProtocol, createDefaultSearchAreaProtocol } from "./default-locate-protocol.mjs";
|
|
4
|
+
import { parseLocatePromptOrder } from "./locate-prompt-order.mjs";
|
|
4
5
|
const defaultLocateResultFormatDefinition = {
|
|
5
6
|
coordinates: {
|
|
6
7
|
shape: 'bbox',
|
|
@@ -31,7 +32,7 @@ function resolveLocate(locate, resolvedCustomPlanner, protocolContext) {
|
|
|
31
32
|
const searchAreaResultCodec = searchAreaResultFormat ? createLocateResultCodec(searchAreaResultFormat) : void 0;
|
|
32
33
|
return {
|
|
33
34
|
kind: 'standard',
|
|
34
|
-
|
|
35
|
+
promptOrder: parseLocatePromptOrder(locate?.promptOrder),
|
|
35
36
|
element: {
|
|
36
37
|
protocol: elementProtocol,
|
|
37
38
|
resultCodec: elementResultCodec
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/model-adapter/locate.mjs","sources":["../../../../src/ai-model/model-adapter/locate.ts"],"sourcesContent":["import { createLocateResultCodec } from '../shared/model-locate-result/factory';\nimport type { LocateResultFormatDefinition } from '../shared/model-locate-result/types';\nimport { resolvePlanningTapLocator } from '../workflows/grounding/planning-action-locate';\nimport type { ResolvedCustomPlanningDefinition } from './custom-planning-types';\nimport {\n createDefaultElementProtocol,\n createDefaultSearchAreaProtocol,\n} from './default-locate-protocol';\nimport type {\n StandardLocateProtocolContext,\n StandardLocateProtocolDefinition,\n} from './locate-protocol';\nimport type { LocateAdapter, ModelAdapterDefinition } from './types';\n\nconst defaultLocateResultFormatDefinition: LocateResultFormatDefinition = {\n coordinates: { shape: 'bbox', order: 'xy', normalizedBy: 1000 },\n};\n\nconst resolveLocateProtocolDefinition = (\n protocolDefinition: StandardLocateProtocolDefinition,\n protocolContext: StandardLocateProtocolContext,\n) =>\n typeof protocolDefinition === 'function'\n ? protocolDefinition(protocolContext)\n : protocolDefinition;\n\nexport function resolveLocate(\n locate: ModelAdapterDefinition['locate'],\n resolvedCustomPlanner: ResolvedCustomPlanningDefinition | undefined,\n protocolContext: StandardLocateProtocolContext,\n): LocateAdapter {\n if (locate?.kind === 'custom') {\n let locateFn = locate.locateFn;\n\n if (!locateFn) {\n const planningTapLocator = locate.planningTapLocator;\n\n if (!planningTapLocator) {\n throw new Error(\n 'Custom locate definition requires either locateFn or planningTapLocator',\n );\n }\n\n if (!resolvedCustomPlanner) {\n throw new Error(\n 'Custom planning tap locator requires a custom planning planner definition',\n );\n }\n locateFn = resolvePlanningTapLocator(\n planningTapLocator,\n resolvedCustomPlanner,\n );\n }\n\n return {\n kind: 'custom',\n locateFn,\n };\n }\n\n const elementProtocol = resolveLocateProtocolDefinition(\n locate?.element?.protocol ?? createDefaultElementProtocol,\n protocolContext,\n );\n const elementResultFormat =\n locate?.element?.resultFormat ?? defaultLocateResultFormatDefinition;\n const elementResultCodec = createLocateResultCodec(elementResultFormat);\n const searchAreaProtocol =\n locate?.searchArea === false\n ? undefined\n : resolveLocateProtocolDefinition(\n locate?.searchArea?.protocol ?? createDefaultSearchAreaProtocol,\n protocolContext,\n );\n const searchAreaResultFormat =\n locate?.searchArea === false\n ? undefined\n : (locate?.searchArea?.resultFormat ?? elementResultFormat);\n const searchAreaResultCodec = searchAreaResultFormat\n ? createLocateResultCodec(searchAreaResultFormat)\n : undefined;\n\n return {\n kind: 'standard',\n
|
|
1
|
+
{"version":3,"file":"ai-model/model-adapter/locate.mjs","sources":["../../../../src/ai-model/model-adapter/locate.ts"],"sourcesContent":["import { createLocateResultCodec } from '../shared/model-locate-result/factory';\nimport type { LocateResultFormatDefinition } from '../shared/model-locate-result/types';\nimport { resolvePlanningTapLocator } from '../workflows/grounding/planning-action-locate';\nimport type { ResolvedCustomPlanningDefinition } from './custom-planning-types';\nimport {\n createDefaultElementProtocol,\n createDefaultSearchAreaProtocol,\n} from './default-locate-protocol';\nimport { parseLocatePromptOrder } from './locate-prompt-order';\nimport type {\n StandardLocateProtocolContext,\n StandardLocateProtocolDefinition,\n} from './locate-protocol';\nimport type { LocateAdapter, ModelAdapterDefinition } from './types';\n\nconst defaultLocateResultFormatDefinition: LocateResultFormatDefinition = {\n coordinates: { shape: 'bbox', order: 'xy', normalizedBy: 1000 },\n};\n\nconst resolveLocateProtocolDefinition = (\n protocolDefinition: StandardLocateProtocolDefinition,\n protocolContext: StandardLocateProtocolContext,\n) =>\n typeof protocolDefinition === 'function'\n ? protocolDefinition(protocolContext)\n : protocolDefinition;\n\nexport function resolveLocate(\n locate: ModelAdapterDefinition['locate'],\n resolvedCustomPlanner: ResolvedCustomPlanningDefinition | undefined,\n protocolContext: StandardLocateProtocolContext,\n): LocateAdapter {\n if (locate?.kind === 'custom') {\n let locateFn = locate.locateFn;\n\n if (!locateFn) {\n const planningTapLocator = locate.planningTapLocator;\n\n if (!planningTapLocator) {\n throw new Error(\n 'Custom locate definition requires either locateFn or planningTapLocator',\n );\n }\n\n if (!resolvedCustomPlanner) {\n throw new Error(\n 'Custom planning tap locator requires a custom planning planner definition',\n );\n }\n locateFn = resolvePlanningTapLocator(\n planningTapLocator,\n resolvedCustomPlanner,\n );\n }\n\n return {\n kind: 'custom',\n locateFn,\n };\n }\n\n const elementProtocol = resolveLocateProtocolDefinition(\n locate?.element?.protocol ?? createDefaultElementProtocol,\n protocolContext,\n );\n const elementResultFormat =\n locate?.element?.resultFormat ?? defaultLocateResultFormatDefinition;\n const elementResultCodec = createLocateResultCodec(elementResultFormat);\n const searchAreaProtocol =\n locate?.searchArea === false\n ? undefined\n : resolveLocateProtocolDefinition(\n locate?.searchArea?.protocol ?? createDefaultSearchAreaProtocol,\n protocolContext,\n );\n const searchAreaResultFormat =\n locate?.searchArea === false\n ? undefined\n : (locate?.searchArea?.resultFormat ?? elementResultFormat);\n const searchAreaResultCodec = searchAreaResultFormat\n ? createLocateResultCodec(searchAreaResultFormat)\n : undefined;\n\n return {\n kind: 'standard',\n promptOrder: parseLocatePromptOrder(locate?.promptOrder),\n element: {\n protocol: elementProtocol,\n resultCodec: elementResultCodec,\n },\n ...(searchAreaProtocol && searchAreaResultCodec\n ? {\n searchArea: {\n protocol: searchAreaProtocol,\n resultCodec: searchAreaResultCodec,\n },\n }\n : {}),\n };\n}\n"],"names":["defaultLocateResultFormatDefinition","resolveLocateProtocolDefinition","protocolDefinition","protocolContext","resolveLocate","locate","resolvedCustomPlanner","locateFn","planningTapLocator","Error","resolvePlanningTapLocator","elementProtocol","createDefaultElementProtocol","elementResultFormat","elementResultCodec","createLocateResultCodec","searchAreaProtocol","undefined","createDefaultSearchAreaProtocol","searchAreaResultFormat","searchAreaResultCodec","parseLocatePromptOrder"],"mappings":";;;;AAeA,MAAMA,sCAAoE;IACxE,aAAa;QAAE,OAAO;QAAQ,OAAO;QAAM,cAAc;IAAK;AAChE;AAEA,MAAMC,kCAAkC,CACtCC,oBACAC,kBAEA,AAA8B,cAA9B,OAAOD,qBACHA,mBAAmBC,mBACnBD;AAEC,SAASE,cACdC,MAAwC,EACxCC,qBAAmE,EACnEH,eAA8C;IAE9C,IAAIE,QAAQ,SAAS,UAAU;QAC7B,IAAIE,WAAWF,OAAO,QAAQ;QAE9B,IAAI,CAACE,UAAU;YACb,MAAMC,qBAAqBH,OAAO,kBAAkB;YAEpD,IAAI,CAACG,oBACH,MAAM,IAAIC,MACR;YAIJ,IAAI,CAACH,uBACH,MAAM,IAAIG,MACR;YAGJF,WAAWG,0BACTF,oBACAF;QAEJ;QAEA,OAAO;YACL,MAAM;YACNC;QACF;IACF;IAEA,MAAMI,kBAAkBV,gCACtBI,QAAQ,SAAS,YAAYO,8BAC7BT;IAEF,MAAMU,sBACJR,QAAQ,SAAS,gBAAgBL;IACnC,MAAMc,qBAAqBC,wBAAwBF;IACnD,MAAMG,qBACJX,QAAQ,eAAe,QACnBY,SACAhB,gCACEI,QAAQ,YAAY,YAAYa,iCAChCf;IAER,MAAMgB,yBACJd,QAAQ,eAAe,QACnBY,SACCZ,QAAQ,YAAY,gBAAgBQ;IAC3C,MAAMO,wBAAwBD,yBAC1BJ,wBAAwBI,0BACxBF;IAEJ,OAAO;QACL,MAAM;QACN,aAAaI,uBAAuBhB,QAAQ;QAC5C,SAAS;YACP,UAAUM;YACV,aAAaG;QACf;QACA,GAAIE,sBAAsBI,wBACtB;YACE,YAAY;gBACV,UAAUJ;gBACV,aAAaI;YACf;QACF,IACA,CAAC,CAAC;IACR;AACF"}
|
|
@@ -17,10 +17,6 @@ const jevLocate = async ({ targetElementDescription, locateImage, options })=>{
|
|
|
17
17
|
...references.images
|
|
18
18
|
},
|
|
19
19
|
questions: {
|
|
20
|
-
present: {
|
|
21
|
-
type: 'noul',
|
|
22
|
-
instructions: 'Is the target described in `target` present in `screenshot`? Reference images, if any, describe the target; they are not the screenshot.'
|
|
23
|
-
},
|
|
24
20
|
target: {
|
|
25
21
|
type: 'box',
|
|
26
22
|
instructions: 'The single target described in `target`, in `screenshot`.'
|
|
@@ -30,17 +26,11 @@ const jevLocate = async ({ targetElementDescription, locateImage, options })=>{
|
|
|
30
26
|
abortSignal: options.abortSignal
|
|
31
27
|
});
|
|
32
28
|
const { answers } = systemOneResponseSchema.parse(JSON.parse(response.content));
|
|
33
|
-
assert(
|
|
29
|
+
assert(answers.target?.type === 'box', 'Invalid Jev locate answer');
|
|
34
30
|
return {
|
|
35
31
|
rawResponse: response.content,
|
|
36
32
|
usage: response.usage,
|
|
37
|
-
|
|
38
|
-
locatedPixelResult: mapJevBox(answers.target.box, locateImage)
|
|
39
|
-
} : {
|
|
40
|
-
errors: [
|
|
41
|
-
'The target is not present in the screenshot'
|
|
42
|
-
]
|
|
43
|
-
}
|
|
33
|
+
locatedPixelResult: mapJevBox(answers.target.box, locateImage)
|
|
44
34
|
};
|
|
45
35
|
};
|
|
46
36
|
export { jevLocate };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/models/jev/locate.mjs","sources":["../../../../../src/ai-model/models/jev/locate.ts"],"sourcesContent":["import { userPromptToString } from '@/common';\nimport { assert } from '@midscene/shared/utils';\nimport { callSystemOneAI } from '../../service-caller';\nimport { systemOneResponseSchema } from '../../service-caller/system-one/schema';\nimport type { LocateFn } from '../../workflows/grounding/types';\nimport { mapJevBox, prepareJevImage, prepareReferences } from './image';\n\nexport const jevLocate: LocateFn = async ({\n targetElementDescription,\n locateImage,\n options,\n}) => {\n const image = await prepareJevImage(locateImage.imageBase64);\n const references = await prepareReferences(targetElementDescription);\n const target = userPromptToString(targetElementDescription);\n const response = await callSystemOneAI(\n {\n state: { target, references: references.references },\n images: { screenshot: image.imageBase64, ...references.images },\n questions: {\n
|
|
1
|
+
{"version":3,"file":"ai-model/models/jev/locate.mjs","sources":["../../../../../src/ai-model/models/jev/locate.ts"],"sourcesContent":["import { userPromptToString } from '@/common';\nimport { assert } from '@midscene/shared/utils';\nimport { callSystemOneAI } from '../../service-caller';\nimport { systemOneResponseSchema } from '../../service-caller/system-one/schema';\nimport type { LocateFn } from '../../workflows/grounding/types';\nimport { mapJevBox, prepareJevImage, prepareReferences } from './image';\n\nexport const jevLocate: LocateFn = async ({\n targetElementDescription,\n locateImage,\n options,\n}) => {\n const image = await prepareJevImage(locateImage.imageBase64);\n const references = await prepareReferences(targetElementDescription);\n const target = userPromptToString(targetElementDescription);\n const response = await callSystemOneAI(\n {\n state: { target, references: references.references },\n images: { screenshot: image.imageBase64, ...references.images },\n questions: {\n target: {\n type: 'box',\n instructions:\n 'The single target described in `target`, in `screenshot`.',\n },\n },\n },\n options.modelRuntime,\n { abortSignal: options.abortSignal },\n );\n const { answers } = systemOneResponseSchema.parse(\n JSON.parse(response.content),\n );\n assert(answers.target?.type === 'box', 'Invalid Jev locate answer');\n return {\n rawResponse: response.content,\n usage: response.usage,\n locatedPixelResult: mapJevBox(answers.target.box, locateImage),\n };\n};\n"],"names":["jevLocate","targetElementDescription","locateImage","options","image","prepareJevImage","references","prepareReferences","target","userPromptToString","response","callSystemOneAI","answers","systemOneResponseSchema","JSON","assert","mapJevBox"],"mappings":";;;;;AAOO,MAAMA,YAAsB,OAAO,EACxCC,wBAAwB,EACxBC,WAAW,EACXC,OAAO,EACR;IACC,MAAMC,QAAQ,MAAMC,gBAAgBH,YAAY,WAAW;IAC3D,MAAMI,aAAa,MAAMC,kBAAkBN;IAC3C,MAAMO,SAASC,mBAAmBR;IAClC,MAAMS,WAAW,MAAMC,gBACrB;QACE,OAAO;YAAEH;YAAQ,YAAYF,WAAW,UAAU;QAAC;QACnD,QAAQ;YAAE,YAAYF,MAAM,WAAW;YAAE,GAAGE,WAAW,MAAM;QAAC;QAC9D,WAAW;YACT,QAAQ;gBACN,MAAM;gBACN,cACE;YACJ;QACF;IACF,GACAH,QAAQ,YAAY,EACpB;QAAE,aAAaA,QAAQ,WAAW;IAAC;IAErC,MAAM,EAAES,OAAO,EAAE,GAAGC,wBAAwB,KAAK,CAC/CC,KAAK,KAAK,CAACJ,SAAS,OAAO;IAE7BK,OAAOH,QAAQ,MAAM,EAAE,SAAS,OAAO;IACvC,OAAO;QACL,aAAaF,SAAS,OAAO;QAC7B,OAAOA,SAAS,KAAK;QACrB,oBAAoBM,UAAUJ,QAAQ,MAAM,CAAC,GAAG,EAAEV;IACpD;AACF"}
|
|
@@ -128,7 +128,10 @@ async function callModelWithRetry(input) {
|
|
|
128
128
|
if (error instanceof SiwcTokenExpiredError) {
|
|
129
129
|
const refresh = async ()=>{
|
|
130
130
|
assert(config.openaiApiKey, 'Missing rejected SIWC access token');
|
|
131
|
-
await siwcTokenManager.refreshAccessToken(config.openaiApiKey
|
|
131
|
+
await siwcTokenManager.refreshAccessToken(config.openaiApiKey, {
|
|
132
|
+
httpProxy: config.httpProxy,
|
|
133
|
+
socksProxy: config.socksProxy
|
|
134
|
+
});
|
|
132
135
|
return prepareSiwcCall(input.modelRuntime, options);
|
|
133
136
|
};
|
|
134
137
|
const refreshed = options?.abortSignal ? await runWithAbortSignal(options.abortSignal, refresh) : await refresh();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/call-ai.mjs","sources":["../../../../src/ai-model/service-caller/call-ai.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert, uuid } from '@midscene/shared/utils';\nimport type { ModelRuntime } from '../models';\nimport { callCodex, prepareCodexCall } from './codex/call-codex';\nimport { isCodexAppServerProvider } from './codex/codex-app-server';\nimport {\n isModelCallRecordingEnabled,\n recordModelCallEvent,\n} from './model-call-recorder';\nimport { type PreparedOpenAIInput, callOpenAI } from './openai/call-openai';\nimport { prepareChatCompletion } from './openai/chat-completion/chat-completion';\nimport { prepareResponses } from './openai/responses/responses';\nimport {\n SiwcTokenExpiredError,\n prepareSiwcCall,\n siwcTokenManager,\n} from './openai/siwc';\nimport {\n buildRequestAbortSignal,\n resolveEffectiveTimeoutMs,\n runWithAbortSignal,\n waitForRetry,\n} from './request-timeout';\nimport {\n prepareSystemOne,\n callSystemOne as sendSystemOneRequest,\n} from './system-one';\nimport type { SystemOneRequest } from './system-one/schema';\nimport type { ModelCallMessages } from './types';\nimport type {\n AICallResult,\n CallAIOptions,\n ModelCallContext,\n ModelCallResult,\n} from './types';\nimport {\n appendAIRequestFailureSummary,\n buildUsageInfo,\n nextInternalCallId,\n normalizeRetryCount,\n toError,\n} from './utils';\n\nconst DEFAULT_MODEL_PROTOCOL = 'openai-chat';\n\nexport async function callGenerativeAI(\n messages: ModelCallMessages,\n originalModelRuntime: ModelRuntime,\n originalOptions?: CallAIOptions,\n): Promise<AICallResult> {\n originalOptions?.abortSignal?.throwIfAborted();\n const isStreaming = originalOptions?.stream === true;\n if (isStreaming) {\n // Explicit streaming is currently used only by recorder code generation.\n // Streaming can also be aggregated without onChunk, but then the caller\n // cannot consume incremental output, which is the purpose of this option.\n // Environment-enabled streaming supports that aggregation-only use case.\n // Once onChunk delivers a chunk, later errors are not retried to avoid\n // delivering duplicate content. Errors before the first chunk can retry.\n assert(\n typeof originalOptions?.onChunk === 'function',\n 'onChunk is required when stream: true is explicitly specified',\n );\n }\n\n // Resolve authentication constraints before protocol selection and validation.\n const { modelRuntime, options } = await prepareSiwcCall(\n originalModelRuntime,\n originalOptions,\n );\n\n const { config: modelConfig } = modelRuntime;\n return executeModelCall(modelRuntime, options, async () => {\n const input = { messages, modelRuntime, options };\n if (isCodexAppServerProvider(modelConfig.openaiBaseURL)) {\n return { kind: 'codex', input: prepareCodexCall(input) };\n }\n const protocol = modelConfig.protocol ?? DEFAULT_MODEL_PROTOCOL;\n\n assert(\n modelRuntime.adapter.supportedProtocols.includes(protocol),\n `Model adapter \"${modelConfig.modelFamily ?? 'default'}\" does not support protocol \"${protocol}\"`,\n );\n if (protocol === 'openai-responses') {\n return { kind: 'responses', input: await prepareResponses(input) };\n }\n return {\n kind: 'chat-completion',\n input: await prepareChatCompletion(input),\n };\n });\n}\n\nexport async function callSystemOneAI(\n request: SystemOneRequest,\n modelRuntime: ModelRuntime,\n options?: CallAIOptions,\n): Promise<AICallResult> {\n options?.abortSignal?.throwIfAborted();\n assert(!options?.stream, 'Jev does not support streaming output');\n assert(\n modelRuntime.config.modelFamily === 'jev',\n 'System One currently only supports the Jev model family in Midscene',\n );\n return executeModelCall(modelRuntime, options, async () => ({\n kind: 'system-one',\n input: await prepareSystemOne(modelRuntime.config, request),\n }));\n}\n\nasync function executeModelCall(\n modelRuntime: ModelRuntime,\n options: CallAIOptions | undefined,\n prepare: () => Promise<PreparedModelInput>,\n): Promise<AICallResult> {\n // Keep identifiers stable across all request attempts.\n const executionId = modelRuntime.executionId ?? `unscoped-${uuid()}`;\n const internalCallId = nextInternalCallId();\n const { config: modelConfig } = modelRuntime;\n\n const prepared = options?.abortSignal\n ? await runWithAbortSignal(options.abortSignal, prepare)\n : await prepare();\n\n const result = await callModelWithRetry({\n modelRuntime,\n options,\n executionId,\n internalCallId,\n prepared,\n });\n\n const {\n rawUsage,\n timeCost,\n totalTimeCost,\n retryCount,\n requestId,\n responseModelName,\n ...response\n } = result;\n\n const rawUsageInfo = buildUsageInfo({\n requestKind: prepared.kind,\n stream: response.isStreamed,\n usageData: rawUsage,\n timeCost,\n totalTimeCost,\n retryCount,\n requestId,\n responseModelName,\n modelName: modelConfig.modelName,\n modelDescription: modelConfig.modelDescription,\n slot: modelConfig.slot,\n internalCallId,\n });\n\n const usage = rawUsageInfo\n ? { ...rawUsageInfo, modelFallback: modelRuntime.modelFallback }\n : undefined;\n\n const debugProfileStats = getDebug('ai:profile:stats');\n const debugProfileDetail = getDebug('ai:profile:detail');\n debugProfileStats(\n `model, ${modelConfig.modelName}, mode, ${modelConfig.modelFamily || 'default'}, streaming, ${response.isStreamed}, prompt-tokens, ${usage?.prompt_tokens ?? ''}, completion-tokens, ${usage?.completion_tokens ?? ''}, total-tokens, ${usage?.total_tokens ?? ''}, cached-input, ${usage?.cached_input ?? ''}, cost-ms, ${timeCost ?? ''}, requestId, ${requestId ?? ''}, slot, ${modelConfig.slot}, configured-temperature, ${modelConfig.temperature ?? ''}`,\n );\n debugProfileDetail(`model usage detail: ${JSON.stringify(usage)}`);\n\n if (usage && modelRuntime.onUsage) {\n modelRuntime.onUsage(usage);\n }\n return { ...response, usage };\n}\n\ntype PreparedModelInput =\n | {\n kind: 'system-one';\n input: Awaited<ReturnType<typeof prepareSystemOne>>;\n }\n | { kind: 'codex'; input: ReturnType<typeof prepareCodexCall> }\n | PreparedOpenAIInput;\n\ntype ModelCallInput = {\n modelRuntime: ModelRuntime;\n options?: CallAIOptions;\n executionId: string;\n internalCallId: string;\n prepared: PreparedModelInput;\n};\n\n/** Carries the original failure across the retry boundary without replaying output. */\nclass NonRetryableModelCallError extends Error {\n readonly cause: Error;\n\n constructor(cause: Error) {\n super(cause.message, { cause });\n this.cause = cause;\n }\n}\n\nasync function callModelWithRetry(\n input: ModelCallInput,\n): Promise<ModelCallResult & { totalTimeCost: number; retryCount: number }> {\n const startTime = Date.now();\n const {\n modelRuntime: { config },\n options,\n } = input;\n const maxAttempts = normalizeRetryCount(config.retryCount) + 1;\n const retryInterval = config.retryInterval ?? 2000;\n const attemptErrors: Array<{ attempt: number; error: unknown }> = [];\n\n for (let attempt = 1; ; attempt++) {\n options?.abortSignal?.throwIfAborted();\n try {\n const result = await callModelOnce(input, attempt);\n return {\n ...result,\n totalTimeCost: Date.now() - startTime,\n retryCount: attempt - 1,\n };\n } catch (error) {\n options?.abortSignal?.throwIfAborted();\n if (error instanceof NonRetryableModelCallError) {\n throw error.cause;\n }\n // A rejected SIWC token gets one authentication retry, independently of\n // normal retryCount. Delivered chunks are rejected above before refreshing.\n if (error instanceof SiwcTokenExpiredError) {\n const refresh = async () => {\n assert(config.openaiApiKey, 'Missing rejected SIWC access token');\n await siwcTokenManager.refreshAccessToken(config.openaiApiKey);\n return prepareSiwcCall(input.modelRuntime, options);\n };\n // Cancel only this caller's wait; other callers may share the refresh.\n const refreshed = options?.abortSignal\n ? await runWithAbortSignal(options.abortSignal, refresh)\n : await refresh();\n options?.abortSignal?.throwIfAborted();\n try {\n const result = await callModelOnce(\n { ...input, ...refreshed },\n attempt + 1,\n );\n return {\n ...result,\n totalTimeCost: Date.now() - startTime,\n retryCount: attempt,\n };\n } catch (retryError) {\n // Do not re-enter either retry path after the refreshed request fails.\n throw retryError instanceof NonRetryableModelCallError\n ? retryError.cause\n : retryError;\n }\n }\n const lastError = toError(error);\n attemptErrors.push({ attempt, error: lastError });\n if (attempt === maxAttempts) {\n throw appendAIRequestFailureSummary(\n lastError,\n attemptErrors,\n maxAttempts,\n );\n }\n getDebug('ai:call', {\n console: true,\n warning: {\n kind: 'model-request-retry',\n modelName: config.modelName,\n error: lastError,\n callId: input.internalCallId,\n attempt,\n maxAttempts,\n delayMs: retryInterval,\n },\n })(\n `AI call failed (attempt ${attempt}/${maxAttempts}), retrying in ${retryInterval}ms... Error: ${lastError.message}`,\n );\n }\n await waitForRetry(retryInterval, options?.abortSignal);\n }\n}\n\nasync function callModelOnce(\n {\n modelRuntime,\n options,\n executionId,\n internalCallId,\n prepared,\n }: ModelCallInput,\n attempt: number,\n): Promise<ModelCallResult> {\n const { config: modelConfig } = modelRuntime;\n const recordingEnabled = isModelCallRecordingEnabled();\n const recordEvent =\n recordingEnabled || modelRuntime.onRequest\n ? (event: Record<string, unknown>) => {\n if (event.type === 'request') {\n const request = event.request as { url?: string } | undefined;\n modelRuntime.onRequest?.(request?.url);\n }\n if (!recordingEnabled) return;\n void recordModelCallEvent({\n executionId,\n callId: internalCallId,\n semanticRetryAttempt: options?.semanticRetryAttempt,\n slot: modelConfig.slot,\n intent: modelConfig.intent,\n modelFamily: modelConfig.modelFamily,\n modelFallback: modelRuntime.modelFallback,\n ...event,\n attempt,\n });\n }\n : undefined;\n const effectiveTimeoutMs = resolveEffectiveTimeoutMs(modelConfig.timeout);\n const { signal: requestSignal, cleanup } = buildRequestAbortSignal(\n effectiveTimeoutMs,\n options?.abortSignal,\n );\n let deliveredChunk = false;\n const onChunk = options?.onChunk;\n const context: ModelCallContext = {\n modelRuntime,\n executionId,\n recordEvent,\n requestSignal,\n effectiveTimeoutMs,\n options: onChunk\n ? {\n ...options,\n onChunk: (chunk) => {\n requestSignal.throwIfAborted();\n deliveredChunk = true;\n onChunk(chunk);\n },\n }\n : options,\n };\n try {\n return await runWithAbortSignal(requestSignal, () =>\n prepared.kind === 'system-one'\n ? sendSystemOneRequest(context, prepared.input)\n : prepared.kind === 'codex'\n ? callCodex(context, prepared.input)\n : callOpenAI(context, prepared),\n );\n } catch (error) {\n options?.abortSignal?.throwIfAborted();\n const lastError = toError(error);\n if (deliveredChunk) {\n throw new NonRetryableModelCallError(lastError);\n }\n throw lastError;\n } finally {\n cleanup();\n }\n}\n"],"names":["DEFAULT_MODEL_PROTOCOL","callGenerativeAI","messages","originalModelRuntime","originalOptions","isStreaming","assert","modelRuntime","options","prepareSiwcCall","modelConfig","executeModelCall","input","isCodexAppServerProvider","prepareCodexCall","protocol","prepareResponses","prepareChatCompletion","callSystemOneAI","request","prepareSystemOne","prepare","executionId","uuid","internalCallId","nextInternalCallId","prepared","runWithAbortSignal","result","callModelWithRetry","rawUsage","timeCost","totalTimeCost","retryCount","requestId","responseModelName","response","rawUsageInfo","buildUsageInfo","usage","undefined","debugProfileStats","getDebug","debugProfileDetail","JSON","NonRetryableModelCallError","Error","cause","startTime","Date","config","maxAttempts","normalizeRetryCount","retryInterval","attemptErrors","attempt","callModelOnce","error","SiwcTokenExpiredError","refresh","siwcTokenManager","refreshed","retryError","lastError","toError","appendAIRequestFailureSummary","waitForRetry","recordingEnabled","isModelCallRecordingEnabled","recordEvent","event","recordModelCallEvent","effectiveTimeoutMs","resolveEffectiveTimeoutMs","requestSignal","cleanup","buildRequestAbortSignal","deliveredChunk","onChunk","context","chunk","sendSystemOneRequest","callCodex","callOpenAI"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/call-ai.mjs","sources":["../../../../src/ai-model/service-caller/call-ai.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert, uuid } from '@midscene/shared/utils';\nimport type { ModelRuntime } from '../models';\nimport { callCodex, prepareCodexCall } from './codex/call-codex';\nimport { isCodexAppServerProvider } from './codex/codex-app-server';\nimport {\n isModelCallRecordingEnabled,\n recordModelCallEvent,\n} from './model-call-recorder';\nimport { type PreparedOpenAIInput, callOpenAI } from './openai/call-openai';\nimport { prepareChatCompletion } from './openai/chat-completion/chat-completion';\nimport { prepareResponses } from './openai/responses/responses';\nimport {\n SiwcTokenExpiredError,\n prepareSiwcCall,\n siwcTokenManager,\n} from './openai/siwc';\nimport {\n buildRequestAbortSignal,\n resolveEffectiveTimeoutMs,\n runWithAbortSignal,\n waitForRetry,\n} from './request-timeout';\nimport {\n prepareSystemOne,\n callSystemOne as sendSystemOneRequest,\n} from './system-one';\nimport type { SystemOneRequest } from './system-one/schema';\nimport type { ModelCallMessages } from './types';\nimport type {\n AICallResult,\n CallAIOptions,\n ModelCallContext,\n ModelCallResult,\n} from './types';\nimport {\n appendAIRequestFailureSummary,\n buildUsageInfo,\n nextInternalCallId,\n normalizeRetryCount,\n toError,\n} from './utils';\n\nconst DEFAULT_MODEL_PROTOCOL = 'openai-chat';\n\nexport async function callGenerativeAI(\n messages: ModelCallMessages,\n originalModelRuntime: ModelRuntime,\n originalOptions?: CallAIOptions,\n): Promise<AICallResult> {\n originalOptions?.abortSignal?.throwIfAborted();\n const isStreaming = originalOptions?.stream === true;\n if (isStreaming) {\n // Explicit streaming is currently used only by recorder code generation.\n // Streaming can also be aggregated without onChunk, but then the caller\n // cannot consume incremental output, which is the purpose of this option.\n // Environment-enabled streaming supports that aggregation-only use case.\n // Once onChunk delivers a chunk, later errors are not retried to avoid\n // delivering duplicate content. Errors before the first chunk can retry.\n assert(\n typeof originalOptions?.onChunk === 'function',\n 'onChunk is required when stream: true is explicitly specified',\n );\n }\n\n // Resolve authentication constraints before protocol selection and validation.\n const { modelRuntime, options } = await prepareSiwcCall(\n originalModelRuntime,\n originalOptions,\n );\n\n const { config: modelConfig } = modelRuntime;\n return executeModelCall(modelRuntime, options, async () => {\n const input = { messages, modelRuntime, options };\n if (isCodexAppServerProvider(modelConfig.openaiBaseURL)) {\n return { kind: 'codex', input: prepareCodexCall(input) };\n }\n const protocol = modelConfig.protocol ?? DEFAULT_MODEL_PROTOCOL;\n\n assert(\n modelRuntime.adapter.supportedProtocols.includes(protocol),\n `Model adapter \"${modelConfig.modelFamily ?? 'default'}\" does not support protocol \"${protocol}\"`,\n );\n if (protocol === 'openai-responses') {\n return { kind: 'responses', input: await prepareResponses(input) };\n }\n return {\n kind: 'chat-completion',\n input: await prepareChatCompletion(input),\n };\n });\n}\n\nexport async function callSystemOneAI(\n request: SystemOneRequest,\n modelRuntime: ModelRuntime,\n options?: CallAIOptions,\n): Promise<AICallResult> {\n options?.abortSignal?.throwIfAborted();\n assert(!options?.stream, 'Jev does not support streaming output');\n assert(\n modelRuntime.config.modelFamily === 'jev',\n 'System One currently only supports the Jev model family in Midscene',\n );\n return executeModelCall(modelRuntime, options, async () => ({\n kind: 'system-one',\n input: await prepareSystemOne(modelRuntime.config, request),\n }));\n}\n\nasync function executeModelCall(\n modelRuntime: ModelRuntime,\n options: CallAIOptions | undefined,\n prepare: () => Promise<PreparedModelInput>,\n): Promise<AICallResult> {\n // Keep identifiers stable across all request attempts.\n const executionId = modelRuntime.executionId ?? `unscoped-${uuid()}`;\n const internalCallId = nextInternalCallId();\n const { config: modelConfig } = modelRuntime;\n\n const prepared = options?.abortSignal\n ? await runWithAbortSignal(options.abortSignal, prepare)\n : await prepare();\n\n const result = await callModelWithRetry({\n modelRuntime,\n options,\n executionId,\n internalCallId,\n prepared,\n });\n\n const {\n rawUsage,\n timeCost,\n totalTimeCost,\n retryCount,\n requestId,\n responseModelName,\n ...response\n } = result;\n\n const rawUsageInfo = buildUsageInfo({\n requestKind: prepared.kind,\n stream: response.isStreamed,\n usageData: rawUsage,\n timeCost,\n totalTimeCost,\n retryCount,\n requestId,\n responseModelName,\n modelName: modelConfig.modelName,\n modelDescription: modelConfig.modelDescription,\n slot: modelConfig.slot,\n internalCallId,\n });\n\n const usage = rawUsageInfo\n ? { ...rawUsageInfo, modelFallback: modelRuntime.modelFallback }\n : undefined;\n\n const debugProfileStats = getDebug('ai:profile:stats');\n const debugProfileDetail = getDebug('ai:profile:detail');\n debugProfileStats(\n `model, ${modelConfig.modelName}, mode, ${modelConfig.modelFamily || 'default'}, streaming, ${response.isStreamed}, prompt-tokens, ${usage?.prompt_tokens ?? ''}, completion-tokens, ${usage?.completion_tokens ?? ''}, total-tokens, ${usage?.total_tokens ?? ''}, cached-input, ${usage?.cached_input ?? ''}, cost-ms, ${timeCost ?? ''}, requestId, ${requestId ?? ''}, slot, ${modelConfig.slot}, configured-temperature, ${modelConfig.temperature ?? ''}`,\n );\n debugProfileDetail(`model usage detail: ${JSON.stringify(usage)}`);\n\n if (usage && modelRuntime.onUsage) {\n modelRuntime.onUsage(usage);\n }\n return { ...response, usage };\n}\n\ntype PreparedModelInput =\n | {\n kind: 'system-one';\n input: Awaited<ReturnType<typeof prepareSystemOne>>;\n }\n | { kind: 'codex'; input: ReturnType<typeof prepareCodexCall> }\n | PreparedOpenAIInput;\n\ntype ModelCallInput = {\n modelRuntime: ModelRuntime;\n options?: CallAIOptions;\n executionId: string;\n internalCallId: string;\n prepared: PreparedModelInput;\n};\n\n/** Carries the original failure across the retry boundary without replaying output. */\nclass NonRetryableModelCallError extends Error {\n readonly cause: Error;\n\n constructor(cause: Error) {\n super(cause.message, { cause });\n this.cause = cause;\n }\n}\n\nasync function callModelWithRetry(\n input: ModelCallInput,\n): Promise<ModelCallResult & { totalTimeCost: number; retryCount: number }> {\n const startTime = Date.now();\n const {\n modelRuntime: { config },\n options,\n } = input;\n const maxAttempts = normalizeRetryCount(config.retryCount) + 1;\n const retryInterval = config.retryInterval ?? 2000;\n const attemptErrors: Array<{ attempt: number; error: unknown }> = [];\n\n for (let attempt = 1; ; attempt++) {\n options?.abortSignal?.throwIfAborted();\n try {\n const result = await callModelOnce(input, attempt);\n return {\n ...result,\n totalTimeCost: Date.now() - startTime,\n retryCount: attempt - 1,\n };\n } catch (error) {\n options?.abortSignal?.throwIfAborted();\n if (error instanceof NonRetryableModelCallError) {\n throw error.cause;\n }\n // A rejected SIWC token gets one authentication retry, independently of\n // normal retryCount. Delivered chunks are rejected above before refreshing.\n if (error instanceof SiwcTokenExpiredError) {\n const refresh = async () => {\n assert(config.openaiApiKey, 'Missing rejected SIWC access token');\n await siwcTokenManager.refreshAccessToken(config.openaiApiKey, {\n httpProxy: config.httpProxy,\n socksProxy: config.socksProxy,\n });\n return prepareSiwcCall(input.modelRuntime, options);\n };\n // Cancel only this caller's wait; other callers may share the refresh.\n const refreshed = options?.abortSignal\n ? await runWithAbortSignal(options.abortSignal, refresh)\n : await refresh();\n options?.abortSignal?.throwIfAborted();\n try {\n const result = await callModelOnce(\n { ...input, ...refreshed },\n attempt + 1,\n );\n return {\n ...result,\n totalTimeCost: Date.now() - startTime,\n retryCount: attempt,\n };\n } catch (retryError) {\n // Do not re-enter either retry path after the refreshed request fails.\n throw retryError instanceof NonRetryableModelCallError\n ? retryError.cause\n : retryError;\n }\n }\n const lastError = toError(error);\n attemptErrors.push({ attempt, error: lastError });\n if (attempt === maxAttempts) {\n throw appendAIRequestFailureSummary(\n lastError,\n attemptErrors,\n maxAttempts,\n );\n }\n getDebug('ai:call', {\n console: true,\n warning: {\n kind: 'model-request-retry',\n modelName: config.modelName,\n error: lastError,\n callId: input.internalCallId,\n attempt,\n maxAttempts,\n delayMs: retryInterval,\n },\n })(\n `AI call failed (attempt ${attempt}/${maxAttempts}), retrying in ${retryInterval}ms... Error: ${lastError.message}`,\n );\n }\n await waitForRetry(retryInterval, options?.abortSignal);\n }\n}\n\nasync function callModelOnce(\n {\n modelRuntime,\n options,\n executionId,\n internalCallId,\n prepared,\n }: ModelCallInput,\n attempt: number,\n): Promise<ModelCallResult> {\n const { config: modelConfig } = modelRuntime;\n const recordingEnabled = isModelCallRecordingEnabled();\n const recordEvent =\n recordingEnabled || modelRuntime.onRequest\n ? (event: Record<string, unknown>) => {\n if (event.type === 'request') {\n const request = event.request as { url?: string } | undefined;\n modelRuntime.onRequest?.(request?.url);\n }\n if (!recordingEnabled) return;\n void recordModelCallEvent({\n executionId,\n callId: internalCallId,\n semanticRetryAttempt: options?.semanticRetryAttempt,\n slot: modelConfig.slot,\n intent: modelConfig.intent,\n modelFamily: modelConfig.modelFamily,\n modelFallback: modelRuntime.modelFallback,\n ...event,\n attempt,\n });\n }\n : undefined;\n const effectiveTimeoutMs = resolveEffectiveTimeoutMs(modelConfig.timeout);\n const { signal: requestSignal, cleanup } = buildRequestAbortSignal(\n effectiveTimeoutMs,\n options?.abortSignal,\n );\n let deliveredChunk = false;\n const onChunk = options?.onChunk;\n const context: ModelCallContext = {\n modelRuntime,\n executionId,\n recordEvent,\n requestSignal,\n effectiveTimeoutMs,\n options: onChunk\n ? {\n ...options,\n onChunk: (chunk) => {\n requestSignal.throwIfAborted();\n deliveredChunk = true;\n onChunk(chunk);\n },\n }\n : options,\n };\n try {\n return await runWithAbortSignal(requestSignal, () =>\n prepared.kind === 'system-one'\n ? sendSystemOneRequest(context, prepared.input)\n : prepared.kind === 'codex'\n ? callCodex(context, prepared.input)\n : callOpenAI(context, prepared),\n );\n } catch (error) {\n options?.abortSignal?.throwIfAborted();\n const lastError = toError(error);\n if (deliveredChunk) {\n throw new NonRetryableModelCallError(lastError);\n }\n throw lastError;\n } finally {\n cleanup();\n }\n}\n"],"names":["DEFAULT_MODEL_PROTOCOL","callGenerativeAI","messages","originalModelRuntime","originalOptions","isStreaming","assert","modelRuntime","options","prepareSiwcCall","modelConfig","executeModelCall","input","isCodexAppServerProvider","prepareCodexCall","protocol","prepareResponses","prepareChatCompletion","callSystemOneAI","request","prepareSystemOne","prepare","executionId","uuid","internalCallId","nextInternalCallId","prepared","runWithAbortSignal","result","callModelWithRetry","rawUsage","timeCost","totalTimeCost","retryCount","requestId","responseModelName","response","rawUsageInfo","buildUsageInfo","usage","undefined","debugProfileStats","getDebug","debugProfileDetail","JSON","NonRetryableModelCallError","Error","cause","startTime","Date","config","maxAttempts","normalizeRetryCount","retryInterval","attemptErrors","attempt","callModelOnce","error","SiwcTokenExpiredError","refresh","siwcTokenManager","refreshed","retryError","lastError","toError","appendAIRequestFailureSummary","waitForRetry","recordingEnabled","isModelCallRecordingEnabled","recordEvent","event","recordModelCallEvent","effectiveTimeoutMs","resolveEffectiveTimeoutMs","requestSignal","cleanup","buildRequestAbortSignal","deliveredChunk","onChunk","context","chunk","sendSystemOneRequest","callCodex","callOpenAI"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jwtVerify } from "jose";
|
|
2
2
|
import { tokenResponseError, withSiwcStage } from "./errors.mjs";
|
|
3
|
+
import { createSiwcJWKSet, fetchSiwc } from "./network.mjs";
|
|
3
4
|
const ISSUER = 'https://auth.openai.com';
|
|
4
5
|
const RESOURCE = 'https://api.openai.com/v1';
|
|
5
6
|
function base64url(bytes) {
|
|
@@ -58,7 +59,7 @@ function parseSiwcCallback(url, attempt) {
|
|
|
58
59
|
async function exchangeSiwcCode(attempt, callback, signal, dependencies) {
|
|
59
60
|
signal.throwIfAborted();
|
|
60
61
|
const response = await withSiwcStage('Token exchange', async ()=>{
|
|
61
|
-
const response = await (dependencies?.fetch ??
|
|
62
|
+
const response = await (dependencies?.fetch ?? fetchSiwc)(`${ISSUER}/api/accounts/oauth/token`, {
|
|
62
63
|
method: 'POST',
|
|
63
64
|
headers: {
|
|
64
65
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
@@ -81,7 +82,7 @@ async function exchangeSiwcCode(attempt, callback, signal, dependencies) {
|
|
|
81
82
|
if (!tokens || 'string' != typeof tokens.access_token || !tokens.access_token || 'string' != typeof tokens.refresh_token || !tokens.refresh_token || 'string' != typeof tokens.id_token || 'string' != typeof tokens.scope || 'number' != typeof tokens.expires_in || !Number.isFinite(tokens.expires_in) || tokens.expires_in <= 0 || 'string' != typeof tokens.token_type || 'bearer' !== tokens.token_type.toLowerCase()) throw new Error('OpenAI returned an invalid token response.');
|
|
82
83
|
const scopes = tokens.scope.split(/\s+/);
|
|
83
84
|
if (!scopes.includes('chatgpt.tokens.use.direct')) throw new Error('ChatGPT plan usage was not authorized.');
|
|
84
|
-
const { payload } = await withSiwcStage('ID token verification', ()=>jwtVerify(tokens.id_token, dependencies?.keys ??
|
|
85
|
+
const { payload } = await withSiwcStage('ID token verification', ()=>jwtVerify(tokens.id_token, dependencies?.keys ?? createSiwcJWKSet(ISSUER, signal), {
|
|
85
86
|
issuer: ISSUER,
|
|
86
87
|
audience: callback.clientId,
|
|
87
88
|
algorithms: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/openai/siwc/authorization.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/siwc/authorization.ts"],"sourcesContent":["import { type JWTVerifyGetKey, createRemoteJWKSet, jwtVerify } from 'jose';\nimport { tokenResponseError, withSiwcStage } from './errors';\n\nconst ISSUER = 'https://auth.openai.com';\nconst RESOURCE = 'https://api.openai.com/v1';\nexport interface SiwcLoginAttempt {\n hostId: string;\n redirectUri: string;\n clientId?: string;\n state: string;\n nonce: string;\n verifier: string;\n expiresAt: number;\n}\n\nfunction base64url(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\nexport async function createSiwcAuthorization(options: {\n hostId: string;\n clientId?: string;\n redirectUri: string;\n agentName: string;\n}) {\n const { hostId, clientId, redirectUri, agentName } = options;\n const random = () => base64url(crypto.getRandomValues(new Uint8Array(32)));\n const attempt: SiwcLoginAttempt = {\n hostId,\n redirectUri,\n clientId,\n state: random(),\n nonce: random(),\n verifier: random(),\n expiresAt: Date.now() + 5 * 60_000,\n };\n const challenge = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(attempt.verifier),\n );\n const url = new URL(`${ISSUER}/api/accounts/authorize`);\n url.search = new URLSearchParams({\n client_id: clientId ?? 'dynamic_agent_client',\n ...(clientId ? {} : { agent_name_hint: agentName }),\n ext_agent_host_id: hostId,\n response_type: 'code',\n redirect_uri: attempt.redirectUri,\n scope:\n 'openid profile email offline_access resource.invoke chatgpt.tokens.use.direct',\n resource: RESOURCE,\n state: attempt.state,\n nonce: attempt.nonce,\n code_challenge_method: 'S256',\n code_challenge: base64url(new Uint8Array(challenge)),\n }).toString();\n return { attempt, url: url.toString() };\n}\n\nexport function parseSiwcCallback(url: string, attempt: SiwcLoginAttempt) {\n const callback = new URL(url);\n const expected = new URL(attempt.redirectUri);\n if (\n callback.origin !== expected.origin ||\n callback.pathname !== expected.pathname\n ) {\n return undefined;\n }\n const params = callback.searchParams;\n if (\n params.getAll('state').length !== 1 ||\n params.get('state') !== attempt.state\n ) {\n return undefined;\n }\n if (Date.now() >= attempt.expiresAt) {\n throw new Error('Authorization timed out. Start authorization again.');\n }\n if (params.has('error')) {\n throw new Error(\n `OpenAI authorization failed: ${params.get('error_description') || params.get('error')}`,\n );\n }\n const code = params.get('code');\n const clientId = params.get('client_id') || attempt.clientId;\n if (\n !code ||\n !clientId ||\n clientId === 'dynamic_agent_client' ||\n params.getAll('code').length !== 1 ||\n params.getAll('client_id').length > 1 ||\n (attempt.clientId && clientId !== attempt.clientId)\n ) {\n throw new Error('Invalid OpenAI authorization callback.');\n }\n return { code, clientId };\n}\n\nexport async function exchangeSiwcCode(\n attempt: SiwcLoginAttempt,\n callback: { code: string; clientId: string },\n signal: AbortSignal,\n dependencies?: { fetch: typeof fetch; keys: JWTVerifyGetKey },\n) {\n signal.throwIfAborted();\n const response = await withSiwcStage('Token exchange', async () => {\n const response = await (dependencies?.fetch ?? fetch)(\n `${ISSUER}/api/accounts/oauth/token`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: callback.clientId,\n code: callback.code,\n code_verifier: attempt.verifier,\n redirect_uri: attempt.redirectUri,\n resource: RESOURCE,\n }),\n signal,\n redirect: 'error',\n },\n );\n if (!response.ok) {\n throw await tokenResponseError(response);\n }\n return response;\n });\n const tokens = await withSiwcStage('Token response parsing', () =>\n response.json(),\n );\n if (\n !tokens ||\n typeof tokens.access_token !== 'string' ||\n !tokens.access_token ||\n typeof tokens.refresh_token !== 'string' ||\n !tokens.refresh_token ||\n typeof tokens.id_token !== 'string' ||\n typeof tokens.scope !== 'string' ||\n typeof tokens.expires_in !== 'number' ||\n !Number.isFinite(tokens.expires_in) ||\n tokens.expires_in <= 0 ||\n typeof tokens.token_type !== 'string' ||\n tokens.token_type.toLowerCase() !== 'bearer'\n ) {\n throw new Error('OpenAI returned an invalid token response.');\n }\n const scopes = tokens.scope.split(/\\s+/);\n if (!scopes.includes('chatgpt.tokens.use.direct')) {\n throw new Error('ChatGPT plan usage was not authorized.');\n }\n const { payload } = await withSiwcStage('ID token verification', () =>\n jwtVerify(\n tokens.id_token,\n dependencies?.keys ??\n createRemoteJWKSet(new URL(`${ISSUER}/.well-known/jwks.json`)),\n {\n issuer: ISSUER,\n audience: callback.clientId,\n algorithms: ['RS256'],\n // Some CI machines have incorrect system clocks. Do not compare ID token\n // exp/nbf claims against local time when accepting newly returned tokens.\n // Keep signature, issuer, audience and required-claim validation.\n clockTolerance: Number.POSITIVE_INFINITY,\n requiredClaims: ['sub', 'exp', 'iat', 'nonce'],\n },\n ),\n );\n if (payload.nonce !== attempt.nonce || !payload.sub) {\n throw new Error('OpenAI login identity or nonce did not match.');\n }\n signal.throwIfAborted();\n return {\n ...(typeof payload.email === 'string' ? { email: payload.email } : {}),\n ext_agent_host_id: attempt.hostId,\n client_id: callback.clientId,\n subject: payload.sub,\n issuer: ISSUER,\n access_token: tokens.access_token as string,\n refresh_token: tokens.refresh_token as string,\n id_token: tokens.id_token as string,\n scopes,\n expires_at: Date.now() + tokens.expires_in * 1000,\n };\n}\n"],"names":["ISSUER","RESOURCE","base64url","bytes","btoa","String","createSiwcAuthorization","options","hostId","clientId","redirectUri","agentName","random","crypto","Uint8Array","attempt","Date","challenge","TextEncoder","url","URL","URLSearchParams","parseSiwcCallback","callback","expected","params","Error","code","exchangeSiwcCode","signal","dependencies","response","withSiwcStage","fetch","tokenResponseError","tokens","Number","scopes","payload","jwtVerify","createRemoteJWKSet"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/siwc/authorization.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/siwc/authorization.ts"],"sourcesContent":["import { type JWTVerifyGetKey, jwtVerify } from 'jose';\nimport { tokenResponseError, withSiwcStage } from './errors';\nimport { createSiwcJWKSet, fetchSiwc } from './network';\n\nconst ISSUER = 'https://auth.openai.com';\nconst RESOURCE = 'https://api.openai.com/v1';\nexport interface SiwcLoginAttempt {\n hostId: string;\n redirectUri: string;\n clientId?: string;\n state: string;\n nonce: string;\n verifier: string;\n expiresAt: number;\n}\n\nfunction base64url(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\nexport async function createSiwcAuthorization(options: {\n hostId: string;\n clientId?: string;\n redirectUri: string;\n agentName: string;\n}) {\n const { hostId, clientId, redirectUri, agentName } = options;\n const random = () => base64url(crypto.getRandomValues(new Uint8Array(32)));\n const attempt: SiwcLoginAttempt = {\n hostId,\n redirectUri,\n clientId,\n state: random(),\n nonce: random(),\n verifier: random(),\n expiresAt: Date.now() + 5 * 60_000,\n };\n const challenge = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(attempt.verifier),\n );\n const url = new URL(`${ISSUER}/api/accounts/authorize`);\n url.search = new URLSearchParams({\n client_id: clientId ?? 'dynamic_agent_client',\n ...(clientId ? {} : { agent_name_hint: agentName }),\n ext_agent_host_id: hostId,\n response_type: 'code',\n redirect_uri: attempt.redirectUri,\n scope:\n 'openid profile email offline_access resource.invoke chatgpt.tokens.use.direct',\n resource: RESOURCE,\n state: attempt.state,\n nonce: attempt.nonce,\n code_challenge_method: 'S256',\n code_challenge: base64url(new Uint8Array(challenge)),\n }).toString();\n return { attempt, url: url.toString() };\n}\n\nexport function parseSiwcCallback(url: string, attempt: SiwcLoginAttempt) {\n const callback = new URL(url);\n const expected = new URL(attempt.redirectUri);\n if (\n callback.origin !== expected.origin ||\n callback.pathname !== expected.pathname\n ) {\n return undefined;\n }\n const params = callback.searchParams;\n if (\n params.getAll('state').length !== 1 ||\n params.get('state') !== attempt.state\n ) {\n return undefined;\n }\n if (Date.now() >= attempt.expiresAt) {\n throw new Error('Authorization timed out. Start authorization again.');\n }\n if (params.has('error')) {\n throw new Error(\n `OpenAI authorization failed: ${params.get('error_description') || params.get('error')}`,\n );\n }\n const code = params.get('code');\n const clientId = params.get('client_id') || attempt.clientId;\n if (\n !code ||\n !clientId ||\n clientId === 'dynamic_agent_client' ||\n params.getAll('code').length !== 1 ||\n params.getAll('client_id').length > 1 ||\n (attempt.clientId && clientId !== attempt.clientId)\n ) {\n throw new Error('Invalid OpenAI authorization callback.');\n }\n return { code, clientId };\n}\n\nexport async function exchangeSiwcCode(\n attempt: SiwcLoginAttempt,\n callback: { code: string; clientId: string },\n signal: AbortSignal,\n dependencies?: { fetch: typeof fetch; keys: JWTVerifyGetKey },\n) {\n signal.throwIfAborted();\n const response = await withSiwcStage('Token exchange', async () => {\n const response = await (dependencies?.fetch ?? fetchSiwc)(\n `${ISSUER}/api/accounts/oauth/token`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: callback.clientId,\n code: callback.code,\n code_verifier: attempt.verifier,\n redirect_uri: attempt.redirectUri,\n resource: RESOURCE,\n }),\n signal,\n redirect: 'error',\n },\n );\n if (!response.ok) {\n throw await tokenResponseError(response);\n }\n return response;\n });\n const tokens = await withSiwcStage('Token response parsing', () =>\n response.json(),\n );\n if (\n !tokens ||\n typeof tokens.access_token !== 'string' ||\n !tokens.access_token ||\n typeof tokens.refresh_token !== 'string' ||\n !tokens.refresh_token ||\n typeof tokens.id_token !== 'string' ||\n typeof tokens.scope !== 'string' ||\n typeof tokens.expires_in !== 'number' ||\n !Number.isFinite(tokens.expires_in) ||\n tokens.expires_in <= 0 ||\n typeof tokens.token_type !== 'string' ||\n tokens.token_type.toLowerCase() !== 'bearer'\n ) {\n throw new Error('OpenAI returned an invalid token response.');\n }\n const scopes = tokens.scope.split(/\\s+/);\n if (!scopes.includes('chatgpt.tokens.use.direct')) {\n throw new Error('ChatGPT plan usage was not authorized.');\n }\n const { payload } = await withSiwcStage('ID token verification', () =>\n jwtVerify(\n tokens.id_token,\n dependencies?.keys ?? createSiwcJWKSet(ISSUER, signal),\n {\n issuer: ISSUER,\n audience: callback.clientId,\n algorithms: ['RS256'],\n // Some CI machines have incorrect system clocks. Do not compare ID token\n // exp/nbf claims against local time when accepting newly returned tokens.\n // Keep signature, issuer, audience and required-claim validation.\n clockTolerance: Number.POSITIVE_INFINITY,\n requiredClaims: ['sub', 'exp', 'iat', 'nonce'],\n },\n ),\n );\n if (payload.nonce !== attempt.nonce || !payload.sub) {\n throw new Error('OpenAI login identity or nonce did not match.');\n }\n signal.throwIfAborted();\n return {\n ...(typeof payload.email === 'string' ? { email: payload.email } : {}),\n ext_agent_host_id: attempt.hostId,\n client_id: callback.clientId,\n subject: payload.sub,\n issuer: ISSUER,\n access_token: tokens.access_token as string,\n refresh_token: tokens.refresh_token as string,\n id_token: tokens.id_token as string,\n scopes,\n expires_at: Date.now() + tokens.expires_in * 1000,\n };\n}\n"],"names":["ISSUER","RESOURCE","base64url","bytes","btoa","String","createSiwcAuthorization","options","hostId","clientId","redirectUri","agentName","random","crypto","Uint8Array","attempt","Date","challenge","TextEncoder","url","URL","URLSearchParams","parseSiwcCallback","callback","expected","params","Error","code","exchangeSiwcCode","signal","dependencies","response","withSiwcStage","fetchSiwc","tokenResponseError","tokens","Number","scopes","payload","jwtVerify","createSiwcJWKSet"],"mappings":";;;AAIA,MAAMA,SAAS;AACf,MAAMC,WAAW;AAWjB,SAASC,UAAUC,KAAiB;IAClC,OAAOC,KAAKC,OAAO,YAAY,IAAIF,QAChC,OAAO,CAAC,OAAO,KACf,OAAO,CAAC,OAAO,KACf,OAAO,CAAC,OAAO;AACpB;AAEO,eAAeG,wBAAwBC,OAK7C;IACC,MAAM,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,WAAW,EAAEC,SAAS,EAAE,GAAGJ;IACrD,MAAMK,SAAS,IAAMV,UAAUW,OAAO,eAAe,CAAC,IAAIC,WAAW;IACrE,MAAMC,UAA4B;QAChCP;QACAE;QACAD;QACA,OAAOG;QACP,OAAOA;QACP,UAAUA;QACV,WAAWI,KAAK,GAAG,KAAK;IAC1B;IACA,MAAMC,YAAY,MAAMJ,OAAO,MAAM,CAAC,MAAM,CAC1C,WACA,IAAIK,cAAc,MAAM,CAACH,QAAQ,QAAQ;IAE3C,MAAMI,MAAM,IAAIC,IAAI,GAAGpB,OAAO,uBAAuB,CAAC;IACtDmB,IAAI,MAAM,GAAG,IAAIE,gBAAgB;QAC/B,WAAWZ,YAAY;QACvB,GAAIA,WAAW,CAAC,IAAI;YAAE,iBAAiBE;QAAU,CAAC;QAClD,mBAAmBH;QACnB,eAAe;QACf,cAAcO,QAAQ,WAAW;QACjC,OACE;QACF,UAAUd;QACV,OAAOc,QAAQ,KAAK;QACpB,OAAOA,QAAQ,KAAK;QACpB,uBAAuB;QACvB,gBAAgBb,UAAU,IAAIY,WAAWG;IAC3C,GAAG,QAAQ;IACX,OAAO;QAAEF;QAAS,KAAKI,IAAI,QAAQ;IAAG;AACxC;AAEO,SAASG,kBAAkBH,GAAW,EAAEJ,OAAyB;IACtE,MAAMQ,WAAW,IAAIH,IAAID;IACzB,MAAMK,WAAW,IAAIJ,IAAIL,QAAQ,WAAW;IAC5C,IACEQ,SAAS,MAAM,KAAKC,SAAS,MAAM,IACnCD,SAAS,QAAQ,KAAKC,SAAS,QAAQ,EAEvC;IAEF,MAAMC,SAASF,SAAS,YAAY;IACpC,IACEE,AAAkC,MAAlCA,OAAO,MAAM,CAAC,SAAS,MAAM,IAC7BA,OAAO,GAAG,CAAC,aAAaV,QAAQ,KAAK,EAErC;IAEF,IAAIC,KAAK,GAAG,MAAMD,QAAQ,SAAS,EACjC,MAAM,IAAIW,MAAM;IAElB,IAAID,OAAO,GAAG,CAAC,UACb,MAAM,IAAIC,MACR,CAAC,6BAA6B,EAAED,OAAO,GAAG,CAAC,wBAAwBA,OAAO,GAAG,CAAC,UAAU;IAG5F,MAAME,OAAOF,OAAO,GAAG,CAAC;IACxB,MAAMhB,WAAWgB,OAAO,GAAG,CAAC,gBAAgBV,QAAQ,QAAQ;IAC5D,IACE,CAACY,QACD,CAAClB,YACDA,AAAa,2BAAbA,YACAgB,AAAiC,MAAjCA,OAAO,MAAM,CAAC,QAAQ,MAAM,IAC5BA,OAAO,MAAM,CAAC,aAAa,MAAM,GAAG,KACnCV,QAAQ,QAAQ,IAAIN,aAAaM,QAAQ,QAAQ,EAElD,MAAM,IAAIW,MAAM;IAElB,OAAO;QAAEC;QAAMlB;IAAS;AAC1B;AAEO,eAAemB,iBACpBb,OAAyB,EACzBQ,QAA4C,EAC5CM,MAAmB,EACnBC,YAA6D;IAE7DD,OAAO,cAAc;IACrB,MAAME,WAAW,MAAMC,cAAc,kBAAkB;QACrD,MAAMD,WAAW,MAAOD,AAAAA,CAAAA,cAAc,SAASG,SAAQ,EACrD,GAAGjC,OAAO,yBAAyB,CAAC,EACpC;YACE,QAAQ;YACR,SAAS;gBAAE,gBAAgB;YAAoC;YAC/D,MAAM,IAAIqB,gBAAgB;gBACxB,YAAY;gBACZ,WAAWE,SAAS,QAAQ;gBAC5B,MAAMA,SAAS,IAAI;gBACnB,eAAeR,QAAQ,QAAQ;gBAC/B,cAAcA,QAAQ,WAAW;gBACjC,UAAUd;YACZ;YACA4B;YACA,UAAU;QACZ;QAEF,IAAI,CAACE,SAAS,EAAE,EACd,MAAM,MAAMG,mBAAmBH;QAEjC,OAAOA;IACT;IACA,MAAMI,SAAS,MAAMH,cAAc,0BAA0B,IAC3DD,SAAS,IAAI;IAEf,IACE,CAACI,UACD,AAA+B,YAA/B,OAAOA,OAAO,YAAY,IAC1B,CAACA,OAAO,YAAY,IACpB,AAAgC,YAAhC,OAAOA,OAAO,aAAa,IAC3B,CAACA,OAAO,aAAa,IACrB,AAA2B,YAA3B,OAAOA,OAAO,QAAQ,IACtB,AAAwB,YAAxB,OAAOA,OAAO,KAAK,IACnB,AAA6B,YAA7B,OAAOA,OAAO,UAAU,IACxB,CAACC,OAAO,QAAQ,CAACD,OAAO,UAAU,KAClCA,OAAO,UAAU,IAAI,KACrB,AAA6B,YAA7B,OAAOA,OAAO,UAAU,IACxBA,AAAoC,aAApCA,OAAO,UAAU,CAAC,WAAW,IAE7B,MAAM,IAAIT,MAAM;IAElB,MAAMW,SAASF,OAAO,KAAK,CAAC,KAAK,CAAC;IAClC,IAAI,CAACE,OAAO,QAAQ,CAAC,8BACnB,MAAM,IAAIX,MAAM;IAElB,MAAM,EAAEY,OAAO,EAAE,GAAG,MAAMN,cAAc,yBAAyB,IAC/DO,UACEJ,OAAO,QAAQ,EACfL,cAAc,QAAQU,iBAAiBxC,QAAQ6B,SAC/C;YACE,QAAQ7B;YACR,UAAUuB,SAAS,QAAQ;YAC3B,YAAY;gBAAC;aAAQ;YAIrB,gBAAgBa;YAChB,gBAAgB;gBAAC;gBAAO;gBAAO;gBAAO;aAAQ;QAChD;IAGJ,IAAIE,QAAQ,KAAK,KAAKvB,QAAQ,KAAK,IAAI,CAACuB,QAAQ,GAAG,EACjD,MAAM,IAAIZ,MAAM;IAElBG,OAAO,cAAc;IACrB,OAAO;QACL,GAAI,AAAyB,YAAzB,OAAOS,QAAQ,KAAK,GAAgB;YAAE,OAAOA,QAAQ,KAAK;QAAC,IAAI,CAAC,CAAC;QACrE,mBAAmBvB,QAAQ,MAAM;QACjC,WAAWQ,SAAS,QAAQ;QAC5B,SAASe,QAAQ,GAAG;QACpB,QAAQtC;QACR,cAAcmC,OAAO,YAAY;QACjC,eAAeA,OAAO,aAAa;QACnC,UAAUA,OAAO,QAAQ;QACzBE;QACA,YAAYrB,KAAK,GAAG,KAAKmB,AAAoB,OAApBA,OAAO,UAAU;IAC5C;AACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ifInBrowser } from "@midscene/shared/utils";
|
|
2
|
+
import { createLocalJWKSet } from "jose";
|
|
3
|
+
import { createProxyAgentIfNeeded } from "../../proxy/index.mjs";
|
|
4
|
+
async function fetchSiwc(input, init, proxy) {
|
|
5
|
+
if (ifInBrowser) return fetch(input, init);
|
|
6
|
+
const { httpProxy, socksProxy } = proxy ?? {
|
|
7
|
+
httpProxy: process.env.MIDSCENE_MODEL_HTTP_PROXY?.trim() || void 0,
|
|
8
|
+
socksProxy: process.env.MIDSCENE_MODEL_SOCKS_PROXY?.trim() || void 0
|
|
9
|
+
};
|
|
10
|
+
const dispatcher = await createProxyAgentIfNeeded({
|
|
11
|
+
httpProxy,
|
|
12
|
+
socksProxy
|
|
13
|
+
});
|
|
14
|
+
if (!dispatcher) return fetch(input, init);
|
|
15
|
+
try {
|
|
16
|
+
return await fetch(input, {
|
|
17
|
+
...init,
|
|
18
|
+
dispatcher
|
|
19
|
+
});
|
|
20
|
+
} finally{
|
|
21
|
+
dispatcher.close();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function createSiwcJWKSet(issuer, signal, request = fetchSiwc) {
|
|
25
|
+
return async (header, token)=>{
|
|
26
|
+
const response = await request(`${issuer}/.well-known/jwks.json`, {
|
|
27
|
+
signal,
|
|
28
|
+
redirect: 'error'
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) throw new Error(`Failed to fetch OpenAI JWKS: HTTP ${response.status}`);
|
|
31
|
+
return createLocalJWKSet(await response.json())(header, token);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export { createSiwcJWKSet, fetchSiwc };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=network.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai/siwc/network.mjs","sources":["../../../../../../src/ai-model/service-caller/openai/siwc/network.ts"],"sourcesContent":["import { ifInBrowser } from '@midscene/shared/utils';\nimport { type JWTVerifyGetKey, createLocalJWKSet } from 'jose';\nimport type { Dispatcher } from 'undici';\nimport { createProxyAgentIfNeeded } from '../../proxy';\n\nexport interface SiwcProxyConfig {\n httpProxy?: string;\n socksProxy?: string;\n}\n\nexport async function fetchSiwc(\n input: Parameters<typeof fetch>[0],\n init?: RequestInit,\n proxy?: SiwcProxyConfig,\n) {\n if (ifInBrowser) {\n return fetch(input, init);\n }\n const { httpProxy, socksProxy } = proxy ?? {\n httpProxy: process.env.MIDSCENE_MODEL_HTTP_PROXY?.trim() || undefined,\n socksProxy: process.env.MIDSCENE_MODEL_SOCKS_PROXY?.trim() || undefined,\n };\n const dispatcher = (await createProxyAgentIfNeeded({\n httpProxy,\n socksProxy,\n })) as Dispatcher | undefined;\n if (!dispatcher) {\n return fetch(input, init);\n }\n try {\n return await fetch(input, { ...init, dispatcher } as RequestInit);\n } finally {\n // Graceful close waits for the response body to be consumed by the caller.\n void dispatcher.close();\n }\n}\n\nexport function createSiwcJWKSet(\n issuer: string,\n signal: AbortSignal,\n request: typeof fetch = fetchSiwc,\n): JWTVerifyGetKey {\n return async (header, token) => {\n const response = await request(`${issuer}/.well-known/jwks.json`, {\n signal,\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch OpenAI JWKS: HTTP ${response.status}`);\n }\n return createLocalJWKSet(await response.json())(header, token);\n };\n}\n"],"names":["fetchSiwc","input","init","proxy","ifInBrowser","fetch","httpProxy","socksProxy","process","undefined","dispatcher","createProxyAgentIfNeeded","createSiwcJWKSet","issuer","signal","request","header","token","response","Error","createLocalJWKSet"],"mappings":";;;AAUO,eAAeA,UACpBC,KAAkC,EAClCC,IAAkB,EAClBC,KAAuB;IAEvB,IAAIC,aACF,OAAOC,MAAMJ,OAAOC;IAEtB,MAAM,EAAEI,SAAS,EAAEC,UAAU,EAAE,GAAGJ,SAAS;QACzC,WAAWK,QAAQ,GAAG,CAAC,yBAAyB,EAAE,UAAUC;QAC5D,YAAYD,QAAQ,GAAG,CAAC,0BAA0B,EAAE,UAAUC;IAChE;IACA,MAAMC,aAAc,MAAMC,yBAAyB;QACjDL;QACAC;IACF;IACA,IAAI,CAACG,YACH,OAAOL,MAAMJ,OAAOC;IAEtB,IAAI;QACF,OAAO,MAAMG,MAAMJ,OAAO;YAAE,GAAGC,IAAI;YAAEQ;QAAW;IAClD,SAAU;QAEHA,WAAW,KAAK;IACvB;AACF;AAEO,SAASE,iBACdC,MAAc,EACdC,MAAmB,EACnBC,UAAwBf,SAAS;IAEjC,OAAO,OAAOgB,QAAQC;QACpB,MAAMC,WAAW,MAAMH,QAAQ,GAAGF,OAAO,sBAAsB,CAAC,EAAE;YAChEC;YACA,UAAU;QACZ;QACA,IAAI,CAACI,SAAS,EAAE,EACd,MAAM,IAAIC,MAAM,CAAC,kCAAkC,EAAED,SAAS,MAAM,EAAE;QAExE,OAAOE,kBAAkB,MAAMF,SAAS,IAAI,IAAIF,QAAQC;IAC1D;AACF"}
|