@midscene/core 0.26.7-beta-20250818081955.0 → 0.26.7-beta-20250820105545.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/ai-model/common.mjs +58 -16
- package/dist/es/ai-model/common.mjs.map +1 -1
- package/dist/es/ai-model/index.mjs +3 -3
- package/dist/es/ai-model/inspect.mjs +28 -16
- package/dist/es/ai-model/inspect.mjs.map +1 -1
- package/dist/es/ai-model/llm-planning.mjs +26 -23
- package/dist/es/ai-model/llm-planning.mjs.map +1 -1
- package/dist/es/ai-model/prompt/llm-planning.mjs +50 -23
- package/dist/es/ai-model/prompt/llm-planning.mjs.map +1 -1
- package/dist/es/ai-model/prompt/playwright-generator.mjs +9 -3
- package/dist/es/ai-model/prompt/playwright-generator.mjs.map +1 -1
- package/dist/es/ai-model/prompt/util.mjs +2 -2
- package/dist/es/ai-model/prompt/util.mjs.map +1 -1
- package/dist/es/ai-model/prompt/yaml-generator.mjs +9 -3
- package/dist/es/ai-model/prompt/yaml-generator.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/index.mjs +72 -118
- package/dist/es/ai-model/service-caller/index.mjs.map +1 -1
- package/dist/es/ai-model/ui-tars-planning.mjs +5 -5
- package/dist/es/ai-model/ui-tars-planning.mjs.map +1 -1
- package/dist/es/index.mjs +3 -2
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/insight/index.mjs +13 -61
- package/dist/es/insight/index.mjs.map +1 -1
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +5 -6
- package/dist/es/utils.mjs.map +1 -1
- package/dist/lib/ai-model/common.js +80 -20
- package/dist/lib/ai-model/common.js.map +1 -1
- package/dist/lib/ai-model/index.js +14 -5
- package/dist/lib/ai-model/inspect.js +27 -15
- package/dist/lib/ai-model/inspect.js.map +1 -1
- package/dist/lib/ai-model/llm-planning.js +25 -22
- package/dist/lib/ai-model/llm-planning.js.map +1 -1
- package/dist/lib/ai-model/prompt/llm-planning.js +52 -25
- package/dist/lib/ai-model/prompt/llm-planning.js.map +1 -1
- package/dist/lib/ai-model/prompt/playwright-generator.js +9 -3
- package/dist/lib/ai-model/prompt/playwright-generator.js.map +1 -1
- package/dist/lib/ai-model/prompt/util.js +2 -2
- package/dist/lib/ai-model/prompt/util.js.map +1 -1
- package/dist/lib/ai-model/prompt/yaml-generator.js +9 -3
- package/dist/lib/ai-model/prompt/yaml-generator.js.map +1 -1
- package/dist/lib/ai-model/service-caller/index.js +75 -124
- package/dist/lib/ai-model/service-caller/index.js.map +1 -1
- package/dist/lib/ai-model/ui-tars-planning.js +5 -5
- package/dist/lib/ai-model/ui-tars-planning.js.map +1 -1
- package/dist/lib/index.js +20 -4
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/insight/index.js +10 -58
- package/dist/lib/insight/index.js.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/utils.js +4 -5
- package/dist/lib/utils.js.map +1 -1
- package/dist/types/ai-model/common.d.ts +160 -7
- package/dist/types/ai-model/index.d.ts +2 -2
- package/dist/types/ai-model/inspect.d.ts +2 -0
- package/dist/types/ai-model/llm-planning.d.ts +1 -1
- package/dist/types/ai-model/prompt/llm-planning.d.ts +2 -2
- package/dist/types/ai-model/prompt/util.d.ts +2 -1
- package/dist/types/ai-model/service-caller/index.d.ts +6 -6
- package/dist/types/ai-model/ui-tars-planning.d.ts +3 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/insight/index.d.ts +1 -4
- package/dist/types/types.d.ts +8 -11
- package/dist/types/yaml.d.ts +1 -0
- package/package.json +4 -3
|
@@ -99,7 +99,7 @@ Description of all the elements in screenshot:
|
|
|
99
99
|
</div>
|
|
100
100
|
====================
|
|
101
101
|
`;
|
|
102
|
-
async function describeUserPage(context, opt) {
|
|
102
|
+
async function describeUserPage(context, modelPreferences, opt) {
|
|
103
103
|
const { screenshotBase64 } = context;
|
|
104
104
|
let width;
|
|
105
105
|
let height;
|
|
@@ -118,7 +118,7 @@ async function describeUserPage(context, opt) {
|
|
|
118
118
|
});
|
|
119
119
|
let pageDescription = '';
|
|
120
120
|
const visibleOnly = (null == opt ? void 0 : opt.visibleOnly) ?? (null == opt ? void 0 : opt.domIncluded) === 'visible-only';
|
|
121
|
-
if ((null == opt ? void 0 : opt.domIncluded) || !(0, env_namespaceObject.vlLocateMode)()) {
|
|
121
|
+
if ((null == opt ? void 0 : opt.domIncluded) || !(0, env_namespaceObject.vlLocateMode)(modelPreferences)) {
|
|
122
122
|
const contentTree = await (0, extractor_namespaceObject.descriptionOfTree)(treeRoot, null == opt ? void 0 : opt.truncateTextLength, null == opt ? void 0 : opt.filterNonTextContent, visibleOnly);
|
|
123
123
|
const sizeDescription = describeSize({
|
|
124
124
|
width,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/prompt/util.js","sources":["webpack://@midscene/core/webpack/runtime/define_property_getters","webpack://@midscene/core/webpack/runtime/has_own_property","webpack://@midscene/core/webpack/runtime/make_namespace_object","webpack://@midscene/core/./src/ai-model/prompt/util.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { imageInfoOfBase64 } from '@/image/index';\nimport type { BaseElement, ElementTreeNode, Size, UIContext } from '@/types';\nimport { NodeType } from '@midscene/shared/constants';\nimport { vlLocateMode } from '@midscene/shared/env';\nimport {\n descriptionOfTree,\n generateElementByPosition,\n treeToList,\n} from '@midscene/shared/extractor';\nimport { assert } from '@midscene/shared/utils';\n\nexport function describeSize(size: Size) {\n return `${size.width} x ${size.height}`;\n}\n\nexport function describeElement(\n elements: (Pick<BaseElement, 'rect' | 'content'> & { id: string })[],\n) {\n const sliceLength = 80;\n return elements\n .map((item) =>\n [\n item.id,\n item.rect.left,\n item.rect.top,\n item.rect.left + item.rect.width,\n item.rect.top + item.rect.height,\n item.content.length > sliceLength\n ? `${item.content.slice(0, sliceLength)}...`\n : item.content,\n ].join(', '),\n )\n .join('\\n');\n}\nexport const distanceThreshold = 16;\n\nexport function elementByPositionWithElementInfo(\n treeRoot: ElementTreeNode<BaseElement>,\n position: {\n x: number;\n y: number;\n },\n options?: {\n requireStrictDistance?: boolean;\n filterPositionElements?: boolean;\n },\n) {\n const requireStrictDistance = options?.requireStrictDistance ?? true;\n const filterPositionElements = options?.filterPositionElements ?? false;\n\n assert(typeof position !== 'undefined', 'position is required for query');\n\n const matchingElements: BaseElement[] = [];\n\n function dfs(node: ElementTreeNode<BaseElement>) {\n if (node?.node) {\n const item = node.node;\n if (\n item.rect.left <= position.x &&\n position.x <= item.rect.left + item.rect.width &&\n item.rect.top <= position.y &&\n position.y <= item.rect.top + item.rect.height\n ) {\n if (\n !(\n filterPositionElements &&\n item.attributes?.nodeType === NodeType.POSITION\n ) &&\n item.isVisible\n ) {\n matchingElements.push(item);\n }\n }\n }\n\n for (const child of node.children) {\n dfs(child);\n }\n }\n\n dfs(treeRoot);\n\n if (matchingElements.length === 0) {\n return undefined;\n }\n\n // Find the smallest element by area\n const element = matchingElements.reduce((smallest, current) => {\n const smallestArea = smallest.rect.width * smallest.rect.height;\n const currentArea = current.rect.width * current.rect.height;\n return currentArea < smallestArea ? current : smallest;\n });\n\n const distanceToCenter = distance(\n { x: element.center[0], y: element.center[1] },\n position,\n );\n\n if (requireStrictDistance) {\n return distanceToCenter <= distanceThreshold ? element : undefined;\n }\n\n return element;\n}\n\nexport function distance(\n point1: { x: number; y: number },\n point2: { x: number; y: number },\n) {\n return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);\n}\n\nexport const samplePageDescription = `\nAnd the page is described as follows:\n====================\nThe size of the page: 1280 x 720\nSome of the elements are marked with a rectangle in the screenshot corresponding to the markerId, some are not.\n\nDescription of all the elements in screenshot:\n<div id=\"969f1637\" markerId=\"1\" left=\"100\" top=\"100\" width=\"100\" height=\"100\"> // The markerId indicated by the rectangle label in the screenshot\n <h4 id=\"b211ecb2\" markerId=\"5\" left=\"150\" top=\"150\" width=\"90\" height=\"60\">\n The username is accepted\n </h4>\n ...many more\n</div>\n====================\n`;\n\nexport async function describeUserPage<\n ElementType extends BaseElement = BaseElement,\n>(\n context: Omit<UIContext<ElementType>, 'describer'>,\n opt?: {\n truncateTextLength?: number;\n filterNonTextContent?: boolean;\n domIncluded?: boolean | 'visible-only';\n visibleOnly?: boolean;\n },\n) {\n const { screenshotBase64 } = context;\n let width: number;\n let height: number;\n\n if (context.size) {\n ({ width, height } = context.size);\n } else {\n const imgSize = await imageInfoOfBase64(screenshotBase64);\n ({ width, height } = imgSize);\n }\n\n const treeRoot = context.tree;\n // dfs tree, save the id and element info\n const idElementMap: Record<string, ElementType> = {};\n const flatElements: ElementType[] = treeToList(treeRoot);\n\n if (opt?.domIncluded === true && flatElements.length >= 5000) {\n console.warn(\n 'The number of elements is too large, it may cause the prompt to be too long, please use domIncluded: \"visible-only\" to reduce the number of elements',\n );\n }\n\n flatElements.forEach((element) => {\n idElementMap[element.id] = element;\n if (typeof element.indexId !== 'undefined') {\n idElementMap[`${element.indexId}`] = element;\n }\n });\n\n let pageDescription = '';\n const visibleOnly = opt?.visibleOnly ?? opt?.domIncluded === 'visible-only';\n if (opt?.domIncluded || !vlLocateMode()) {\n // non-vl mode must provide the page description\n const contentTree = await descriptionOfTree(\n treeRoot,\n opt?.truncateTextLength,\n opt?.filterNonTextContent,\n visibleOnly,\n );\n\n // if match by position, don't need to provide the page description\n const sizeDescription = describeSize({ width, height });\n pageDescription = `The size of the page: ${sizeDescription} \\n The page elements tree:\\n${contentTree}`;\n }\n\n return {\n description: pageDescription,\n elementById(idOrIndexId: string) {\n assert(typeof idOrIndexId !== 'undefined', 'id is required for query');\n const item = idElementMap[`${idOrIndexId}`];\n return item;\n },\n elementByPosition(\n position: { x: number; y: number },\n size: { width: number; height: number },\n ) {\n return elementByPositionWithElementInfo(treeRoot, position);\n },\n insertElementByPosition(position: { x: number; y: number }) {\n const element = generateElementByPosition(position) as ElementType;\n\n treeRoot.children.push({\n node: element,\n children: [],\n });\n flatElements.push(element);\n idElementMap[element.id] = element;\n return element;\n },\n size: { width, height },\n };\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","describeSize","size","describeElement","elements","sliceLength","item","distanceThreshold","elementByPositionWithElementInfo","treeRoot","position","options","requireStrictDistance","filterPositionElements","assert","matchingElements","dfs","node","_item_attributes","NodeType","child","element","smallest","current","smallestArea","currentArea","distanceToCenter","distance","undefined","point1","point2","Math","samplePageDescription","describeUserPage","context","opt","screenshotBase64","width","height","imgSize","imageInfoOfBase64","idElementMap","flatElements","treeToList","console","pageDescription","visibleOnly","vlLocateMode","contentTree","descriptionOfTree","sizeDescription","idOrIndexId","generateElementByPosition"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-model/prompt/util.js","sources":["webpack://@midscene/core/webpack/runtime/define_property_getters","webpack://@midscene/core/webpack/runtime/has_own_property","webpack://@midscene/core/webpack/runtime/make_namespace_object","webpack://@midscene/core/./src/ai-model/prompt/util.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { imageInfoOfBase64 } from '@/image/index';\nimport type { BaseElement, ElementTreeNode, Size, UIContext } from '@/types';\nimport { NodeType } from '@midscene/shared/constants';\nimport { type IModelPreferences, vlLocateMode } from '@midscene/shared/env';\nimport {\n descriptionOfTree,\n generateElementByPosition,\n treeToList,\n} from '@midscene/shared/extractor';\nimport { assert } from '@midscene/shared/utils';\n\nexport function describeSize(size: Size) {\n return `${size.width} x ${size.height}`;\n}\n\nexport function describeElement(\n elements: (Pick<BaseElement, 'rect' | 'content'> & { id: string })[],\n) {\n const sliceLength = 80;\n return elements\n .map((item) =>\n [\n item.id,\n item.rect.left,\n item.rect.top,\n item.rect.left + item.rect.width,\n item.rect.top + item.rect.height,\n item.content.length > sliceLength\n ? `${item.content.slice(0, sliceLength)}...`\n : item.content,\n ].join(', '),\n )\n .join('\\n');\n}\nexport const distanceThreshold = 16;\n\nexport function elementByPositionWithElementInfo(\n treeRoot: ElementTreeNode<BaseElement>,\n position: {\n x: number;\n y: number;\n },\n options?: {\n requireStrictDistance?: boolean;\n filterPositionElements?: boolean;\n },\n) {\n const requireStrictDistance = options?.requireStrictDistance ?? true;\n const filterPositionElements = options?.filterPositionElements ?? false;\n\n assert(typeof position !== 'undefined', 'position is required for query');\n\n const matchingElements: BaseElement[] = [];\n\n function dfs(node: ElementTreeNode<BaseElement>) {\n if (node?.node) {\n const item = node.node;\n if (\n item.rect.left <= position.x &&\n position.x <= item.rect.left + item.rect.width &&\n item.rect.top <= position.y &&\n position.y <= item.rect.top + item.rect.height\n ) {\n if (\n !(\n filterPositionElements &&\n item.attributes?.nodeType === NodeType.POSITION\n ) &&\n item.isVisible\n ) {\n matchingElements.push(item);\n }\n }\n }\n\n for (const child of node.children) {\n dfs(child);\n }\n }\n\n dfs(treeRoot);\n\n if (matchingElements.length === 0) {\n return undefined;\n }\n\n // Find the smallest element by area\n const element = matchingElements.reduce((smallest, current) => {\n const smallestArea = smallest.rect.width * smallest.rect.height;\n const currentArea = current.rect.width * current.rect.height;\n return currentArea < smallestArea ? current : smallest;\n });\n\n const distanceToCenter = distance(\n { x: element.center[0], y: element.center[1] },\n position,\n );\n\n if (requireStrictDistance) {\n return distanceToCenter <= distanceThreshold ? element : undefined;\n }\n\n return element;\n}\n\nexport function distance(\n point1: { x: number; y: number },\n point2: { x: number; y: number },\n) {\n return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);\n}\n\nexport const samplePageDescription = `\nAnd the page is described as follows:\n====================\nThe size of the page: 1280 x 720\nSome of the elements are marked with a rectangle in the screenshot corresponding to the markerId, some are not.\n\nDescription of all the elements in screenshot:\n<div id=\"969f1637\" markerId=\"1\" left=\"100\" top=\"100\" width=\"100\" height=\"100\"> // The markerId indicated by the rectangle label in the screenshot\n <h4 id=\"b211ecb2\" markerId=\"5\" left=\"150\" top=\"150\" width=\"90\" height=\"60\">\n The username is accepted\n </h4>\n ...many more\n</div>\n====================\n`;\n\nexport async function describeUserPage<\n ElementType extends BaseElement = BaseElement,\n>(\n context: Omit<UIContext<ElementType>, 'describer'>,\n modelPreferences: IModelPreferences,\n opt?: {\n truncateTextLength?: number;\n filterNonTextContent?: boolean;\n domIncluded?: boolean | 'visible-only';\n visibleOnly?: boolean;\n },\n) {\n const { screenshotBase64 } = context;\n let width: number;\n let height: number;\n\n if (context.size) {\n ({ width, height } = context.size);\n } else {\n const imgSize = await imageInfoOfBase64(screenshotBase64);\n ({ width, height } = imgSize);\n }\n\n const treeRoot = context.tree;\n // dfs tree, save the id and element info\n const idElementMap: Record<string, ElementType> = {};\n const flatElements: ElementType[] = treeToList(treeRoot);\n\n if (opt?.domIncluded === true && flatElements.length >= 5000) {\n console.warn(\n 'The number of elements is too large, it may cause the prompt to be too long, please use domIncluded: \"visible-only\" to reduce the number of elements',\n );\n }\n\n flatElements.forEach((element) => {\n idElementMap[element.id] = element;\n if (typeof element.indexId !== 'undefined') {\n idElementMap[`${element.indexId}`] = element;\n }\n });\n\n let pageDescription = '';\n const visibleOnly = opt?.visibleOnly ?? opt?.domIncluded === 'visible-only';\n if (opt?.domIncluded || !vlLocateMode(modelPreferences)) {\n // non-vl mode must provide the page description\n const contentTree = await descriptionOfTree(\n treeRoot,\n opt?.truncateTextLength,\n opt?.filterNonTextContent,\n visibleOnly,\n );\n\n // if match by position, don't need to provide the page description\n const sizeDescription = describeSize({ width, height });\n pageDescription = `The size of the page: ${sizeDescription} \\n The page elements tree:\\n${contentTree}`;\n }\n\n return {\n description: pageDescription,\n elementById(idOrIndexId: string) {\n assert(typeof idOrIndexId !== 'undefined', 'id is required for query');\n const item = idElementMap[`${idOrIndexId}`];\n return item;\n },\n elementByPosition(\n position: { x: number; y: number },\n size: { width: number; height: number },\n ) {\n return elementByPositionWithElementInfo(treeRoot, position);\n },\n insertElementByPosition(position: { x: number; y: number }) {\n const element = generateElementByPosition(position) as ElementType;\n\n treeRoot.children.push({\n node: element,\n children: [],\n });\n flatElements.push(element);\n idElementMap[element.id] = element;\n return element;\n },\n size: { width, height },\n };\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","describeSize","size","describeElement","elements","sliceLength","item","distanceThreshold","elementByPositionWithElementInfo","treeRoot","position","options","requireStrictDistance","filterPositionElements","assert","matchingElements","dfs","node","_item_attributes","NodeType","child","element","smallest","current","smallestArea","currentArea","distanceToCenter","distance","undefined","point1","point2","Math","samplePageDescription","describeUserPage","context","modelPreferences","opt","screenshotBase64","width","height","imgSize","imageInfoOfBase64","idElementMap","flatElements","treeToList","console","pageDescription","visibleOnly","vlLocateMode","contentTree","descriptionOfTree","sizeDescription","idOrIndexId","generateElementByPosition"],"mappings":"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"}
|
|
@@ -178,7 +178,9 @@ Respond with YAML only, no explanations.`
|
|
|
178
178
|
}))
|
|
179
179
|
});
|
|
180
180
|
}
|
|
181
|
-
const response = await (0, external_index_js_namespaceObject.callAi)(prompt, external_index_js_namespaceObject.AIActionType.EXTRACT_DATA
|
|
181
|
+
const response = await (0, external_index_js_namespaceObject.callAi)(prompt, external_index_js_namespaceObject.AIActionType.EXTRACT_DATA, {
|
|
182
|
+
intent: 'default'
|
|
183
|
+
});
|
|
182
184
|
if ((null == response ? void 0 : response.content) && 'string' == typeof response.content) return response.content;
|
|
183
185
|
throw new Error('Failed to generate YAML test configuration');
|
|
184
186
|
} catch (error) {
|
|
@@ -234,12 +236,16 @@ Respond with YAML only, no explanations.`
|
|
|
234
236
|
}))
|
|
235
237
|
});
|
|
236
238
|
}
|
|
237
|
-
if (options.stream && options.onChunk) return await (0, external_index_js_namespaceObject.callAi)(prompt, external_index_js_namespaceObject.AIActionType.EXTRACT_DATA,
|
|
239
|
+
if (options.stream && options.onChunk) return await (0, external_index_js_namespaceObject.callAi)(prompt, external_index_js_namespaceObject.AIActionType.EXTRACT_DATA, {
|
|
240
|
+
intent: 'default'
|
|
241
|
+
}, {
|
|
238
242
|
stream: true,
|
|
239
243
|
onChunk: options.onChunk
|
|
240
244
|
});
|
|
241
245
|
{
|
|
242
|
-
const response = await (0, external_index_js_namespaceObject.callAi)(prompt, external_index_js_namespaceObject.AIActionType.EXTRACT_DATA
|
|
246
|
+
const response = await (0, external_index_js_namespaceObject.callAi)(prompt, external_index_js_namespaceObject.AIActionType.EXTRACT_DATA, {
|
|
247
|
+
intent: 'default'
|
|
248
|
+
});
|
|
243
249
|
if ((null == response ? void 0 : response.content) && 'string' == typeof response.content) return {
|
|
244
250
|
content: response.content,
|
|
245
251
|
usage: response.usage,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/prompt/yaml-generator.js","sources":["webpack://@midscene/core/webpack/runtime/define_property_getters","webpack://@midscene/core/webpack/runtime/has_own_property","webpack://@midscene/core/webpack/runtime/make_namespace_object","webpack://@midscene/core/./src/ai-model/prompt/yaml-generator.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import type {\n StreamingAIResponse,\n StreamingCodeGenerationOptions,\n} from '@/types';\nimport { YAML_EXAMPLE_CODE } from '@midscene/shared/constants';\nimport {\n AIActionType,\n type ChatCompletionMessageParam,\n callAi,\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): 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\nRespond with YAML only, no explanations.`,\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 callAi(prompt, AIActionType.EXTRACT_DATA);\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): 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\nRespond with YAML only, no explanations.`,\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, AIActionType.EXTRACT_DATA, undefined, {\n stream: true,\n onChunk: options.onChunk,\n });\n } else {\n // Fallback to non-streaming\n const response = await callAi(prompt, AIActionType.EXTRACT_DATA);\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":["__webpack_require__","definition","key","Object","obj","prop","Symbol","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","summary","yamlSummary","prompt","YAML_EXAMPLE_CODE","JSON","response","callAi","AIActionType","error","generateYamlTestStream","undefined"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-model/prompt/yaml-generator.js","sources":["webpack://@midscene/core/webpack/runtime/define_property_getters","webpack://@midscene/core/webpack/runtime/has_own_property","webpack://@midscene/core/webpack/runtime/make_namespace_object","webpack://@midscene/core/./src/ai-model/prompt/yaml-generator.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import type {\n StreamingAIResponse,\n StreamingCodeGenerationOptions,\n} from '@/types';\nimport { YAML_EXAMPLE_CODE } from '@midscene/shared/constants';\nimport {\n AIActionType,\n type ChatCompletionMessageParam,\n callAi,\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): 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\nRespond with YAML only, no explanations.`,\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 callAi(prompt, AIActionType.EXTRACT_DATA, {\n intent: 'default',\n });\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): 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\nRespond with YAML only, no explanations.`,\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(\n prompt,\n AIActionType.EXTRACT_DATA,\n {\n intent: 'default',\n },\n {\n stream: true,\n onChunk: options.onChunk,\n },\n );\n } else {\n // Fallback to non-streaming\n const response = await callAi(prompt, AIActionType.EXTRACT_DATA, {\n intent: 'default',\n });\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":["__webpack_require__","definition","key","Object","obj","prop","Symbol","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","summary","yamlSummary","prompt","YAML_EXAMPLE_CODE","JSON","response","callAi","AIActionType","error","generateYamlTestStream"],"mappings":"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"}
|
|
@@ -50,14 +50,13 @@ var __webpack_exports__ = {};
|
|
|
50
50
|
(()=>{
|
|
51
51
|
__webpack_require__.r(__webpack_exports__);
|
|
52
52
|
__webpack_require__.d(__webpack_exports__, {
|
|
53
|
-
preprocessDoubaoBboxJson: ()=>preprocessDoubaoBboxJson,
|
|
54
|
-
checkAIConfig: ()=>checkAIConfig,
|
|
55
|
-
callAiFnWithStringResponse: ()=>callAiFnWithStringResponse,
|
|
56
|
-
safeParseJson: ()=>safeParseJson,
|
|
57
53
|
extractJSONFromCodeBlock: ()=>extractJSONFromCodeBlock,
|
|
54
|
+
preprocessDoubaoBboxJson: ()=>preprocessDoubaoBboxJson,
|
|
58
55
|
callToGetJSONObject: ()=>callToGetJSONObject,
|
|
56
|
+
callAiFnWithStringResponse: ()=>callAiFnWithStringResponse,
|
|
57
|
+
getResponseFormat: ()=>getResponseFormat,
|
|
59
58
|
call: ()=>call,
|
|
60
|
-
|
|
59
|
+
safeParseJson: ()=>safeParseJson
|
|
61
60
|
});
|
|
62
61
|
const external_types_js_namespaceObject = require("../../types.js");
|
|
63
62
|
const sdk_namespaceObject = require("@anthropic-ai/sdk");
|
|
@@ -75,46 +74,9 @@ var __webpack_exports__ = {};
|
|
|
75
74
|
const assertion_js_namespaceObject = require("../prompt/assertion.js");
|
|
76
75
|
const llm_locator_js_namespaceObject = require("../prompt/llm-locator.js");
|
|
77
76
|
const llm_planning_js_namespaceObject = require("../prompt/llm-planning.js");
|
|
78
|
-
function
|
|
79
|
-
const
|
|
80
|
-
const azureConfig = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.MIDSCENE_USE_AZURE_OPENAI);
|
|
81
|
-
const anthropicKey = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.ANTHROPIC_API_KEY);
|
|
82
|
-
const initConfigJson = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.MIDSCENE_OPENAI_INIT_CONFIG_JSON);
|
|
83
|
-
if (openaiKey) return true;
|
|
84
|
-
if (azureConfig) return true;
|
|
85
|
-
if (anthropicKey) return true;
|
|
86
|
-
return Boolean(initConfigJson);
|
|
87
|
-
}
|
|
88
|
-
let debugConfigInitialized = false;
|
|
89
|
-
function initDebugConfig() {
|
|
90
|
-
if (debugConfigInitialized) return;
|
|
91
|
-
const shouldPrintTiming = (0, env_namespaceObject.getAIConfigInBoolean)(env_namespaceObject.MIDSCENE_DEBUG_AI_PROFILE);
|
|
92
|
-
let debugConfig = '';
|
|
93
|
-
if (shouldPrintTiming) {
|
|
94
|
-
console.warn('MIDSCENE_DEBUG_AI_PROFILE is deprecated, use DEBUG=midscene:ai:profile instead');
|
|
95
|
-
debugConfig = 'ai:profile';
|
|
96
|
-
}
|
|
97
|
-
const shouldPrintAIResponse = (0, env_namespaceObject.getAIConfigInBoolean)(env_namespaceObject.MIDSCENE_DEBUG_AI_RESPONSE);
|
|
98
|
-
if (shouldPrintAIResponse) {
|
|
99
|
-
console.warn('MIDSCENE_DEBUG_AI_RESPONSE is deprecated, use DEBUG=midscene:ai:response instead');
|
|
100
|
-
debugConfig = debugConfig ? 'ai:*' : 'ai:call';
|
|
101
|
-
}
|
|
102
|
-
if (debugConfig) (0, logger_namespaceObject.enableDebug)(debugConfig);
|
|
103
|
-
debugConfigInitialized = true;
|
|
104
|
-
}
|
|
105
|
-
const defaultModel = 'gpt-4o';
|
|
106
|
-
function getModelName() {
|
|
107
|
-
let modelName = defaultModel;
|
|
108
|
-
const nameInConfig = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.MIDSCENE_MODEL_NAME);
|
|
109
|
-
if (nameInConfig) modelName = nameInConfig;
|
|
110
|
-
return modelName;
|
|
111
|
-
}
|
|
112
|
-
async function createChatClient({ AIActionTypeValue }) {
|
|
113
|
-
initDebugConfig();
|
|
77
|
+
async function createChatClient({ AIActionTypeValue, modelPreferences }) {
|
|
78
|
+
const { socksProxy, httpProxy, modelName, openaiBaseURL, openaiApiKey, openaiExtraConfig, openaiUseAzureDeprecated, useAzureOpenai, azureOpenaiScope, azureOpenaiKey, azureOpenaiEndpoint, azureOpenaiApiVersion, azureOpenaiDeployment, azureExtraConfig, useAnthropicSdk, anthropicApiKey } = (0, env_namespaceObject.decideModelConfig)(modelPreferences, true);
|
|
114
79
|
let openai;
|
|
115
|
-
const extraConfig = (0, env_namespaceObject.getAIConfigInJson)(env_namespaceObject.MIDSCENE_OPENAI_INIT_CONFIG_JSON);
|
|
116
|
-
const socksProxy = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.MIDSCENE_OPENAI_SOCKS_PROXY);
|
|
117
|
-
const httpProxy = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.MIDSCENE_OPENAI_HTTP_PROXY);
|
|
118
80
|
let proxyAgent;
|
|
119
81
|
const debugProxy = (0, logger_namespaceObject.getDebug)('ai:call:proxy');
|
|
120
82
|
if (httpProxy) {
|
|
@@ -124,56 +86,47 @@ var __webpack_exports__ = {};
|
|
|
124
86
|
debugProxy('using socks proxy', socksProxy);
|
|
125
87
|
proxyAgent = new external_socks_proxy_agent_namespaceObject.SocksProxyAgent(socksProxy);
|
|
126
88
|
}
|
|
127
|
-
if (
|
|
128
|
-
baseURL:
|
|
129
|
-
apiKey:
|
|
89
|
+
if (openaiUseAzureDeprecated) openai = new external_openai_namespaceObject.AzureOpenAI({
|
|
90
|
+
baseURL: openaiBaseURL,
|
|
91
|
+
apiKey: openaiApiKey,
|
|
130
92
|
httpAgent: proxyAgent,
|
|
131
|
-
...
|
|
93
|
+
...openaiExtraConfig,
|
|
132
94
|
dangerouslyAllowBrowser: true
|
|
133
95
|
});
|
|
134
|
-
else if (
|
|
135
|
-
const extraAzureConfig = (0, env_namespaceObject.getAIConfigInJson)(env_namespaceObject.MIDSCENE_AZURE_OPENAI_INIT_CONFIG_JSON);
|
|
136
|
-
const scope = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.MIDSCENE_AZURE_OPENAI_SCOPE);
|
|
96
|
+
else if (useAzureOpenai) {
|
|
137
97
|
let tokenProvider;
|
|
138
|
-
if (
|
|
98
|
+
if (azureOpenaiScope) {
|
|
139
99
|
(0, utils_namespaceObject.assert)(!utils_namespaceObject.ifInBrowser, 'Azure OpenAI is not supported in browser with Midscene.');
|
|
140
100
|
const credential = new identity_namespaceObject.DefaultAzureCredential();
|
|
141
|
-
(0,
|
|
142
|
-
tokenProvider = (0, identity_namespaceObject.getBearerTokenProvider)(credential, scope);
|
|
101
|
+
tokenProvider = (0, identity_namespaceObject.getBearerTokenProvider)(credential, azureOpenaiScope);
|
|
143
102
|
openai = new external_openai_namespaceObject.AzureOpenAI({
|
|
144
103
|
azureADTokenProvider: tokenProvider,
|
|
145
|
-
endpoint:
|
|
146
|
-
apiVersion:
|
|
147
|
-
deployment:
|
|
148
|
-
...
|
|
149
|
-
...
|
|
104
|
+
endpoint: azureOpenaiEndpoint,
|
|
105
|
+
apiVersion: azureOpenaiApiVersion,
|
|
106
|
+
deployment: azureOpenaiDeployment,
|
|
107
|
+
...openaiExtraConfig,
|
|
108
|
+
...azureExtraConfig
|
|
150
109
|
});
|
|
151
110
|
} else openai = new external_openai_namespaceObject.AzureOpenAI({
|
|
152
|
-
apiKey:
|
|
153
|
-
endpoint:
|
|
154
|
-
apiVersion:
|
|
155
|
-
deployment:
|
|
111
|
+
apiKey: azureOpenaiKey,
|
|
112
|
+
endpoint: azureOpenaiEndpoint,
|
|
113
|
+
apiVersion: azureOpenaiApiVersion,
|
|
114
|
+
deployment: azureOpenaiDeployment,
|
|
156
115
|
dangerouslyAllowBrowser: true,
|
|
157
|
-
...
|
|
158
|
-
...
|
|
159
|
-
});
|
|
160
|
-
} else if (!(0, env_namespaceObject.getAIConfig)(env_namespaceObject.MIDSCENE_USE_ANTHROPIC_SDK)) {
|
|
161
|
-
const baseURL = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.OPENAI_BASE_URL);
|
|
162
|
-
if ('string' == typeof baseURL) {
|
|
163
|
-
if (!/^https?:\/\//.test(baseURL)) throw new Error(`OPENAI_BASE_URL must be a valid URL starting with http:// or https://, but got: ${baseURL}\nPlease check your config.`);
|
|
164
|
-
}
|
|
165
|
-
openai = new (external_openai_default())({
|
|
166
|
-
baseURL: (0, env_namespaceObject.getAIConfig)(env_namespaceObject.OPENAI_BASE_URL),
|
|
167
|
-
apiKey: (0, env_namespaceObject.getAIConfig)(env_namespaceObject.OPENAI_API_KEY),
|
|
168
|
-
httpAgent: proxyAgent,
|
|
169
|
-
...extraConfig,
|
|
170
|
-
defaultHeaders: {
|
|
171
|
-
...(null == extraConfig ? void 0 : extraConfig.defaultHeaders) || {},
|
|
172
|
-
[env_namespaceObject.MIDSCENE_API_TYPE]: AIActionTypeValue.toString()
|
|
173
|
-
},
|
|
174
|
-
dangerouslyAllowBrowser: true
|
|
116
|
+
...openaiExtraConfig,
|
|
117
|
+
...azureExtraConfig
|
|
175
118
|
});
|
|
176
|
-
}
|
|
119
|
+
} else if (!useAnthropicSdk) openai = new (external_openai_default())({
|
|
120
|
+
baseURL: openaiBaseURL,
|
|
121
|
+
apiKey: openaiApiKey,
|
|
122
|
+
httpAgent: proxyAgent,
|
|
123
|
+
...openaiExtraConfig,
|
|
124
|
+
defaultHeaders: {
|
|
125
|
+
...(null == openaiExtraConfig ? void 0 : openaiExtraConfig.defaultHeaders) || {},
|
|
126
|
+
[env_namespaceObject.MIDSCENE_API_TYPE]: AIActionTypeValue.toString()
|
|
127
|
+
},
|
|
128
|
+
dangerouslyAllowBrowser: true
|
|
129
|
+
});
|
|
177
130
|
if (openai && (0, env_namespaceObject.getAIConfigInBoolean)(env_namespaceObject.MIDSCENE_LANGSMITH_DEBUG)) {
|
|
178
131
|
if (utils_namespaceObject.ifInBrowser) throw new Error('langsmith is not supported in browser');
|
|
179
132
|
console.log('DEBUGGING MODE: langsmith wrapper enabled');
|
|
@@ -182,53 +135,51 @@ var __webpack_exports__ = {};
|
|
|
182
135
|
}
|
|
183
136
|
if (void 0 !== openai) return {
|
|
184
137
|
completion: openai.chat.completions,
|
|
185
|
-
style: 'openai'
|
|
138
|
+
style: 'openai',
|
|
139
|
+
modelName
|
|
186
140
|
};
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
httpAgent: proxyAgent,
|
|
193
|
-
dangerouslyAllowBrowser: true
|
|
194
|
-
});
|
|
195
|
-
}
|
|
141
|
+
if (useAnthropicSdk) openai = new sdk_namespaceObject.Anthropic({
|
|
142
|
+
apiKey: anthropicApiKey,
|
|
143
|
+
httpAgent: proxyAgent,
|
|
144
|
+
dangerouslyAllowBrowser: true
|
|
145
|
+
});
|
|
196
146
|
if (void 0 !== openai && openai.messages) return {
|
|
197
147
|
completion: openai.messages,
|
|
198
|
-
style: 'anthropic'
|
|
148
|
+
style: 'anthropic',
|
|
149
|
+
modelName
|
|
199
150
|
};
|
|
200
151
|
throw new Error('Openai SDK or Anthropic SDK is not initialized');
|
|
201
152
|
}
|
|
202
|
-
async function call(messages, AIActionTypeValue,
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
153
|
+
async function call(messages, AIActionTypeValue, modelPreferences, options) {
|
|
154
|
+
const { completion, style, modelName } = await createChatClient({
|
|
155
|
+
AIActionTypeValue,
|
|
156
|
+
modelPreferences
|
|
206
157
|
});
|
|
158
|
+
const responseFormat = getResponseFormat(modelName, AIActionTypeValue);
|
|
207
159
|
const maxTokens = (0, env_namespaceObject.getAIConfig)(env_namespaceObject.OPENAI_MAX_TOKENS);
|
|
208
160
|
const debugCall = (0, logger_namespaceObject.getDebug)('ai:call');
|
|
209
161
|
const debugProfileStats = (0, logger_namespaceObject.getDebug)('ai:profile:stats');
|
|
210
162
|
const debugProfileDetail = (0, logger_namespaceObject.getDebug)('ai:profile:detail');
|
|
211
163
|
const startTime = Date.now();
|
|
212
|
-
const model = getModelName();
|
|
213
164
|
const isStreaming = (null == options ? void 0 : options.stream) && (null == options ? void 0 : options.onChunk);
|
|
214
165
|
let content;
|
|
215
166
|
let accumulated = '';
|
|
216
167
|
let usage;
|
|
217
168
|
let timeCost;
|
|
218
169
|
const commonConfig = {
|
|
219
|
-
temperature: 'vlm-ui-tars' === (0, env_namespaceObject.vlLocateMode)() ? 0.0 : 0.1,
|
|
170
|
+
temperature: 'vlm-ui-tars' === (0, env_namespaceObject.vlLocateMode)(modelPreferences) ? 0.0 : 0.1,
|
|
220
171
|
stream: !!isStreaming,
|
|
221
172
|
max_tokens: 'number' == typeof maxTokens ? maxTokens : Number.parseInt(maxTokens || '2048', 10),
|
|
222
|
-
...'qwen-vl' === (0, env_namespaceObject.vlLocateMode)() ? {
|
|
173
|
+
...'qwen-vl' === (0, env_namespaceObject.vlLocateMode)(modelPreferences) ? {
|
|
223
174
|
vl_high_resolution_images: true
|
|
224
175
|
} : {}
|
|
225
176
|
};
|
|
226
177
|
try {
|
|
227
178
|
if ('openai' === style) {
|
|
228
|
-
debugCall(`sending ${isStreaming ? 'streaming ' : ''}request to ${
|
|
179
|
+
debugCall(`sending ${isStreaming ? 'streaming ' : ''}request to ${modelName}`);
|
|
229
180
|
if (isStreaming) {
|
|
230
181
|
const stream = await completion.create({
|
|
231
|
-
model,
|
|
182
|
+
model: modelName,
|
|
232
183
|
messages,
|
|
233
184
|
response_format: responseFormat,
|
|
234
185
|
...commonConfig
|
|
@@ -271,7 +222,7 @@ var __webpack_exports__ = {};
|
|
|
271
222
|
completion_tokens: usage.completion_tokens ?? 0,
|
|
272
223
|
total_tokens: usage.total_tokens ?? 0,
|
|
273
224
|
time_cost: timeCost ?? 0,
|
|
274
|
-
model_name:
|
|
225
|
+
model_name: modelName
|
|
275
226
|
}
|
|
276
227
|
};
|
|
277
228
|
options.onChunk(finalChunk);
|
|
@@ -279,17 +230,17 @@ var __webpack_exports__ = {};
|
|
|
279
230
|
}
|
|
280
231
|
}
|
|
281
232
|
content = accumulated;
|
|
282
|
-
debugProfileStats(`streaming model, ${
|
|
233
|
+
debugProfileStats(`streaming model, ${modelName}, mode, ${(0, env_namespaceObject.vlLocateMode)(modelPreferences) || 'default'}, cost-ms, ${timeCost}`);
|
|
283
234
|
} else {
|
|
284
235
|
var _result_usage, _result_usage1, _result_usage2;
|
|
285
236
|
const result = await completion.create({
|
|
286
|
-
model,
|
|
237
|
+
model: modelName,
|
|
287
238
|
messages,
|
|
288
239
|
response_format: responseFormat,
|
|
289
240
|
...commonConfig
|
|
290
241
|
});
|
|
291
242
|
timeCost = Date.now() - startTime;
|
|
292
|
-
debugProfileStats(`model, ${
|
|
243
|
+
debugProfileStats(`model, ${modelName}, mode, ${(0, env_namespaceObject.vlLocateMode)(modelPreferences) || 'default'}, ui-tars-version, ${(0, env_namespaceObject.uiTarsModelVersion)(modelPreferences)}, prompt-tokens, ${(null == (_result_usage = result.usage) ? void 0 : _result_usage.prompt_tokens) || ''}, completion-tokens, ${(null == (_result_usage1 = result.usage) ? void 0 : _result_usage1.completion_tokens) || ''}, total-tokens, ${(null == (_result_usage2 = result.usage) ? void 0 : _result_usage2.total_tokens) || ''}, cost-ms, ${timeCost}, requestId, ${result._request_id || ''}`);
|
|
293
244
|
debugProfileDetail(`model usage detail: ${JSON.stringify(result.usage)}`);
|
|
294
245
|
(0, utils_namespaceObject.assert)(result.choices, `invalid response from LLM service: ${JSON.stringify(result)}`);
|
|
295
246
|
content = result.choices[0].message.content;
|
|
@@ -316,7 +267,7 @@ var __webpack_exports__ = {};
|
|
|
316
267
|
};
|
|
317
268
|
if (isStreaming) {
|
|
318
269
|
const stream = await completion.create({
|
|
319
|
-
model,
|
|
270
|
+
model: modelName,
|
|
320
271
|
system: 'You are a versatile professional in software UI automation',
|
|
321
272
|
messages: messages.map((m)=>({
|
|
322
273
|
role: 'user',
|
|
@@ -352,7 +303,7 @@ var __webpack_exports__ = {};
|
|
|
352
303
|
completion_tokens: anthropicUsage.output_tokens ?? 0,
|
|
353
304
|
total_tokens: (anthropicUsage.input_tokens ?? 0) + (anthropicUsage.output_tokens ?? 0),
|
|
354
305
|
time_cost: timeCost ?? 0,
|
|
355
|
-
model_name:
|
|
306
|
+
model_name: modelName
|
|
356
307
|
} : void 0
|
|
357
308
|
};
|
|
358
309
|
options.onChunk(finalChunk);
|
|
@@ -362,7 +313,7 @@ var __webpack_exports__ = {};
|
|
|
362
313
|
content = accumulated;
|
|
363
314
|
} else {
|
|
364
315
|
const result = await completion.create({
|
|
365
|
-
model,
|
|
316
|
+
model: modelName,
|
|
366
317
|
system: 'You are a versatile professional in software UI automation',
|
|
367
318
|
messages: messages.map((m)=>({
|
|
368
319
|
role: 'user',
|
|
@@ -392,7 +343,7 @@ var __webpack_exports__ = {};
|
|
|
392
343
|
completion_tokens: usage.completion_tokens ?? 0,
|
|
393
344
|
total_tokens: usage.total_tokens ?? 0,
|
|
394
345
|
time_cost: timeCost ?? 0,
|
|
395
|
-
model_name:
|
|
346
|
+
model_name: modelName
|
|
396
347
|
} : void 0,
|
|
397
348
|
isStreamed: !!isStreaming
|
|
398
349
|
};
|
|
@@ -404,10 +355,9 @@ var __webpack_exports__ = {};
|
|
|
404
355
|
throw newError;
|
|
405
356
|
}
|
|
406
357
|
}
|
|
407
|
-
|
|
358
|
+
const getResponseFormat = (modelName, AIActionTypeValue)=>{
|
|
408
359
|
let responseFormat;
|
|
409
|
-
|
|
410
|
-
if (model.includes('gpt-4')) switch(AIActionTypeValue){
|
|
360
|
+
if (modelName.includes('gpt-4')) switch(AIActionTypeValue){
|
|
411
361
|
case external_common_js_namespaceObject.AIActionType.ASSERT:
|
|
412
362
|
responseFormat = assertion_js_namespaceObject.assertSchema;
|
|
413
363
|
break;
|
|
@@ -424,19 +374,22 @@ var __webpack_exports__ = {};
|
|
|
424
374
|
};
|
|
425
375
|
break;
|
|
426
376
|
}
|
|
427
|
-
if ('gpt-4o-2024-05-13' ===
|
|
377
|
+
if ('gpt-4o-2024-05-13' === modelName) responseFormat = {
|
|
428
378
|
type: external_types_js_namespaceObject.AIResponseFormat.JSON
|
|
429
379
|
};
|
|
430
|
-
|
|
380
|
+
return responseFormat;
|
|
381
|
+
};
|
|
382
|
+
async function callToGetJSONObject(messages, AIActionTypeValue, modelPreferences) {
|
|
383
|
+
const response = await call(messages, AIActionTypeValue, modelPreferences);
|
|
431
384
|
(0, utils_namespaceObject.assert)(response, 'empty response');
|
|
432
|
-
const jsonContent = safeParseJson(response.content);
|
|
385
|
+
const jsonContent = safeParseJson(response.content, modelPreferences);
|
|
433
386
|
return {
|
|
434
387
|
content: jsonContent,
|
|
435
388
|
usage: response.usage
|
|
436
389
|
};
|
|
437
390
|
}
|
|
438
|
-
async function callAiFnWithStringResponse(msgs, AIActionTypeValue) {
|
|
439
|
-
const { content, usage } = await call(msgs, AIActionTypeValue);
|
|
391
|
+
async function callAiFnWithStringResponse(msgs, AIActionTypeValue, modelPreferences) {
|
|
392
|
+
const { content, usage } = await call(msgs, AIActionTypeValue, modelPreferences);
|
|
440
393
|
return {
|
|
441
394
|
content,
|
|
442
395
|
usage
|
|
@@ -457,7 +410,7 @@ var __webpack_exports__ = {};
|
|
|
457
410
|
if (input.includes('bbox')) while(/\d+\s+\d+/.test(input))input = input.replace(/(\d+)\s+(\d+)/g, '$1,$2');
|
|
458
411
|
return input;
|
|
459
412
|
}
|
|
460
|
-
function safeParseJson(input) {
|
|
413
|
+
function safeParseJson(input, modelPreferences) {
|
|
461
414
|
const cleanJsonString = extractJSONFromCodeBlock(input);
|
|
462
415
|
if (null == cleanJsonString ? void 0 : cleanJsonString.match(/\((\d+),(\d+)\)/)) {
|
|
463
416
|
var _cleanJsonString_match;
|
|
@@ -469,7 +422,7 @@ var __webpack_exports__ = {};
|
|
|
469
422
|
try {
|
|
470
423
|
return JSON.parse((0, external_jsonrepair_namespaceObject.jsonrepair)(cleanJsonString));
|
|
471
424
|
} catch (e) {}
|
|
472
|
-
if ('doubao-vision' === (0, env_namespaceObject.vlLocateMode)() || 'vlm-ui-tars' === (0, env_namespaceObject.vlLocateMode)()) {
|
|
425
|
+
if ('doubao-vision' === (0, env_namespaceObject.vlLocateMode)(modelPreferences) || 'vlm-ui-tars' === (0, env_namespaceObject.vlLocateMode)(modelPreferences)) {
|
|
473
426
|
const jsonString = preprocessDoubaoBboxJson(cleanJsonString);
|
|
474
427
|
return JSON.parse((0, external_jsonrepair_namespaceObject.jsonrepair)(jsonString));
|
|
475
428
|
}
|
|
@@ -479,18 +432,16 @@ var __webpack_exports__ = {};
|
|
|
479
432
|
exports.call = __webpack_exports__.call;
|
|
480
433
|
exports.callAiFnWithStringResponse = __webpack_exports__.callAiFnWithStringResponse;
|
|
481
434
|
exports.callToGetJSONObject = __webpack_exports__.callToGetJSONObject;
|
|
482
|
-
exports.checkAIConfig = __webpack_exports__.checkAIConfig;
|
|
483
435
|
exports.extractJSONFromCodeBlock = __webpack_exports__.extractJSONFromCodeBlock;
|
|
484
|
-
exports.
|
|
436
|
+
exports.getResponseFormat = __webpack_exports__.getResponseFormat;
|
|
485
437
|
exports.preprocessDoubaoBboxJson = __webpack_exports__.preprocessDoubaoBboxJson;
|
|
486
438
|
exports.safeParseJson = __webpack_exports__.safeParseJson;
|
|
487
439
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
488
440
|
"call",
|
|
489
441
|
"callAiFnWithStringResponse",
|
|
490
442
|
"callToGetJSONObject",
|
|
491
|
-
"checkAIConfig",
|
|
492
443
|
"extractJSONFromCodeBlock",
|
|
493
|
-
"
|
|
444
|
+
"getResponseFormat",
|
|
494
445
|
"preprocessDoubaoBboxJson",
|
|
495
446
|
"safeParseJson"
|
|
496
447
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|