@midscene/core 1.6.3 → 1.7.0
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.mjs +5 -4
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/ai-model/connectivity.mjs +138 -0
- package/dist/es/ai-model/connectivity.mjs.map +1 -0
- package/dist/es/ai-model/index.mjs +2 -1
- package/dist/es/ai-model/inspect.mjs +4 -1
- package/dist/es/ai-model/inspect.mjs.map +1 -1
- package/dist/es/ai-model/prompt/yaml-generator.mjs +58 -74
- package/dist/es/ai-model/prompt/yaml-generator.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/index.mjs +1 -1
- package/dist/es/ai-model/service-caller/index.mjs.map +1 -1
- package/dist/es/index.mjs +2 -2
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/report-cli.mjs +14 -4
- package/dist/es/report-cli.mjs.map +1 -1
- package/dist/es/report-markdown.mjs +26 -0
- package/dist/es/report-markdown.mjs.map +1 -1
- package/dist/es/utils.mjs +2 -2
- package/dist/lib/agent/agent.js +5 -4
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/ai-model/connectivity.js +182 -0
- package/dist/lib/ai-model/connectivity.js.map +1 -0
- package/dist/lib/ai-model/index.js +5 -1
- package/dist/lib/ai-model/inspect.js +4 -1
- package/dist/lib/ai-model/inspect.js.map +1 -1
- package/dist/lib/ai-model/prompt/yaml-generator.js +58 -74
- package/dist/lib/ai-model/prompt/yaml-generator.js.map +1 -1
- package/dist/lib/ai-model/service-caller/index.js +1 -1
- package/dist/lib/ai-model/service-caller/index.js.map +1 -1
- package/dist/lib/index.js +3 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/report-cli.js +12 -2
- package/dist/lib/report-cli.js.map +1 -1
- package/dist/lib/report-markdown.js +26 -0
- package/dist/lib/report-markdown.js.map +1 -1
- package/dist/lib/utils.js +2 -2
- package/dist/types/ai-model/connectivity.d.ts +20 -0
- package/dist/types/ai-model/index.d.ts +2 -0
- package/dist/types/ai-model/prompt/yaml-generator.d.ts +2 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/connectivity.mjs","sources":["../../../src/ai-model/connectivity.ts"],"sourcesContent":["import { ScreenshotItem } from '@/screenshot-item';\nimport Service from '@/service';\nimport type { UIContext } from '@/types';\nimport type { IModelConfig, TIntent } from '@midscene/shared/env';\nimport { imageInfoOfBase64 } from '@midscene/shared/img';\nimport { callAI } from './service-caller';\n\nconst CONNECTIVITY_FIXTURE_IMAGE =\n 'data:image/png;base64,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';\n\nconst TEXT_EXPECTED_TOKEN = 'CONNECTIVITY_OK';\nconst VISION_EXPECTED_TEXT = 'what needs to be done?';\nconst LOCATE_PROMPT = 'the main todo input box';\n\nexport interface ConnectivityCheckResultItem {\n name: 'text' | 'vision' | 'aiLocate';\n intent: TIntent;\n modelName: string;\n modelFamily?: string;\n passed: boolean;\n durationMs: number;\n message: string;\n}\n\nexport interface ConnectivityTestResult {\n passed: boolean;\n checks: ConnectivityCheckResultItem[];\n}\n\nexport interface ConnectivityTestConfig {\n defaultModelConfig: IModelConfig;\n planningModelConfig: IModelConfig;\n insightModelConfig: IModelConfig;\n}\n\nfunction normalizeText(text: string): string {\n return text\n .trim()\n .replace(/^['\"`]+|['\"`]+$/g, '')\n .toLowerCase();\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\nfunction hasValidRect(value: unknown): boolean {\n if (!value || typeof value !== 'object') {\n return false;\n }\n\n const rect = value as {\n left?: unknown;\n top?: unknown;\n width?: unknown;\n height?: unknown;\n };\n\n return (\n isFiniteNumber(rect.left) &&\n isFiniteNumber(rect.top) &&\n isFiniteNumber(rect.width) &&\n isFiniteNumber(rect.height)\n );\n}\n\nfunction hasValidCenter(value: unknown): boolean {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n isFiniteNumber(value[0]) &&\n isFiniteNumber(value[1])\n );\n}\n\nasync function buildFixtureContext(): Promise<UIContext> {\n const shotSize = await imageInfoOfBase64(CONNECTIVITY_FIXTURE_IMAGE);\n return {\n screenshot: ScreenshotItem.create(CONNECTIVITY_FIXTURE_IMAGE, Date.now()),\n shotSize,\n shrunkShotToLogicalRatio: 1,\n };\n}\n\nfunction buildCheckResult(\n name: ConnectivityCheckResultItem['name'],\n modelConfig: IModelConfig,\n result: Omit<\n ConnectivityCheckResultItem,\n 'name' | 'intent' | 'modelName' | 'modelFamily'\n >,\n): ConnectivityCheckResultItem {\n return {\n name,\n intent: modelConfig.intent,\n modelName: modelConfig.modelName,\n modelFamily: modelConfig.modelFamily,\n ...result,\n };\n}\n\nexport async function runConnectivityTest(\n config: ConnectivityTestConfig,\n): Promise<ConnectivityTestResult> {\n const checks: ConnectivityCheckResultItem[] = [];\n\n {\n const startTime = Date.now();\n try {\n const result = await callAI(\n [\n {\n role: 'system',\n content: 'Reply with the exact token the user asks for.',\n },\n {\n role: 'user',\n content: `Return exactly ${TEXT_EXPECTED_TOKEN}`,\n },\n ],\n config.planningModelConfig,\n );\n const content = result.content.trim();\n const passed = content.includes(TEXT_EXPECTED_TOKEN);\n checks.push(\n buildCheckResult('text', config.planningModelConfig, {\n passed,\n durationMs: Date.now() - startTime,\n message: passed ? '' : `Unexpected response: ${content}`,\n }),\n );\n } catch (error) {\n checks.push(\n buildCheckResult('text', config.planningModelConfig, {\n passed: false,\n durationMs: Date.now() - startTime,\n message: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n }\n\n {\n const startTime = Date.now();\n try {\n const result = await callAI(\n [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: 'What is the main content of this image ? It is a photo or a form ?',\n },\n {\n type: 'image_url',\n image_url: {\n url: CONNECTIVITY_FIXTURE_IMAGE,\n detail: 'high',\n },\n },\n ],\n },\n ],\n config.insightModelConfig,\n );\n const normalized = normalizeText(result.content);\n checks.push(\n buildCheckResult('vision', config.insightModelConfig, {\n passed: true,\n durationMs: Date.now() - startTime,\n message: '',\n }),\n );\n } catch (error) {\n checks.push(\n buildCheckResult('vision', config.insightModelConfig, {\n passed: false,\n durationMs: Date.now() - startTime,\n message: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n }\n\n {\n const startTime = Date.now();\n try {\n const context = await buildFixtureContext();\n const service = new Service(context);\n const locateResult = await service.locate(\n { prompt: LOCATE_PROMPT },\n {},\n config.defaultModelConfig,\n );\n const targetRect = locateResult.rect || locateResult.element?.rect;\n const center = locateResult.element?.center;\n const passed = hasValidRect(targetRect) && hasValidCenter(center);\n checks.push(\n buildCheckResult('aiLocate', config.defaultModelConfig, {\n passed,\n durationMs: Date.now() - startTime,\n message: passed\n ? ''\n : `Invalid locate result: ${JSON.stringify({\n rect: targetRect,\n center,\n })}`,\n }),\n );\n } catch (error) {\n checks.push(\n buildCheckResult('aiLocate', config.defaultModelConfig, {\n passed: false,\n durationMs: Date.now() - startTime,\n message: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n }\n\n return {\n passed: checks.every((item) => item.passed),\n checks,\n };\n}\n"],"names":["CONNECTIVITY_FIXTURE_IMAGE","TEXT_EXPECTED_TOKEN","LOCATE_PROMPT","normalizeText","text","isFiniteNumber","value","Number","hasValidRect","rect","hasValidCenter","Array","buildFixtureContext","shotSize","imageInfoOfBase64","ScreenshotItem","Date","buildCheckResult","name","modelConfig","result","runConnectivityTest","config","checks","startTime","callAI","content","passed","error","Error","String","context","service","Service","locateResult","targetRect","center","JSON","item"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AIResponseParseError, callAI, callAIWithObjectResponse, callAIWithStringResponse } from "./service-caller/index.mjs";
|
|
2
|
+
import { runConnectivityTest } from "./connectivity.mjs";
|
|
2
3
|
import { systemPromptToLocateElement } from "./prompt/llm-locator.mjs";
|
|
3
4
|
import { generatePlaywrightTest, generatePlaywrightTestStream } from "./prompt/playwright-generator.mjs";
|
|
4
5
|
import { generateYamlTest, generateYamlTestStream } from "./prompt/yaml-generator.mjs";
|
|
@@ -8,4 +9,4 @@ import { autoGLMPlanning } from "./auto-glm/planning.mjs";
|
|
|
8
9
|
import { PointSchema, RectSchema, SizeSchema, TMultimodalPromptSchema, TUserPromptSchema, adaptBboxToRect, dumpActionParam, findAllMidsceneLocatorField, getMidsceneLocationSchema, parseActionParam } from "../common.mjs";
|
|
9
10
|
import { uiTarsPlanning } from "./ui-tars-planning.mjs";
|
|
10
11
|
import { ConversationHistory } from "./conversation-history.mjs";
|
|
11
|
-
export { AIResponseParseError, AiExtractElementInfo, AiJudgeOrderSensitive, AiLocateElement, AiLocateSection, ConversationHistory, PointSchema, RectSchema, SizeSchema, TMultimodalPromptSchema, TUserPromptSchema, adaptBboxToRect, autoGLMPlanning, callAI, callAIWithObjectResponse, callAIWithStringResponse, dumpActionParam, findAllMidsceneLocatorField, generatePlaywrightTest, generatePlaywrightTestStream, generateYamlTest, generateYamlTestStream, getMidsceneLocationSchema, parseActionParam, plan, systemPromptToLocateElement, uiTarsPlanning };
|
|
12
|
+
export { AIResponseParseError, AiExtractElementInfo, AiJudgeOrderSensitive, AiLocateElement, AiLocateSection, ConversationHistory, PointSchema, RectSchema, SizeSchema, TMultimodalPromptSchema, TUserPromptSchema, adaptBboxToRect, autoGLMPlanning, callAI, callAIWithObjectResponse, callAIWithStringResponse, dumpActionParam, findAllMidsceneLocatorField, generatePlaywrightTest, generatePlaywrightTestStream, generateYamlTest, generateYamlTestStream, getMidsceneLocationSchema, parseActionParam, plan, runConnectivityTest, systemPromptToLocateElement, uiTarsPlanning };
|
|
@@ -383,7 +383,10 @@ async function AiJudgeOrderSensitive(description, callAIFn, modelConfig) {
|
|
|
383
383
|
content: userPrompt
|
|
384
384
|
}
|
|
385
385
|
];
|
|
386
|
-
|
|
386
|
+
debugInspect("AiJudgeOrderSensitive: deepThink=false, description=%s", description);
|
|
387
|
+
const result = await callAIFn(msgs, modelConfig, {
|
|
388
|
+
deepThink: false
|
|
389
|
+
});
|
|
387
390
|
return {
|
|
388
391
|
isOrderSensitive: result.content.isOrderSensitive ?? false,
|
|
389
392
|
usage: result.usage
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/inspect.mjs","sources":["../../../src/ai-model/inspect.ts"],"sourcesContent":["import type {\n AIDataExtractionResponse,\n AIElementResponse,\n AISectionLocatorResponse,\n AIUsageInfo,\n Rect,\n ServiceExtractOption,\n UIContext,\n} from '@/types';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport {\n generateElementByPoint,\n generateElementByRect,\n} from '@midscene/shared/extractor/dom-util';\nimport {\n cropByRect,\n paddingToMatchBlockByBase64,\n preProcessImageUrl,\n scaleImage,\n} from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport type { LocateResultElement } from '@midscene/shared/types';\nimport { assert } from '@midscene/shared/utils';\nimport type {\n ChatCompletionSystemMessageParam,\n ChatCompletionUserMessageParam,\n} from 'openai/resources/index';\nimport type { TMultimodalPrompt, TUserPrompt } from '../common';\nimport { adaptBboxToRect, expandSearchArea, mergeRects } from '../common';\nimport { parseAutoGLMLocateResponse } from './auto-glm/parser';\nimport { getAutoGLMLocatePrompt } from './auto-glm/prompt';\nimport { isAutoGLM } from './auto-glm/util';\nimport {\n extractDataQueryPrompt,\n parseXMLExtractionResponse,\n systemPromptToExtract,\n} from './prompt/extraction';\nimport {\n findElementPrompt,\n systemPromptToLocateElement,\n} from './prompt/llm-locator';\nimport {\n sectionLocatorInstruction,\n systemPromptToLocateSection,\n} from './prompt/llm-section-locator';\nimport {\n orderSensitiveJudgePrompt,\n systemPromptToJudgeOrderSensitive,\n} from './prompt/order-sensitive-judge';\nimport {\n AIResponseParseError,\n callAI,\n callAIWithObjectResponse,\n callAIWithStringResponse,\n} from './service-caller/index';\n\nexport type AIArgs = [\n ChatCompletionSystemMessageParam,\n ...ChatCompletionUserMessageParam[],\n];\n\nconst debugInspect = getDebug('ai:inspect');\nconst debugSection = getDebug('ai:section');\n\nexport async function buildSearchAreaConfig(options: {\n context: UIContext;\n baseRect: Rect;\n modelFamily: IModelConfig['modelFamily'];\n}): Promise<{ rect: Rect; imageBase64: string; scale: number }> {\n const { context, baseRect, modelFamily } = options;\n const scaleRatio = 2;\n const sectionRect = expandSearchArea(baseRect, context.shotSize);\n\n const croppedResult = await cropByRect(\n context.screenshot.base64,\n sectionRect,\n modelFamily === 'qwen2.5-vl',\n );\n\n const scaledResult = await scaleImage(croppedResult.imageBase64, scaleRatio);\n sectionRect.width = scaledResult.width;\n sectionRect.height = scaledResult.height;\n return {\n rect: sectionRect,\n imageBase64: scaledResult.imageBase64,\n scale: scaleRatio,\n };\n}\n\nconst extraTextFromUserPrompt = (prompt: TUserPrompt): string => {\n if (typeof prompt === 'string') {\n return prompt;\n } else {\n return prompt.prompt;\n }\n};\n\nconst promptsToChatParam = async (\n multimodalPrompt: TMultimodalPrompt,\n): Promise<ChatCompletionUserMessageParam[]> => {\n const msgs: ChatCompletionUserMessageParam[] = [];\n if (multimodalPrompt?.images?.length) {\n msgs.push({\n role: 'user',\n content: [\n {\n type: 'text',\n text: 'Next, I will provide all the reference images.',\n },\n ],\n });\n\n for (const item of multimodalPrompt.images) {\n const base64 = await preProcessImageUrl(\n item.url,\n !!multimodalPrompt.convertHttpImage2Base64,\n );\n\n msgs.push({\n role: 'user',\n content: [\n {\n type: 'text',\n text: `this is the reference image named '${item.name}':`,\n },\n ],\n });\n\n msgs.push({\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: base64,\n detail: 'high',\n },\n },\n ],\n });\n }\n }\n return msgs;\n};\n\nexport async function AiLocateElement(options: {\n context: UIContext;\n targetElementDescription: TUserPrompt;\n searchConfig?: Awaited<ReturnType<typeof AiLocateSection>>;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n}): Promise<{\n parseResult: {\n elements: LocateResultElement[];\n errors?: string[];\n };\n rect?: Rect;\n rawResponse: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n}> {\n const { context, targetElementDescription, modelConfig } = options;\n const { modelFamily } = modelConfig;\n const screenshotBase64 = context.screenshot.base64;\n\n assert(\n targetElementDescription,\n 'cannot find the target element description',\n );\n const targetElementDescriptionText = extraTextFromUserPrompt(\n targetElementDescription,\n );\n const userInstructionPrompt = findElementPrompt(targetElementDescriptionText);\n const systemPrompt = isAutoGLM(modelFamily)\n ? getAutoGLMLocatePrompt(modelFamily)\n : systemPromptToLocateElement(modelFamily);\n\n let imagePayload = screenshotBase64;\n let imageWidth = context.shotSize.width;\n let imageHeight = context.shotSize.height;\n let originalImageWidth = imageWidth;\n let originalImageHeight = imageHeight;\n\n if (options.searchConfig) {\n assert(\n options.searchConfig.rect,\n 'searchArea is provided but its rect cannot be found. Failed to locate element',\n );\n assert(\n options.searchConfig.imageBase64,\n 'searchArea is provided but its imageBase64 cannot be found. Failed to locate element',\n );\n\n imagePayload = options.searchConfig.imageBase64;\n imageWidth = options.searchConfig.rect?.width;\n imageHeight = options.searchConfig.rect?.height;\n originalImageWidth = imageWidth;\n originalImageHeight = imageHeight;\n } else if (modelFamily === 'qwen2.5-vl') {\n const paddedResult = await paddingToMatchBlockByBase64(imagePayload);\n imageWidth = paddedResult.width;\n imageHeight = paddedResult.height;\n imagePayload = paddedResult.imageBase64;\n }\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n {\n type: 'text',\n text: isAutoGLM(modelFamily)\n ? `Tap: ${userInstructionPrompt}`\n : userInstructionPrompt,\n },\n ],\n },\n ];\n\n if (typeof targetElementDescription !== 'string') {\n const addOns = await promptsToChatParam({\n images: targetElementDescription.images,\n convertHttpImage2Base64: targetElementDescription.convertHttpImage2Base64,\n });\n msgs.push(...addOns);\n }\n\n if (isAutoGLM(modelFamily)) {\n const { content: rawResponseContent, usage } =\n await callAIWithStringResponse(msgs, modelConfig, {\n abortSignal: options.abortSignal,\n });\n\n debugInspect('auto-glm rawResponse:', rawResponseContent);\n\n const parsed = parseAutoGLMLocateResponse(rawResponseContent);\n\n debugInspect('auto-glm thinking:', parsed.think);\n debugInspect('auto-glm coordinates:', parsed.coordinates);\n\n let resRect: Rect | undefined;\n let matchedElements: LocateResultElement[] = [];\n let errors: string[] = [];\n\n if (parsed.error || !parsed.coordinates) {\n errors = [parsed.error || 'Failed to parse auto-glm response'];\n debugInspect('auto-glm parse error:', errors[0]);\n } else {\n const { x, y } = parsed.coordinates;\n\n debugInspect('auto-glm coordinates [0-999]:', { x, y });\n\n // Convert auto-glm coordinates [0,999] to pixel bbox\n // Map from [0,999] to pixel coordinates\n const pixelX = Math.round((x * imageWidth) / 1000);\n const pixelY = Math.round((y * imageHeight) / 1000);\n\n debugInspect('auto-glm pixel coordinates:', { pixelX, pixelY });\n\n // Apply offset if searching in a cropped area\n let finalX = pixelX;\n let finalY = pixelY;\n if (options.searchConfig?.rect) {\n finalX += options.searchConfig.rect.left;\n finalY += options.searchConfig.rect.top;\n }\n\n const element: LocateResultElement = generateElementByPoint(\n [finalX, finalY],\n targetElementDescriptionText as string,\n );\n\n resRect = element.rect;\n debugInspect('auto-glm resRect:', resRect);\n\n if (element) {\n matchedElements = [element];\n }\n }\n\n return {\n rect: resRect,\n parseResult: {\n elements: matchedElements,\n errors,\n },\n rawResponse: rawResponseContent,\n usage,\n reasoning_content: parsed.think,\n };\n }\n\n let res: Awaited<\n ReturnType<\n typeof callAIWithObjectResponse<AIElementResponse | [number, number]>\n >\n >;\n try {\n res = await callAIWithObjectResponse<AIElementResponse | [number, number]>(\n msgs,\n modelConfig,\n { abortSignal: options.abortSignal },\n );\n } catch (callError) {\n // Return error with usage and rawResponse if available\n const errorMessage =\n callError instanceof Error ? callError.message : String(callError);\n const rawResponse =\n callError instanceof AIResponseParseError\n ? callError.rawResponse\n : errorMessage;\n const usage =\n callError instanceof AIResponseParseError ? callError.usage : undefined;\n return {\n rect: undefined,\n parseResult: {\n elements: [],\n errors: [`AI call error: ${errorMessage}`],\n },\n rawResponse,\n usage,\n reasoning_content: undefined,\n };\n }\n\n const rawResponse = JSON.stringify(res.content);\n\n let resRect: Rect | undefined;\n let matchedElements: LocateResultElement[] = [];\n let errors: string[] | undefined =\n 'errors' in res.content ? res.content.errors : [];\n try {\n if (\n 'bbox' in res.content &&\n Array.isArray(res.content.bbox) &&\n res.content.bbox.length >= 1\n ) {\n resRect = adaptBboxToRect(\n res.content.bbox,\n imageWidth,\n imageHeight,\n options.searchConfig?.rect?.left,\n options.searchConfig?.rect?.top,\n originalImageWidth,\n originalImageHeight,\n modelFamily,\n options.searchConfig?.scale,\n );\n\n debugInspect('resRect', resRect);\n\n const element: LocateResultElement = generateElementByRect(\n resRect,\n targetElementDescriptionText as string,\n );\n errors = [];\n\n if (element) {\n matchedElements = [element];\n }\n }\n } catch (e) {\n const msg =\n e instanceof Error\n ? `Failed to parse bbox: ${e.message}`\n : 'unknown error in locate';\n if (!errors || errors?.length === 0) {\n errors = [msg];\n } else {\n errors.push(`(${msg})`);\n }\n }\n\n return {\n rect: resRect,\n parseResult: {\n elements: matchedElements as LocateResultElement[],\n errors: errors as string[],\n },\n rawResponse,\n usage: res.usage,\n reasoning_content: res.reasoning_content,\n };\n}\n\nexport async function AiLocateSection(options: {\n context: UIContext;\n sectionDescription: TUserPrompt;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n}): Promise<{\n rect?: Rect;\n imageBase64?: string;\n scale?: number;\n error?: string;\n rawResponse: string;\n usage?: AIUsageInfo;\n}> {\n const { context, sectionDescription, modelConfig } = options;\n const { modelFamily } = modelConfig;\n const screenshotBase64 = context.screenshot.base64;\n\n const systemPrompt = systemPromptToLocateSection(modelFamily);\n const sectionLocatorInstructionText = sectionLocatorInstruction(\n extraTextFromUserPrompt(sectionDescription),\n );\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: screenshotBase64,\n detail: 'high',\n },\n },\n {\n type: 'text',\n text: sectionLocatorInstructionText,\n },\n ],\n },\n ];\n\n if (typeof sectionDescription !== 'string') {\n const addOns = await promptsToChatParam({\n images: sectionDescription.images,\n convertHttpImage2Base64: sectionDescription.convertHttpImage2Base64,\n });\n msgs.push(...addOns);\n }\n\n let result: Awaited<\n ReturnType<typeof callAIWithObjectResponse<AISectionLocatorResponse>>\n >;\n try {\n result = await callAIWithObjectResponse<AISectionLocatorResponse>(\n msgs,\n modelConfig,\n { abortSignal: options.abortSignal },\n );\n } catch (callError) {\n // Return error with usage and rawResponse if available\n const errorMessage =\n callError instanceof Error ? callError.message : String(callError);\n const rawResponse =\n callError instanceof AIResponseParseError\n ? callError.rawResponse\n : errorMessage;\n const usage =\n callError instanceof AIResponseParseError ? callError.usage : undefined;\n return {\n rect: undefined,\n imageBase64: undefined,\n error: `AI call error: ${errorMessage}`,\n rawResponse,\n usage,\n };\n }\n\n let searchAreaConfig:\n | Awaited<ReturnType<typeof buildSearchAreaConfig>>\n | undefined;\n const sectionBbox = result.content.bbox;\n if (sectionBbox) {\n const targetRect = adaptBboxToRect(\n sectionBbox,\n context.shotSize.width,\n context.shotSize.height,\n 0,\n 0,\n context.shotSize.width,\n context.shotSize.height,\n modelFamily,\n );\n debugSection('original targetRect %j', targetRect);\n\n const referenceBboxList = result.content.references_bbox || [];\n debugSection('referenceBboxList %j', referenceBboxList);\n\n const referenceRects = referenceBboxList\n .filter((bbox) => Array.isArray(bbox))\n .map((bbox) => {\n return adaptBboxToRect(\n bbox,\n context.shotSize.width,\n context.shotSize.height,\n 0,\n 0,\n context.shotSize.width,\n context.shotSize.height,\n modelFamily,\n );\n });\n debugSection('referenceRects %j', referenceRects);\n\n // merge the sectionRect and referenceRects\n const mergedRect = mergeRects([targetRect, ...referenceRects]);\n debugSection('mergedRect %j', mergedRect);\n\n const expandedRect = expandSearchArea(mergedRect, context.shotSize);\n const originalWidth = expandedRect.width;\n const originalHeight = expandedRect.height;\n debugSection('expanded sectionRect %j', expandedRect);\n\n searchAreaConfig = await buildSearchAreaConfig({\n context,\n baseRect: mergedRect,\n modelFamily,\n });\n\n debugSection(\n 'scaled sectionRect from %dx%d to %dx%d (scale=%d)',\n originalWidth,\n originalHeight,\n searchAreaConfig.rect.width,\n searchAreaConfig.rect.height,\n searchAreaConfig.scale,\n );\n }\n\n return {\n rect: searchAreaConfig?.rect,\n imageBase64: searchAreaConfig?.imageBase64,\n scale: searchAreaConfig?.scale,\n error: result.content.error,\n rawResponse: JSON.stringify(result.content),\n usage: result.usage,\n };\n}\n\nexport async function AiExtractElementInfo<T>(options: {\n dataQuery: string | Record<string, string>;\n multimodalPrompt?: TMultimodalPrompt;\n context: UIContext;\n pageDescription?: string;\n extractOption?: ServiceExtractOption;\n modelConfig: IModelConfig;\n}) {\n const { dataQuery, context, extractOption, multimodalPrompt, modelConfig } =\n options;\n const systemPrompt = systemPromptToExtract();\n const screenshotBase64 = context.screenshot.base64;\n\n const extractDataPromptText = extractDataQueryPrompt(\n options.pageDescription || '',\n dataQuery,\n );\n\n const userContent: ChatCompletionUserMessageParam['content'] = [];\n\n if (extractOption?.screenshotIncluded !== false) {\n userContent.push({\n type: 'image_url',\n image_url: {\n url: screenshotBase64,\n detail: 'high',\n },\n });\n }\n\n userContent.push({\n type: 'text',\n text: extractDataPromptText,\n });\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: userContent,\n },\n ];\n\n if (multimodalPrompt) {\n const addOns = await promptsToChatParam({\n images: multimodalPrompt.images,\n convertHttpImage2Base64: multimodalPrompt.convertHttpImage2Base64,\n });\n msgs.push(...addOns);\n }\n\n const {\n content: rawResponse,\n usage,\n reasoning_content,\n } = await callAI(msgs, modelConfig);\n\n // Parse XML response to JSON object\n let parseResult: AIDataExtractionResponse<T>;\n try {\n parseResult = parseXMLExtractionResponse<T>(rawResponse);\n } catch (parseError) {\n // Throw AIResponseParseError with usage and rawResponse preserved\n const errorMessage =\n parseError instanceof Error ? parseError.message : String(parseError);\n throw new AIResponseParseError(\n `XML parse error: ${errorMessage}`,\n rawResponse,\n usage,\n );\n }\n\n return {\n parseResult,\n rawResponse,\n usage,\n reasoning_content,\n };\n}\n\nexport async function AiJudgeOrderSensitive(\n description: string,\n callAIFn: typeof callAIWithObjectResponse<{ isOrderSensitive: boolean }>,\n modelConfig: IModelConfig,\n): Promise<{\n isOrderSensitive: boolean;\n usage?: AIUsageInfo;\n}> {\n const systemPrompt = systemPromptToJudgeOrderSensitive();\n const userPrompt = orderSensitiveJudgePrompt(description);\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: userPrompt,\n },\n ];\n\n const result = await callAIFn(msgs, modelConfig);\n\n return {\n isOrderSensitive: result.content.isOrderSensitive ?? false,\n usage: result.usage,\n };\n}\n"],"names":["debugInspect","getDebug","debugSection","buildSearchAreaConfig","options","context","baseRect","modelFamily","scaleRatio","sectionRect","expandSearchArea","croppedResult","cropByRect","scaledResult","scaleImage","extraTextFromUserPrompt","prompt","promptsToChatParam","multimodalPrompt","msgs","item","base64","preProcessImageUrl","AiLocateElement","targetElementDescription","modelConfig","screenshotBase64","assert","targetElementDescriptionText","userInstructionPrompt","findElementPrompt","systemPrompt","isAutoGLM","getAutoGLMLocatePrompt","systemPromptToLocateElement","imagePayload","imageWidth","imageHeight","originalImageWidth","originalImageHeight","paddedResult","paddingToMatchBlockByBase64","addOns","rawResponseContent","usage","callAIWithStringResponse","parsed","parseAutoGLMLocateResponse","resRect","matchedElements","errors","x","y","pixelX","Math","pixelY","finalX","finalY","element","generateElementByPoint","res","callAIWithObjectResponse","callError","errorMessage","Error","String","rawResponse","AIResponseParseError","undefined","JSON","Array","adaptBboxToRect","generateElementByRect","e","msg","AiLocateSection","sectionDescription","systemPromptToLocateSection","sectionLocatorInstructionText","sectionLocatorInstruction","result","searchAreaConfig","sectionBbox","targetRect","referenceBboxList","referenceRects","bbox","mergedRect","mergeRects","expandedRect","originalWidth","originalHeight","AiExtractElementInfo","dataQuery","extractOption","systemPromptToExtract","extractDataPromptText","extractDataQueryPrompt","userContent","reasoning_content","callAI","parseResult","parseXMLExtractionResponse","parseError","AiJudgeOrderSensitive","description","callAIFn","systemPromptToJudgeOrderSensitive","userPrompt","orderSensitiveJudgePrompt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-model/inspect.mjs","sources":["../../../src/ai-model/inspect.ts"],"sourcesContent":["import type {\n AIDataExtractionResponse,\n AIElementResponse,\n AISectionLocatorResponse,\n AIUsageInfo,\n Rect,\n ServiceExtractOption,\n UIContext,\n} from '@/types';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport {\n generateElementByPoint,\n generateElementByRect,\n} from '@midscene/shared/extractor/dom-util';\nimport {\n cropByRect,\n paddingToMatchBlockByBase64,\n preProcessImageUrl,\n scaleImage,\n} from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport type { LocateResultElement } from '@midscene/shared/types';\nimport { assert } from '@midscene/shared/utils';\nimport type {\n ChatCompletionSystemMessageParam,\n ChatCompletionUserMessageParam,\n} from 'openai/resources/index';\nimport type { TMultimodalPrompt, TUserPrompt } from '../common';\nimport { adaptBboxToRect, expandSearchArea, mergeRects } from '../common';\nimport { parseAutoGLMLocateResponse } from './auto-glm/parser';\nimport { getAutoGLMLocatePrompt } from './auto-glm/prompt';\nimport { isAutoGLM } from './auto-glm/util';\nimport {\n extractDataQueryPrompt,\n parseXMLExtractionResponse,\n systemPromptToExtract,\n} from './prompt/extraction';\nimport {\n findElementPrompt,\n systemPromptToLocateElement,\n} from './prompt/llm-locator';\nimport {\n sectionLocatorInstruction,\n systemPromptToLocateSection,\n} from './prompt/llm-section-locator';\nimport {\n orderSensitiveJudgePrompt,\n systemPromptToJudgeOrderSensitive,\n} from './prompt/order-sensitive-judge';\nimport {\n AIResponseParseError,\n callAI,\n callAIWithObjectResponse,\n callAIWithStringResponse,\n} from './service-caller/index';\n\nexport type AIArgs = [\n ChatCompletionSystemMessageParam,\n ...ChatCompletionUserMessageParam[],\n];\n\nconst debugInspect = getDebug('ai:inspect');\nconst debugSection = getDebug('ai:section');\n\nexport async function buildSearchAreaConfig(options: {\n context: UIContext;\n baseRect: Rect;\n modelFamily: IModelConfig['modelFamily'];\n}): Promise<{ rect: Rect; imageBase64: string; scale: number }> {\n const { context, baseRect, modelFamily } = options;\n const scaleRatio = 2;\n const sectionRect = expandSearchArea(baseRect, context.shotSize);\n\n const croppedResult = await cropByRect(\n context.screenshot.base64,\n sectionRect,\n modelFamily === 'qwen2.5-vl',\n );\n\n const scaledResult = await scaleImage(croppedResult.imageBase64, scaleRatio);\n sectionRect.width = scaledResult.width;\n sectionRect.height = scaledResult.height;\n return {\n rect: sectionRect,\n imageBase64: scaledResult.imageBase64,\n scale: scaleRatio,\n };\n}\n\nconst extraTextFromUserPrompt = (prompt: TUserPrompt): string => {\n if (typeof prompt === 'string') {\n return prompt;\n } else {\n return prompt.prompt;\n }\n};\n\nconst promptsToChatParam = async (\n multimodalPrompt: TMultimodalPrompt,\n): Promise<ChatCompletionUserMessageParam[]> => {\n const msgs: ChatCompletionUserMessageParam[] = [];\n if (multimodalPrompt?.images?.length) {\n msgs.push({\n role: 'user',\n content: [\n {\n type: 'text',\n text: 'Next, I will provide all the reference images.',\n },\n ],\n });\n\n for (const item of multimodalPrompt.images) {\n const base64 = await preProcessImageUrl(\n item.url,\n !!multimodalPrompt.convertHttpImage2Base64,\n );\n\n msgs.push({\n role: 'user',\n content: [\n {\n type: 'text',\n text: `this is the reference image named '${item.name}':`,\n },\n ],\n });\n\n msgs.push({\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: base64,\n detail: 'high',\n },\n },\n ],\n });\n }\n }\n return msgs;\n};\n\nexport async function AiLocateElement(options: {\n context: UIContext;\n targetElementDescription: TUserPrompt;\n searchConfig?: Awaited<ReturnType<typeof AiLocateSection>>;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n}): Promise<{\n parseResult: {\n elements: LocateResultElement[];\n errors?: string[];\n };\n rect?: Rect;\n rawResponse: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n}> {\n const { context, targetElementDescription, modelConfig } = options;\n const { modelFamily } = modelConfig;\n const screenshotBase64 = context.screenshot.base64;\n\n assert(\n targetElementDescription,\n 'cannot find the target element description',\n );\n const targetElementDescriptionText = extraTextFromUserPrompt(\n targetElementDescription,\n );\n const userInstructionPrompt = findElementPrompt(targetElementDescriptionText);\n const systemPrompt = isAutoGLM(modelFamily)\n ? getAutoGLMLocatePrompt(modelFamily)\n : systemPromptToLocateElement(modelFamily);\n\n let imagePayload = screenshotBase64;\n let imageWidth = context.shotSize.width;\n let imageHeight = context.shotSize.height;\n let originalImageWidth = imageWidth;\n let originalImageHeight = imageHeight;\n\n if (options.searchConfig) {\n assert(\n options.searchConfig.rect,\n 'searchArea is provided but its rect cannot be found. Failed to locate element',\n );\n assert(\n options.searchConfig.imageBase64,\n 'searchArea is provided but its imageBase64 cannot be found. Failed to locate element',\n );\n\n imagePayload = options.searchConfig.imageBase64;\n imageWidth = options.searchConfig.rect?.width;\n imageHeight = options.searchConfig.rect?.height;\n originalImageWidth = imageWidth;\n originalImageHeight = imageHeight;\n } else if (modelFamily === 'qwen2.5-vl') {\n const paddedResult = await paddingToMatchBlockByBase64(imagePayload);\n imageWidth = paddedResult.width;\n imageHeight = paddedResult.height;\n imagePayload = paddedResult.imageBase64;\n }\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n {\n type: 'text',\n text: isAutoGLM(modelFamily)\n ? `Tap: ${userInstructionPrompt}`\n : userInstructionPrompt,\n },\n ],\n },\n ];\n\n if (typeof targetElementDescription !== 'string') {\n const addOns = await promptsToChatParam({\n images: targetElementDescription.images,\n convertHttpImage2Base64: targetElementDescription.convertHttpImage2Base64,\n });\n msgs.push(...addOns);\n }\n\n if (isAutoGLM(modelFamily)) {\n const { content: rawResponseContent, usage } =\n await callAIWithStringResponse(msgs, modelConfig, {\n abortSignal: options.abortSignal,\n });\n\n debugInspect('auto-glm rawResponse:', rawResponseContent);\n\n const parsed = parseAutoGLMLocateResponse(rawResponseContent);\n\n debugInspect('auto-glm thinking:', parsed.think);\n debugInspect('auto-glm coordinates:', parsed.coordinates);\n\n let resRect: Rect | undefined;\n let matchedElements: LocateResultElement[] = [];\n let errors: string[] = [];\n\n if (parsed.error || !parsed.coordinates) {\n errors = [parsed.error || 'Failed to parse auto-glm response'];\n debugInspect('auto-glm parse error:', errors[0]);\n } else {\n const { x, y } = parsed.coordinates;\n\n debugInspect('auto-glm coordinates [0-999]:', { x, y });\n\n // Convert auto-glm coordinates [0,999] to pixel bbox\n // Map from [0,999] to pixel coordinates\n const pixelX = Math.round((x * imageWidth) / 1000);\n const pixelY = Math.round((y * imageHeight) / 1000);\n\n debugInspect('auto-glm pixel coordinates:', { pixelX, pixelY });\n\n // Apply offset if searching in a cropped area\n let finalX = pixelX;\n let finalY = pixelY;\n if (options.searchConfig?.rect) {\n finalX += options.searchConfig.rect.left;\n finalY += options.searchConfig.rect.top;\n }\n\n const element: LocateResultElement = generateElementByPoint(\n [finalX, finalY],\n targetElementDescriptionText as string,\n );\n\n resRect = element.rect;\n debugInspect('auto-glm resRect:', resRect);\n\n if (element) {\n matchedElements = [element];\n }\n }\n\n return {\n rect: resRect,\n parseResult: {\n elements: matchedElements,\n errors,\n },\n rawResponse: rawResponseContent,\n usage,\n reasoning_content: parsed.think,\n };\n }\n\n let res: Awaited<\n ReturnType<\n typeof callAIWithObjectResponse<AIElementResponse | [number, number]>\n >\n >;\n try {\n res = await callAIWithObjectResponse<AIElementResponse | [number, number]>(\n msgs,\n modelConfig,\n { abortSignal: options.abortSignal },\n );\n } catch (callError) {\n // Return error with usage and rawResponse if available\n const errorMessage =\n callError instanceof Error ? callError.message : String(callError);\n const rawResponse =\n callError instanceof AIResponseParseError\n ? callError.rawResponse\n : errorMessage;\n const usage =\n callError instanceof AIResponseParseError ? callError.usage : undefined;\n return {\n rect: undefined,\n parseResult: {\n elements: [],\n errors: [`AI call error: ${errorMessage}`],\n },\n rawResponse,\n usage,\n reasoning_content: undefined,\n };\n }\n\n const rawResponse = JSON.stringify(res.content);\n\n let resRect: Rect | undefined;\n let matchedElements: LocateResultElement[] = [];\n let errors: string[] | undefined =\n 'errors' in res.content ? res.content.errors : [];\n try {\n if (\n 'bbox' in res.content &&\n Array.isArray(res.content.bbox) &&\n res.content.bbox.length >= 1\n ) {\n resRect = adaptBboxToRect(\n res.content.bbox,\n imageWidth,\n imageHeight,\n options.searchConfig?.rect?.left,\n options.searchConfig?.rect?.top,\n originalImageWidth,\n originalImageHeight,\n modelFamily,\n options.searchConfig?.scale,\n );\n\n debugInspect('resRect', resRect);\n\n const element: LocateResultElement = generateElementByRect(\n resRect,\n targetElementDescriptionText as string,\n );\n errors = [];\n\n if (element) {\n matchedElements = [element];\n }\n }\n } catch (e) {\n const msg =\n e instanceof Error\n ? `Failed to parse bbox: ${e.message}`\n : 'unknown error in locate';\n if (!errors || errors?.length === 0) {\n errors = [msg];\n } else {\n errors.push(`(${msg})`);\n }\n }\n\n return {\n rect: resRect,\n parseResult: {\n elements: matchedElements as LocateResultElement[],\n errors: errors as string[],\n },\n rawResponse,\n usage: res.usage,\n reasoning_content: res.reasoning_content,\n };\n}\n\nexport async function AiLocateSection(options: {\n context: UIContext;\n sectionDescription: TUserPrompt;\n modelConfig: IModelConfig;\n abortSignal?: AbortSignal;\n}): Promise<{\n rect?: Rect;\n imageBase64?: string;\n scale?: number;\n error?: string;\n rawResponse: string;\n usage?: AIUsageInfo;\n}> {\n const { context, sectionDescription, modelConfig } = options;\n const { modelFamily } = modelConfig;\n const screenshotBase64 = context.screenshot.base64;\n\n const systemPrompt = systemPromptToLocateSection(modelFamily);\n const sectionLocatorInstructionText = sectionLocatorInstruction(\n extraTextFromUserPrompt(sectionDescription),\n );\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: screenshotBase64,\n detail: 'high',\n },\n },\n {\n type: 'text',\n text: sectionLocatorInstructionText,\n },\n ],\n },\n ];\n\n if (typeof sectionDescription !== 'string') {\n const addOns = await promptsToChatParam({\n images: sectionDescription.images,\n convertHttpImage2Base64: sectionDescription.convertHttpImage2Base64,\n });\n msgs.push(...addOns);\n }\n\n let result: Awaited<\n ReturnType<typeof callAIWithObjectResponse<AISectionLocatorResponse>>\n >;\n try {\n result = await callAIWithObjectResponse<AISectionLocatorResponse>(\n msgs,\n modelConfig,\n { abortSignal: options.abortSignal },\n );\n } catch (callError) {\n // Return error with usage and rawResponse if available\n const errorMessage =\n callError instanceof Error ? callError.message : String(callError);\n const rawResponse =\n callError instanceof AIResponseParseError\n ? callError.rawResponse\n : errorMessage;\n const usage =\n callError instanceof AIResponseParseError ? callError.usage : undefined;\n return {\n rect: undefined,\n imageBase64: undefined,\n error: `AI call error: ${errorMessage}`,\n rawResponse,\n usage,\n };\n }\n\n let searchAreaConfig:\n | Awaited<ReturnType<typeof buildSearchAreaConfig>>\n | undefined;\n const sectionBbox = result.content.bbox;\n if (sectionBbox) {\n const targetRect = adaptBboxToRect(\n sectionBbox,\n context.shotSize.width,\n context.shotSize.height,\n 0,\n 0,\n context.shotSize.width,\n context.shotSize.height,\n modelFamily,\n );\n debugSection('original targetRect %j', targetRect);\n\n const referenceBboxList = result.content.references_bbox || [];\n debugSection('referenceBboxList %j', referenceBboxList);\n\n const referenceRects = referenceBboxList\n .filter((bbox) => Array.isArray(bbox))\n .map((bbox) => {\n return adaptBboxToRect(\n bbox,\n context.shotSize.width,\n context.shotSize.height,\n 0,\n 0,\n context.shotSize.width,\n context.shotSize.height,\n modelFamily,\n );\n });\n debugSection('referenceRects %j', referenceRects);\n\n // merge the sectionRect and referenceRects\n const mergedRect = mergeRects([targetRect, ...referenceRects]);\n debugSection('mergedRect %j', mergedRect);\n\n const expandedRect = expandSearchArea(mergedRect, context.shotSize);\n const originalWidth = expandedRect.width;\n const originalHeight = expandedRect.height;\n debugSection('expanded sectionRect %j', expandedRect);\n\n searchAreaConfig = await buildSearchAreaConfig({\n context,\n baseRect: mergedRect,\n modelFamily,\n });\n\n debugSection(\n 'scaled sectionRect from %dx%d to %dx%d (scale=%d)',\n originalWidth,\n originalHeight,\n searchAreaConfig.rect.width,\n searchAreaConfig.rect.height,\n searchAreaConfig.scale,\n );\n }\n\n return {\n rect: searchAreaConfig?.rect,\n imageBase64: searchAreaConfig?.imageBase64,\n scale: searchAreaConfig?.scale,\n error: result.content.error,\n rawResponse: JSON.stringify(result.content),\n usage: result.usage,\n };\n}\n\nexport async function AiExtractElementInfo<T>(options: {\n dataQuery: string | Record<string, string>;\n multimodalPrompt?: TMultimodalPrompt;\n context: UIContext;\n pageDescription?: string;\n extractOption?: ServiceExtractOption;\n modelConfig: IModelConfig;\n}) {\n const { dataQuery, context, extractOption, multimodalPrompt, modelConfig } =\n options;\n const systemPrompt = systemPromptToExtract();\n const screenshotBase64 = context.screenshot.base64;\n\n const extractDataPromptText = extractDataQueryPrompt(\n options.pageDescription || '',\n dataQuery,\n );\n\n const userContent: ChatCompletionUserMessageParam['content'] = [];\n\n if (extractOption?.screenshotIncluded !== false) {\n userContent.push({\n type: 'image_url',\n image_url: {\n url: screenshotBase64,\n detail: 'high',\n },\n });\n }\n\n userContent.push({\n type: 'text',\n text: extractDataPromptText,\n });\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: userContent,\n },\n ];\n\n if (multimodalPrompt) {\n const addOns = await promptsToChatParam({\n images: multimodalPrompt.images,\n convertHttpImage2Base64: multimodalPrompt.convertHttpImage2Base64,\n });\n msgs.push(...addOns);\n }\n\n const {\n content: rawResponse,\n usage,\n reasoning_content,\n } = await callAI(msgs, modelConfig);\n\n // Parse XML response to JSON object\n let parseResult: AIDataExtractionResponse<T>;\n try {\n parseResult = parseXMLExtractionResponse<T>(rawResponse);\n } catch (parseError) {\n // Throw AIResponseParseError with usage and rawResponse preserved\n const errorMessage =\n parseError instanceof Error ? parseError.message : String(parseError);\n throw new AIResponseParseError(\n `XML parse error: ${errorMessage}`,\n rawResponse,\n usage,\n );\n }\n\n return {\n parseResult,\n rawResponse,\n usage,\n reasoning_content,\n };\n}\n\nexport async function AiJudgeOrderSensitive(\n description: string,\n callAIFn: typeof callAIWithObjectResponse<{ isOrderSensitive: boolean }>,\n modelConfig: IModelConfig,\n): Promise<{\n isOrderSensitive: boolean;\n usage?: AIUsageInfo;\n}> {\n const systemPrompt = systemPromptToJudgeOrderSensitive();\n const userPrompt = orderSensitiveJudgePrompt(description);\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: userPrompt,\n },\n ];\n\n debugInspect(\n 'AiJudgeOrderSensitive: deepThink=false, description=%s',\n description,\n );\n\n const result = await callAIFn(msgs, modelConfig, {\n deepThink: false,\n });\n\n return {\n isOrderSensitive: result.content.isOrderSensitive ?? false,\n usage: result.usage,\n };\n}\n"],"names":["debugInspect","getDebug","debugSection","buildSearchAreaConfig","options","context","baseRect","modelFamily","scaleRatio","sectionRect","expandSearchArea","croppedResult","cropByRect","scaledResult","scaleImage","extraTextFromUserPrompt","prompt","promptsToChatParam","multimodalPrompt","msgs","item","base64","preProcessImageUrl","AiLocateElement","targetElementDescription","modelConfig","screenshotBase64","assert","targetElementDescriptionText","userInstructionPrompt","findElementPrompt","systemPrompt","isAutoGLM","getAutoGLMLocatePrompt","systemPromptToLocateElement","imagePayload","imageWidth","imageHeight","originalImageWidth","originalImageHeight","paddedResult","paddingToMatchBlockByBase64","addOns","rawResponseContent","usage","callAIWithStringResponse","parsed","parseAutoGLMLocateResponse","resRect","matchedElements","errors","x","y","pixelX","Math","pixelY","finalX","finalY","element","generateElementByPoint","res","callAIWithObjectResponse","callError","errorMessage","Error","String","rawResponse","AIResponseParseError","undefined","JSON","Array","adaptBboxToRect","generateElementByRect","e","msg","AiLocateSection","sectionDescription","systemPromptToLocateSection","sectionLocatorInstructionText","sectionLocatorInstruction","result","searchAreaConfig","sectionBbox","targetRect","referenceBboxList","referenceRects","bbox","mergedRect","mergeRects","expandedRect","originalWidth","originalHeight","AiExtractElementInfo","dataQuery","extractOption","systemPromptToExtract","extractDataPromptText","extractDataQueryPrompt","userContent","reasoning_content","callAI","parseResult","parseXMLExtractionResponse","parseError","AiJudgeOrderSensitive","description","callAIFn","systemPromptToJudgeOrderSensitive","userPrompt","orderSensitiveJudgePrompt"],"mappings":"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"}
|
|
@@ -89,6 +89,54 @@ const createMessageContent = (promptText, screenshots = [], includeScreenshots =
|
|
|
89
89
|
}
|
|
90
90
|
return messageContent;
|
|
91
91
|
};
|
|
92
|
+
const getYamlLanguageInstruction = (language)=>{
|
|
93
|
+
const normalizedLanguage = language?.trim();
|
|
94
|
+
if (!normalizedLanguage) return '';
|
|
95
|
+
return `
|
|
96
|
+
Language requirement:
|
|
97
|
+
- Write all human-readable YAML content in ${normalizedLanguage}.
|
|
98
|
+
- Keep YAML keys, field names, and Midscene API names unchanged.`;
|
|
99
|
+
};
|
|
100
|
+
const createYamlPrompt = ({ yamlSummary, screenshots, language })=>{
|
|
101
|
+
const prompt = [
|
|
102
|
+
{
|
|
103
|
+
role: 'system',
|
|
104
|
+
content: `You are an expert in Midscene.js YAML test generation. Generate clean, accurate YAML following these rules: ${YAML_EXAMPLE_CODE}`
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
role: 'user',
|
|
108
|
+
content: `Generate YAML test for Midscene.js automation from recorded browser events.
|
|
109
|
+
|
|
110
|
+
Event Summary:
|
|
111
|
+
${JSON.stringify(yamlSummary, null, 2)}
|
|
112
|
+
|
|
113
|
+
Convert events:
|
|
114
|
+
- navigation → target.url
|
|
115
|
+
- click → aiTap with element description
|
|
116
|
+
- input → aiInput with value and locate
|
|
117
|
+
- scroll → aiScroll with appropriate direction
|
|
118
|
+
- Add aiAssert for important state changes${getYamlLanguageInstruction(language)}
|
|
119
|
+
|
|
120
|
+
Important: Return ONLY the raw YAML content. Do NOT wrap the response in markdown code blocks (no \`\`\`yaml or \`\`\`). Start directly with the YAML content.`
|
|
121
|
+
}
|
|
122
|
+
];
|
|
123
|
+
if (screenshots.length > 0) {
|
|
124
|
+
prompt.push({
|
|
125
|
+
role: 'user',
|
|
126
|
+
content: 'Here are screenshots from the recording session to help you understand the context:'
|
|
127
|
+
});
|
|
128
|
+
prompt.push({
|
|
129
|
+
role: 'user',
|
|
130
|
+
content: screenshots.map((screenshot)=>({
|
|
131
|
+
type: 'image_url',
|
|
132
|
+
image_url: {
|
|
133
|
+
url: screenshot
|
|
134
|
+
}
|
|
135
|
+
}))
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return prompt;
|
|
139
|
+
};
|
|
92
140
|
const validateEvents = (events)=>{
|
|
93
141
|
if (!events.length) throw new Error('No events provided for test generation');
|
|
94
142
|
};
|
|
@@ -104,43 +152,11 @@ const generateYamlTest = async (events, options, modelConfig)=>{
|
|
|
104
152
|
includeTimestamps: options.includeTimestamps || false
|
|
105
153
|
};
|
|
106
154
|
const screenshots = getScreenshotsForLLM(events, options.maxScreenshots || 3);
|
|
107
|
-
const prompt =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{
|
|
113
|
-
role: 'user',
|
|
114
|
-
content: `Generate YAML test for Midscene.js automation from recorded browser events.
|
|
115
|
-
|
|
116
|
-
Event Summary:
|
|
117
|
-
${JSON.stringify(yamlSummary, null, 2)}
|
|
118
|
-
|
|
119
|
-
Convert events:
|
|
120
|
-
- navigation → target.url
|
|
121
|
-
- click → aiTap with element description
|
|
122
|
-
- input → aiInput with value and locate
|
|
123
|
-
- scroll → aiScroll with appropriate direction
|
|
124
|
-
- Add aiAssert for important state changes
|
|
125
|
-
|
|
126
|
-
Important: Return ONLY the raw YAML content. Do NOT wrap the response in markdown code blocks (no \`\`\`yaml or \`\`\`). Start directly with the YAML content.`
|
|
127
|
-
}
|
|
128
|
-
];
|
|
129
|
-
if (screenshots.length > 0) {
|
|
130
|
-
prompt.push({
|
|
131
|
-
role: 'user',
|
|
132
|
-
content: 'Here are screenshots from the recording session to help you understand the context:'
|
|
133
|
-
});
|
|
134
|
-
prompt.push({
|
|
135
|
-
role: 'user',
|
|
136
|
-
content: screenshots.map((screenshot)=>({
|
|
137
|
-
type: 'image_url',
|
|
138
|
-
image_url: {
|
|
139
|
-
url: screenshot
|
|
140
|
-
}
|
|
141
|
-
}))
|
|
142
|
-
});
|
|
143
|
-
}
|
|
155
|
+
const prompt = createYamlPrompt({
|
|
156
|
+
yamlSummary,
|
|
157
|
+
screenshots,
|
|
158
|
+
language: options.language
|
|
159
|
+
});
|
|
144
160
|
const response = await callAIWithStringResponse(prompt, modelConfig);
|
|
145
161
|
if (response?.content && 'string' == typeof response.content) return response.content;
|
|
146
162
|
throw new Error('Failed to generate YAML test configuration');
|
|
@@ -160,43 +176,11 @@ const generateYamlTestStream = async (events, options, modelConfig)=>{
|
|
|
160
176
|
includeTimestamps: options.includeTimestamps || false
|
|
161
177
|
};
|
|
162
178
|
const screenshots = getScreenshotsForLLM(events, options.maxScreenshots || 3);
|
|
163
|
-
const prompt =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
{
|
|
169
|
-
role: 'user',
|
|
170
|
-
content: `Generate YAML test for Midscene.js automation from recorded browser events.
|
|
171
|
-
|
|
172
|
-
Event Summary:
|
|
173
|
-
${JSON.stringify(yamlSummary, null, 2)}
|
|
174
|
-
|
|
175
|
-
Convert events:
|
|
176
|
-
- navigation → target.url
|
|
177
|
-
- click → aiTap with element description
|
|
178
|
-
- input → aiInput with value and locate
|
|
179
|
-
- scroll → aiScroll with appropriate direction
|
|
180
|
-
- Add aiAssert for important state changes
|
|
181
|
-
|
|
182
|
-
Important: Return ONLY the raw YAML content. Do NOT wrap the response in markdown code blocks (no \`\`\`yaml or \`\`\`). Start directly with the YAML content.`
|
|
183
|
-
}
|
|
184
|
-
];
|
|
185
|
-
if (screenshots.length > 0) {
|
|
186
|
-
prompt.push({
|
|
187
|
-
role: 'user',
|
|
188
|
-
content: 'Here are screenshots from the recording session to help you understand the context:'
|
|
189
|
-
});
|
|
190
|
-
prompt.push({
|
|
191
|
-
role: 'user',
|
|
192
|
-
content: screenshots.map((screenshot)=>({
|
|
193
|
-
type: 'image_url',
|
|
194
|
-
image_url: {
|
|
195
|
-
url: screenshot
|
|
196
|
-
}
|
|
197
|
-
}))
|
|
198
|
-
});
|
|
199
|
-
}
|
|
179
|
+
const prompt = createYamlPrompt({
|
|
180
|
+
yamlSummary,
|
|
181
|
+
screenshots,
|
|
182
|
+
language: options.language
|
|
183
|
+
});
|
|
200
184
|
if (options.stream && options.onChunk) return await callAI(prompt, modelConfig, {
|
|
201
185
|
stream: true,
|
|
202
186
|
onChunk: options.onChunk
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/prompt/yaml-generator.mjs","sources":["../../../../src/ai-model/prompt/yaml-generator.ts"],"sourcesContent":["import type {\n StreamingAIResponse,\n StreamingCodeGenerationOptions,\n} from '@/types';\nimport { YAML_EXAMPLE_CODE } from '@midscene/shared/constants';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport {\n type ChatCompletionMessageParam,\n callAI,\n callAIWithStringResponse,\n} from '../index';\n\n// Common interfaces for test generation (shared between YAML and Playwright)\nexport interface EventCounts {\n navigation: number;\n click: number;\n input: number;\n scroll: number;\n total: number;\n}\n\nexport interface InputDescription {\n description: string;\n value: string;\n}\n\nexport interface ProcessedEvent {\n type: string;\n timestamp: number;\n url?: string;\n title?: string;\n elementDescription?: string;\n value?: string;\n pageInfo?: any;\n elementRect?: any;\n}\n\nexport interface EventSummary {\n testName: string;\n startUrl: string;\n eventCounts: EventCounts;\n urls: string[];\n clickDescriptions: string[];\n inputDescriptions: InputDescription[];\n events: ProcessedEvent[];\n}\n\n// Common ChromeRecordedEvent interface\nexport interface ChromeRecordedEvent {\n type: string;\n timestamp: number;\n url?: string;\n title?: string;\n elementDescription?: string;\n value?: string;\n pageInfo?: any;\n elementRect?: any;\n screenshotBefore?: string;\n screenshotAfter?: string;\n screenshotWithBox?: string;\n}\n\nexport interface YamlGenerationOptions {\n testName?: string;\n includeTimestamps?: boolean;\n maxScreenshots?: number;\n description?: string;\n}\n\nexport interface FilteredEvents {\n navigationEvents: ChromeRecordedEvent[];\n clickEvents: ChromeRecordedEvent[];\n inputEvents: ChromeRecordedEvent[];\n scrollEvents: ChromeRecordedEvent[];\n}\n\n// Common utility functions (shared between YAML and Playwright generators)\n\n/**\n * Get screenshots from events for LLM context\n */\nexport const getScreenshotsForLLM = (\n events: ChromeRecordedEvent[],\n maxScreenshots = 1,\n): string[] => {\n // Find events with screenshots, prioritizing navigation and click events\n const eventsWithScreenshots = events.filter(\n (event) =>\n event.screenshotBefore ||\n event.screenshotAfter ||\n event.screenshotWithBox,\n );\n\n // Sort them by priority (navigation first, then clicks, then others)\n const sortedEvents = [...eventsWithScreenshots].sort((a, b) => {\n if (a.type === 'navigation' && b.type !== 'navigation') return -1;\n if (a.type !== 'navigation' && b.type === 'navigation') return 1;\n if (a.type === 'click' && b.type !== 'click') return -1;\n if (a.type !== 'click' && b.type === 'click') return 1;\n return 0;\n });\n\n // Extract up to maxScreenshots screenshots\n const screenshots: string[] = [];\n for (const event of sortedEvents) {\n // Prefer the most informative screenshot\n const screenshot =\n event.screenshotWithBox ||\n event.screenshotAfter ||\n event.screenshotBefore;\n if (screenshot && !screenshots.includes(screenshot)) {\n screenshots.push(screenshot);\n if (screenshots.length >= maxScreenshots) break;\n }\n }\n\n return screenshots;\n};\n\n/**\n * Filter events by type for easier processing\n */\nexport const filterEventsByType = (\n events: ChromeRecordedEvent[],\n): FilteredEvents => {\n return {\n navigationEvents: events.filter((event) => event.type === 'navigation'),\n clickEvents: events.filter((event) => event.type === 'click'),\n inputEvents: events.filter((event) => event.type === 'input'),\n scrollEvents: events.filter((event) => event.type === 'scroll'),\n };\n};\n\n/**\n * Create event counts summary\n */\nexport const createEventCounts = (\n filteredEvents: FilteredEvents,\n totalEvents: number,\n): EventCounts => {\n return {\n navigation: filteredEvents.navigationEvents.length,\n click: filteredEvents.clickEvents.length,\n input: filteredEvents.inputEvents.length,\n scroll: filteredEvents.scrollEvents.length,\n total: totalEvents,\n };\n};\n\n/**\n * Extract input descriptions from input events\n */\nexport const extractInputDescriptions = (\n inputEvents: ChromeRecordedEvent[],\n): InputDescription[] => {\n return inputEvents\n .map((event) => ({\n description: event.elementDescription || '',\n value: event.value || '',\n }))\n .filter((item) => item.description && item.value);\n};\n\n/**\n * Process events for LLM consumption\n */\nexport const processEventsForLLM = (\n events: ChromeRecordedEvent[],\n): ProcessedEvent[] => {\n return events.map((event) => ({\n type: event.type,\n timestamp: event.timestamp,\n url: event.url,\n title: event.title,\n elementDescription: event.elementDescription,\n value: event.value,\n pageInfo: event.pageInfo,\n elementRect: event.elementRect,\n }));\n};\n\n/**\n * Prepare comprehensive event summary for LLM\n */\nexport const prepareEventSummary = (\n events: ChromeRecordedEvent[],\n options: { testName?: string; maxScreenshots?: number } = {},\n): EventSummary => {\n const filteredEvents = filterEventsByType(events);\n const eventCounts = createEventCounts(filteredEvents, events.length);\n\n // Extract useful information from events\n const startUrl =\n filteredEvents.navigationEvents.length > 0\n ? filteredEvents.navigationEvents[0].url || ''\n : '';\n\n const clickDescriptions = filteredEvents.clickEvents\n .map((event) => event.elementDescription)\n .filter((desc): desc is string => Boolean(desc))\n .slice(0, 10);\n\n const inputDescriptions = extractInputDescriptions(\n filteredEvents.inputEvents,\n ).slice(0, 10);\n\n const urls = filteredEvents.navigationEvents\n .map((e) => e.url)\n .filter((url): url is string => Boolean(url))\n .slice(0, 5);\n\n const processedEvents = processEventsForLLM(events);\n\n return {\n testName: options.testName || 'Automated test from recorded events',\n startUrl,\n eventCounts,\n urls,\n clickDescriptions,\n inputDescriptions,\n events: processedEvents,\n };\n};\n\n/**\n * Create message content for LLM with optional screenshots\n */\nexport const createMessageContent = (\n promptText: string,\n screenshots: string[] = [],\n includeScreenshots = true,\n) => {\n const messageContent: any[] = [\n {\n type: 'text',\n text: promptText,\n },\n ];\n\n // Add screenshots if available and requested\n if (includeScreenshots && screenshots.length > 0) {\n messageContent.unshift({\n type: 'text',\n text: 'Here are screenshots from the recording session to help you understand the context:',\n });\n\n screenshots.forEach((screenshot) => {\n messageContent.push({\n type: 'image_url',\n image_url: {\n url: screenshot,\n },\n });\n });\n }\n\n return messageContent;\n};\n\n/**\n * Validate events before processing\n */\nexport const validateEvents = (events: ChromeRecordedEvent[]): void => {\n if (!events.length) {\n throw new Error('No events provided for test generation');\n }\n};\n\n// YAML-specific generation functions\n\n/**\n * Generates YAML test configuration from recorded events using AI\n */\nexport const generateYamlTest = async (\n events: ChromeRecordedEvent[],\n options: YamlGenerationOptions,\n modelConfig: IModelConfig,\n): Promise<string> => {\n try {\n // Validate input\n validateEvents(events);\n\n // Prepare event summary using shared utilities\n const summary = prepareEventSummary(events, {\n testName: options.testName,\n maxScreenshots: options.maxScreenshots || 3,\n });\n\n // Add YAML-specific options to summary\n const yamlSummary = {\n ...summary,\n includeTimestamps: options.includeTimestamps || false,\n };\n\n // Get screenshots for visual context\n const screenshots = getScreenshotsForLLM(\n events,\n options.maxScreenshots || 3,\n );\n\n // Use LLM to generate the YAML test configuration\n const prompt: ChatCompletionMessageParam[] = [\n {\n role: 'system',\n content: `You are an expert in Midscene.js YAML test generation. Generate clean, accurate YAML following these rules: ${YAML_EXAMPLE_CODE}`,\n },\n {\n role: 'user',\n content: `Generate YAML test for Midscene.js automation from recorded browser events.\n\nEvent Summary:\n${JSON.stringify(yamlSummary, null, 2)}\n\nConvert events:\n- navigation → target.url\n- click → aiTap with element description\n- input → aiInput with value and locate\n- scroll → aiScroll with appropriate direction\n- Add aiAssert for important state changes\n\nImportant: Return ONLY the raw YAML content. Do NOT wrap the response in markdown code blocks (no \\`\\`\\`yaml or \\`\\`\\`). Start directly with the YAML content.`,\n },\n ];\n\n // Add screenshots if available and requested\n if (screenshots.length > 0) {\n prompt.push({\n role: 'user',\n content:\n 'Here are screenshots from the recording session to help you understand the context:',\n });\n\n prompt.push({\n role: 'user',\n content: screenshots.map((screenshot) => ({\n type: 'image_url',\n image_url: {\n url: screenshot,\n },\n })),\n });\n }\n\n const response = await callAIWithStringResponse(prompt, modelConfig);\n\n if (response?.content && typeof response.content === 'string') {\n return response.content;\n }\n\n throw new Error('Failed to generate YAML test configuration');\n } catch (error) {\n throw new Error(`Failed to generate YAML test: ${error}`);\n }\n};\n\n/**\n * Generates YAML test configuration from recorded events using AI with streaming support\n */\nexport const generateYamlTestStream = async (\n events: ChromeRecordedEvent[],\n options: YamlGenerationOptions & StreamingCodeGenerationOptions,\n modelConfig: IModelConfig,\n): Promise<StreamingAIResponse> => {\n try {\n // Validate input\n validateEvents(events);\n\n // Prepare event summary using shared utilities\n const summary = prepareEventSummary(events, {\n testName: options.testName,\n maxScreenshots: options.maxScreenshots || 3,\n });\n\n // Add YAML-specific options to summary\n const yamlSummary = {\n ...summary,\n includeTimestamps: options.includeTimestamps || false,\n };\n\n // Get screenshots for visual context\n const screenshots = getScreenshotsForLLM(\n events,\n options.maxScreenshots || 3,\n );\n\n // Use LLM to generate the YAML test configuration\n const prompt: ChatCompletionMessageParam[] = [\n {\n role: 'system',\n content: `You are an expert in Midscene.js YAML test generation. Generate clean, accurate YAML following these rules: ${YAML_EXAMPLE_CODE}`,\n },\n {\n role: 'user',\n content: `Generate YAML test for Midscene.js automation from recorded browser events.\n\nEvent Summary:\n${JSON.stringify(yamlSummary, null, 2)}\n\nConvert events:\n- navigation → target.url\n- click → aiTap with element description\n- input → aiInput with value and locate\n- scroll → aiScroll with appropriate direction\n- Add aiAssert for important state changes\n\nImportant: Return ONLY the raw YAML content. Do NOT wrap the response in markdown code blocks (no \\`\\`\\`yaml or \\`\\`\\`). Start directly with the YAML content.`,\n },\n ];\n\n // Add screenshots if available and requested\n if (screenshots.length > 0) {\n prompt.push({\n role: 'user',\n content:\n 'Here are screenshots from the recording session to help you understand the context:',\n });\n\n prompt.push({\n role: 'user',\n content: screenshots.map((screenshot) => ({\n type: 'image_url',\n image_url: {\n url: screenshot,\n },\n })),\n });\n }\n\n if (options.stream && options.onChunk) {\n // Use streaming\n return await callAI(prompt, modelConfig, {\n stream: true,\n onChunk: options.onChunk,\n });\n } else {\n // Fallback to non-streaming\n const response = await callAIWithStringResponse(prompt, modelConfig);\n\n if (response?.content && typeof response.content === 'string') {\n return {\n content: response.content,\n usage: response.usage,\n isStreamed: false,\n };\n }\n\n throw new Error('Failed to generate YAML test configuration');\n }\n } catch (error) {\n throw new Error(`Failed to generate YAML test: ${error}`);\n }\n};\n"],"names":["getScreenshotsForLLM","events","maxScreenshots","eventsWithScreenshots","event","sortedEvents","a","b","screenshots","screenshot","filterEventsByType","createEventCounts","filteredEvents","totalEvents","extractInputDescriptions","inputEvents","item","processEventsForLLM","prepareEventSummary","options","eventCounts","startUrl","clickDescriptions","desc","Boolean","inputDescriptions","urls","e","url","processedEvents","createMessageContent","promptText","includeScreenshots","messageContent","validateEvents","Error","generateYamlTest","modelConfig","summary","yamlSummary","prompt","YAML_EXAMPLE_CODE","JSON","response","callAIWithStringResponse","error","generateYamlTestStream","callAI"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-model/prompt/yaml-generator.mjs","sources":["../../../../src/ai-model/prompt/yaml-generator.ts"],"sourcesContent":["import type {\n StreamingAIResponse,\n StreamingCodeGenerationOptions,\n} from '@/types';\nimport { YAML_EXAMPLE_CODE } from '@midscene/shared/constants';\nimport type { IModelConfig } from '@midscene/shared/env';\nimport {\n type ChatCompletionMessageParam,\n callAI,\n callAIWithStringResponse,\n} from '../index';\n\n// Common interfaces for test generation (shared between YAML and Playwright)\nexport interface EventCounts {\n navigation: number;\n click: number;\n input: number;\n scroll: number;\n total: number;\n}\n\nexport interface InputDescription {\n description: string;\n value: string;\n}\n\nexport interface ProcessedEvent {\n type: string;\n timestamp: number;\n url?: string;\n title?: string;\n elementDescription?: string;\n value?: string;\n pageInfo?: any;\n elementRect?: any;\n}\n\nexport interface EventSummary {\n testName: string;\n startUrl: string;\n eventCounts: EventCounts;\n urls: string[];\n clickDescriptions: string[];\n inputDescriptions: InputDescription[];\n events: ProcessedEvent[];\n}\n\n// Common ChromeRecordedEvent interface\nexport interface ChromeRecordedEvent {\n type: string;\n timestamp: number;\n url?: string;\n title?: string;\n elementDescription?: string;\n value?: string;\n pageInfo?: any;\n elementRect?: any;\n screenshotBefore?: string;\n screenshotAfter?: string;\n screenshotWithBox?: string;\n}\n\nexport interface YamlGenerationOptions {\n testName?: string;\n includeTimestamps?: boolean;\n maxScreenshots?: number;\n description?: string;\n /** Language for human-readable YAML content (e.g. 'English', 'Chinese'). Keys and API names are kept as-is. */\n language?: string;\n}\n\nexport interface FilteredEvents {\n navigationEvents: ChromeRecordedEvent[];\n clickEvents: ChromeRecordedEvent[];\n inputEvents: ChromeRecordedEvent[];\n scrollEvents: ChromeRecordedEvent[];\n}\n\n// Common utility functions (shared between YAML and Playwright generators)\n\n/**\n * Get screenshots from events for LLM context\n */\nexport const getScreenshotsForLLM = (\n events: ChromeRecordedEvent[],\n maxScreenshots = 1,\n): string[] => {\n // Find events with screenshots, prioritizing navigation and click events\n const eventsWithScreenshots = events.filter(\n (event) =>\n event.screenshotBefore ||\n event.screenshotAfter ||\n event.screenshotWithBox,\n );\n\n // Sort them by priority (navigation first, then clicks, then others)\n const sortedEvents = [...eventsWithScreenshots].sort((a, b) => {\n if (a.type === 'navigation' && b.type !== 'navigation') return -1;\n if (a.type !== 'navigation' && b.type === 'navigation') return 1;\n if (a.type === 'click' && b.type !== 'click') return -1;\n if (a.type !== 'click' && b.type === 'click') return 1;\n return 0;\n });\n\n // Extract up to maxScreenshots screenshots\n const screenshots: string[] = [];\n for (const event of sortedEvents) {\n // Prefer the most informative screenshot\n const screenshot =\n event.screenshotWithBox ||\n event.screenshotAfter ||\n event.screenshotBefore;\n if (screenshot && !screenshots.includes(screenshot)) {\n screenshots.push(screenshot);\n if (screenshots.length >= maxScreenshots) break;\n }\n }\n\n return screenshots;\n};\n\n/**\n * Filter events by type for easier processing\n */\nexport const filterEventsByType = (\n events: ChromeRecordedEvent[],\n): FilteredEvents => {\n return {\n navigationEvents: events.filter((event) => event.type === 'navigation'),\n clickEvents: events.filter((event) => event.type === 'click'),\n inputEvents: events.filter((event) => event.type === 'input'),\n scrollEvents: events.filter((event) => event.type === 'scroll'),\n };\n};\n\n/**\n * Create event counts summary\n */\nexport const createEventCounts = (\n filteredEvents: FilteredEvents,\n totalEvents: number,\n): EventCounts => {\n return {\n navigation: filteredEvents.navigationEvents.length,\n click: filteredEvents.clickEvents.length,\n input: filteredEvents.inputEvents.length,\n scroll: filteredEvents.scrollEvents.length,\n total: totalEvents,\n };\n};\n\n/**\n * Extract input descriptions from input events\n */\nexport const extractInputDescriptions = (\n inputEvents: ChromeRecordedEvent[],\n): InputDescription[] => {\n return inputEvents\n .map((event) => ({\n description: event.elementDescription || '',\n value: event.value || '',\n }))\n .filter((item) => item.description && item.value);\n};\n\n/**\n * Process events for LLM consumption\n */\nexport const processEventsForLLM = (\n events: ChromeRecordedEvent[],\n): ProcessedEvent[] => {\n return events.map((event) => ({\n type: event.type,\n timestamp: event.timestamp,\n url: event.url,\n title: event.title,\n elementDescription: event.elementDescription,\n value: event.value,\n pageInfo: event.pageInfo,\n elementRect: event.elementRect,\n }));\n};\n\n/**\n * Prepare comprehensive event summary for LLM\n */\nexport const prepareEventSummary = (\n events: ChromeRecordedEvent[],\n options: { testName?: string; maxScreenshots?: number } = {},\n): EventSummary => {\n const filteredEvents = filterEventsByType(events);\n const eventCounts = createEventCounts(filteredEvents, events.length);\n\n // Extract useful information from events\n const startUrl =\n filteredEvents.navigationEvents.length > 0\n ? filteredEvents.navigationEvents[0].url || ''\n : '';\n\n const clickDescriptions = filteredEvents.clickEvents\n .map((event) => event.elementDescription)\n .filter((desc): desc is string => Boolean(desc))\n .slice(0, 10);\n\n const inputDescriptions = extractInputDescriptions(\n filteredEvents.inputEvents,\n ).slice(0, 10);\n\n const urls = filteredEvents.navigationEvents\n .map((e) => e.url)\n .filter((url): url is string => Boolean(url))\n .slice(0, 5);\n\n const processedEvents = processEventsForLLM(events);\n\n return {\n testName: options.testName || 'Automated test from recorded events',\n startUrl,\n eventCounts,\n urls,\n clickDescriptions,\n inputDescriptions,\n events: processedEvents,\n };\n};\n\n/**\n * Create message content for LLM with optional screenshots\n */\nexport const createMessageContent = (\n promptText: string,\n screenshots: string[] = [],\n includeScreenshots = true,\n) => {\n const messageContent: any[] = [\n {\n type: 'text',\n text: promptText,\n },\n ];\n\n // Add screenshots if available and requested\n if (includeScreenshots && screenshots.length > 0) {\n messageContent.unshift({\n type: 'text',\n text: 'Here are screenshots from the recording session to help you understand the context:',\n });\n\n screenshots.forEach((screenshot) => {\n messageContent.push({\n type: 'image_url',\n image_url: {\n url: screenshot,\n },\n });\n });\n }\n\n return messageContent;\n};\n\nconst getYamlLanguageInstruction = (language?: string) => {\n const normalizedLanguage = language?.trim();\n if (!normalizedLanguage) {\n return '';\n }\n\n return `\nLanguage requirement:\n- Write all human-readable YAML content in ${normalizedLanguage}.\n- Keep YAML keys, field names, and Midscene API names unchanged.`;\n};\n\nconst createYamlPrompt = ({\n yamlSummary,\n screenshots,\n language,\n}: {\n yamlSummary: EventSummary & { includeTimestamps: boolean };\n screenshots: string[];\n language?: string;\n}): ChatCompletionMessageParam[] => {\n const prompt: ChatCompletionMessageParam[] = [\n {\n role: 'system',\n content: `You are an expert in Midscene.js YAML test generation. Generate clean, accurate YAML following these rules: ${YAML_EXAMPLE_CODE}`,\n },\n {\n role: 'user',\n content: `Generate YAML test for Midscene.js automation from recorded browser events.\n\nEvent Summary:\n${JSON.stringify(yamlSummary, null, 2)}\n\nConvert events:\n- navigation → target.url\n- click → aiTap with element description\n- input → aiInput with value and locate\n- scroll → aiScroll with appropriate direction\n- Add aiAssert for important state changes${getYamlLanguageInstruction(language)}\n\nImportant: Return ONLY the raw YAML content. Do NOT wrap the response in markdown code blocks (no \\`\\`\\`yaml or \\`\\`\\`). Start directly with the YAML content.`,\n },\n ];\n\n if (screenshots.length > 0) {\n prompt.push({\n role: 'user',\n content:\n 'Here are screenshots from the recording session to help you understand the context:',\n });\n\n prompt.push({\n role: 'user',\n content: screenshots.map((screenshot) => ({\n type: 'image_url',\n image_url: {\n url: screenshot,\n },\n })),\n });\n }\n\n return prompt;\n};\n\n/**\n * Validate events before processing\n */\nexport const validateEvents = (events: ChromeRecordedEvent[]): void => {\n if (!events.length) {\n throw new Error('No events provided for test generation');\n }\n};\n\n// YAML-specific generation functions\n\n/**\n * Generates YAML test configuration from recorded events using AI\n */\nexport const generateYamlTest = async (\n events: ChromeRecordedEvent[],\n options: YamlGenerationOptions,\n modelConfig: IModelConfig,\n): Promise<string> => {\n try {\n // Validate input\n validateEvents(events);\n\n // Prepare event summary using shared utilities\n const summary = prepareEventSummary(events, {\n testName: options.testName,\n maxScreenshots: options.maxScreenshots || 3,\n });\n\n // Add YAML-specific options to summary\n const yamlSummary = {\n ...summary,\n includeTimestamps: options.includeTimestamps || false,\n };\n\n // Get screenshots for visual context\n const screenshots = getScreenshotsForLLM(\n events,\n options.maxScreenshots || 3,\n );\n\n const prompt = createYamlPrompt({\n yamlSummary,\n screenshots,\n language: options.language,\n });\n\n const response = await callAIWithStringResponse(prompt, modelConfig);\n\n if (response?.content && typeof response.content === 'string') {\n return response.content;\n }\n\n throw new Error('Failed to generate YAML test configuration');\n } catch (error) {\n throw new Error(`Failed to generate YAML test: ${error}`);\n }\n};\n\n/**\n * Generates YAML test configuration from recorded events using AI with streaming support\n */\nexport const generateYamlTestStream = async (\n events: ChromeRecordedEvent[],\n options: YamlGenerationOptions & StreamingCodeGenerationOptions,\n modelConfig: IModelConfig,\n): Promise<StreamingAIResponse> => {\n try {\n // Validate input\n validateEvents(events);\n\n // Prepare event summary using shared utilities\n const summary = prepareEventSummary(events, {\n testName: options.testName,\n maxScreenshots: options.maxScreenshots || 3,\n });\n\n // Add YAML-specific options to summary\n const yamlSummary = {\n ...summary,\n includeTimestamps: options.includeTimestamps || false,\n };\n\n // Get screenshots for visual context\n const screenshots = getScreenshotsForLLM(\n events,\n options.maxScreenshots || 3,\n );\n\n const prompt = createYamlPrompt({\n yamlSummary,\n screenshots,\n language: options.language,\n });\n\n if (options.stream && options.onChunk) {\n // Use streaming\n return await callAI(prompt, modelConfig, {\n stream: true,\n onChunk: options.onChunk,\n });\n } else {\n // Fallback to non-streaming\n const response = await callAIWithStringResponse(prompt, modelConfig);\n\n if (response?.content && typeof response.content === 'string') {\n return {\n content: response.content,\n usage: response.usage,\n isStreamed: false,\n };\n }\n\n throw new Error('Failed to generate YAML test configuration');\n }\n } catch (error) {\n throw new Error(`Failed to generate YAML test: ${error}`);\n }\n};\n"],"names":["getScreenshotsForLLM","events","maxScreenshots","eventsWithScreenshots","event","sortedEvents","a","b","screenshots","screenshot","filterEventsByType","createEventCounts","filteredEvents","totalEvents","extractInputDescriptions","inputEvents","item","processEventsForLLM","prepareEventSummary","options","eventCounts","startUrl","clickDescriptions","desc","Boolean","inputDescriptions","urls","e","url","processedEvents","createMessageContent","promptText","includeScreenshots","messageContent","getYamlLanguageInstruction","language","normalizedLanguage","createYamlPrompt","yamlSummary","prompt","YAML_EXAMPLE_CODE","JSON","validateEvents","Error","generateYamlTest","modelConfig","summary","response","callAIWithStringResponse","error","generateYamlTestStream","callAI"],"mappings":"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"}
|
|
@@ -382,7 +382,7 @@ function resolveReasoningConfig({ reasoningEnabled, reasoningEffort, reasoningBu
|
|
|
382
382
|
};
|
|
383
383
|
const debugMessages = [];
|
|
384
384
|
const config = {};
|
|
385
|
-
if ('qwen3-vl' === modelFamily || 'qwen3.5' === modelFamily) {
|
|
385
|
+
if ('qwen3-vl' === modelFamily || 'qwen3.5' === modelFamily || 'qwen3.6' === modelFamily) {
|
|
386
386
|
if (void 0 !== reasoningEnabled) {
|
|
387
387
|
config.enable_thinking = reasoningEnabled;
|
|
388
388
|
debugMessages.push(`enable_thinking=${reasoningEnabled}`);
|