"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/ai-model/service-caller/index.ts
var _sdk = require('@anthropic-ai/sdk');



var _identity = require('@azure/identity');



























var _env = require('@midscene_spp/shared/env');
var _logger = require('@midscene_spp/shared/logger');
var _utils = require('@midscene_spp/shared/utils');

var _dirtyjson = require('dirty-json'); var _dirtyjson2 = _interopRequireDefault(_dirtyjson);
var _httpsproxyagent = require('https-proxy-agent');
var _openai = require('openai'); var _openai2 = _interopRequireDefault(_openai);
var _socksproxyagent = require('socks-proxy-agent');

// src/ai-model/common.ts

var _constants = require('@midscene_spp/shared/constants');

var _extractor = require('@midscene_spp/shared/extractor');
var _img = require('@midscene_spp/shared/img');

async function callAiFn(msgs, AIActionTypeValue) {
  _utils.assert.call(void 0, 
    checkAIConfig(),
    "Cannot find config for AI model service. If you are using a self-hosted model without validating the API key, please set `OPENAI_API_KEY` to any non-null value. https://midscenejs.com/model-provider.html"
  );
  const { content, usage } = await callToGetJSONObject(
    msgs,
    AIActionTypeValue
  );
  return { content, usage };
}
var defaultBboxSize = 20;
var debugInspectUtils = _logger.getDebug.call(void 0, "ai:common");
function fillBboxParam(locate, width, height) {
  if (locate.bbox_2d && !_optionalChain([locate, 'optionalAccess', _ => _.bbox])) {
    locate.bbox = locate.bbox_2d;
    delete locate.bbox_2d;
  }
  if (_optionalChain([locate, 'optionalAccess', _2 => _2.bbox])) {
    locate.bbox = adaptBbox(locate.bbox, width, height);
  }
  return locate;
}
function adaptQwenBbox(bbox) {
  if (bbox.length < 2) {
    const msg = `invalid bbox data for qwen-vl mode: ${JSON.stringify(bbox)} `;
    throw new Error(msg);
  }
  const result = [
    Math.round(bbox[0]),
    Math.round(bbox[1]),
    typeof bbox[2] === "number" ? Math.round(bbox[2]) : Math.round(bbox[0] + defaultBboxSize),
    typeof bbox[3] === "number" ? Math.round(bbox[3]) : Math.round(bbox[1] + defaultBboxSize)
  ];
  return result;
}
function adaptDoubaoBbox(bbox, width, height) {
  _utils.assert.call(void 0, 
    width > 0 && height > 0,
    "width and height must be greater than 0 in doubao mode"
  );
  if (typeof bbox === "string") {
    _utils.assert.call(void 0, 
      /^(\d+)\s(\d+)\s(\d+)\s(\d+)$/.test(bbox.trim()),
      `invalid bbox data string for doubao-vision mode: ${bbox}`
    );
    const splitted = bbox.split(" ");
    if (splitted.length === 4) {
      return [
        Math.round(Number(splitted[0]) * width / 1e3),
        Math.round(Number(splitted[1]) * height / 1e3),
        Math.round(Number(splitted[2]) * width / 1e3),
        Math.round(Number(splitted[3]) * height / 1e3)
      ];
    }
    throw new Error(`invalid bbox data string for doubao-vision mode: ${bbox}`);
  }
  if (Array.isArray(bbox) && Array.isArray(bbox[0])) {
    bbox = bbox[0];
  }
  let bboxList = [];
  if (Array.isArray(bbox) && typeof bbox[0] === "string") {
    bbox.forEach((item) => {
      if (typeof item === "string" && item.includes(",")) {
        const [x, y] = item.split(",");
        bboxList.push(Number(x.trim()), Number(y.trim()));
      } else if (typeof item === "string" && item.includes(" ")) {
        const [x, y] = item.split(" ");
        bboxList.push(Number(x.trim()), Number(y.trim()));
      } else {
        bboxList.push(Number(item));
      }
    });
  } else {
    bboxList = bbox;
  }
  if (bboxList.length === 4 || bboxList.length === 5) {
    return [
      Math.round(bboxList[0] * width / 1e3),
      Math.round(bboxList[1] * height / 1e3),
      Math.round(bboxList[2] * width / 1e3),
      Math.round(bboxList[3] * height / 1e3)
    ];
  }
  if (bboxList.length === 6 || bboxList.length === 2 || bboxList.length === 3 || bboxList.length === 7) {
    return [
      Math.max(
        0,
        Math.round(bboxList[0] * width / 1e3) - defaultBboxSize / 2
      ),
      Math.max(
        0,
        Math.round(bboxList[1] * height / 1e3) - defaultBboxSize / 2
      ),
      Math.min(
        width,
        Math.round(bboxList[0] * width / 1e3) + defaultBboxSize / 2
      ),
      Math.min(
        height,
        Math.round(bboxList[1] * height / 1e3) + defaultBboxSize / 2
      )
    ];
  }
  if (bbox.length === 8) {
    return [
      Math.round(bboxList[0] * width / 1e3),
      Math.round(bboxList[1] * height / 1e3),
      Math.round(bboxList[4] * width / 1e3),
      Math.round(bboxList[5] * height / 1e3)
    ];
  }
  const msg = `invalid bbox data for doubao-vision mode: ${JSON.stringify(bbox)} `;
  throw new Error(msg);
}
function adaptBbox(bbox, width, height) {
  if (_env.vlLocateMode.call(void 0, ) === "doubao-vision" || _env.vlLocateMode.call(void 0, ) === "vlm-ui-tars") {
    return adaptDoubaoBbox(bbox, width, height);
  }
  if (_env.vlLocateMode.call(void 0, ) === "gemini") {
    return adaptGeminiBbox(bbox, width, height);
  }
  return adaptQwenBbox(bbox);
}
function adaptGeminiBbox(bbox, width, height) {
  const left = Math.round(bbox[1] * width / 1e3);
  const top = Math.round(bbox[0] * height / 1e3);
  const right = Math.round(bbox[3] * width / 1e3);
  const bottom = Math.round(bbox[2] * height / 1e3);
  return [left, top, right, bottom];
}
function adaptBboxToRect(bbox, width, height, offsetX = 0, offsetY = 0) {
  debugInspectUtils("adaptBboxToRect", bbox, width, height, offsetX, offsetY);
  const [left, top, right, bottom] = adaptBbox(bbox, width, height);
  const rect = {
    left: left + offsetX,
    top: top + offsetY,
    width: right - left,
    height: bottom - top
  };
  debugInspectUtils("adaptBboxToRect, result=", rect);
  return rect;
}
var warned = false;
function warnGPT4oSizeLimit(size) {
  if (warned)
    return;
  if (_optionalChain([getModelName, 'call', _3 => _3(), 'optionalAccess', _4 => _4.toLowerCase, 'call', _5 => _5(), 'access', _6 => _6.includes, 'call', _7 => _7("gpt-4o")])) {
    const warningMsg = `GPT-4o has a maximum image input size of 2000x768 or 768x2000, but got ${size.width}x${size.height}. Please set your page to a smaller resolution. Otherwise, the result may be inaccurate.`;
    if (Math.max(size.width, size.height) > 2e3 || Math.min(size.width, size.height) > 768) {
      console.warn(warningMsg);
      warned = true;
    }
  } else if (size.width > 1800 || size.height > 1800) {
    console.warn(
      `The image size seems too large (${size.width}x${size.height}). It may lead to more token usage, slower response, and inaccurate result.`
    );
    warned = true;
  }
}
function mergeRects(rects) {
  const minLeft = Math.min(...rects.map((r) => r.left));
  const minTop = Math.min(...rects.map((r) => r.top));
  const maxRight = Math.max(...rects.map((r) => r.left + r.width));
  const maxBottom = Math.max(...rects.map((r) => r.top + r.height));
  return {
    left: minLeft,
    top: minTop,
    width: maxRight - minLeft,
    height: maxBottom - minTop
  };
}
function expandSearchArea(rect, screenSize) {
  const minEdgeSize = 300;
  const defaultPadding = 160;
  const paddingSizeHorizontal = rect.width < minEdgeSize ? Math.ceil((minEdgeSize - rect.width) / 2) : defaultPadding;
  const paddingSizeVertical = rect.height < minEdgeSize ? Math.ceil((minEdgeSize - rect.height) / 2) : defaultPadding;
  rect.left = Math.max(0, rect.left - paddingSizeHorizontal);
  rect.width = Math.min(
    rect.width + paddingSizeHorizontal * 2,
    screenSize.width - rect.left
  );
  rect.top = Math.max(0, rect.top - paddingSizeVertical);
  rect.height = Math.min(
    rect.height + paddingSizeVertical * 2,
    screenSize.height - rect.top
  );
  return rect;
}
async function markupImageForLLM(screenshotBase64, tree, size) {
  const elementsInfo = _extractor.treeToList.call(void 0, tree);
  const elementsPositionInfoWithoutText = elementsInfo.filter(
    (elementInfo) => {
      if (elementInfo.attributes.nodeType === _constants.NodeType.TEXT) {
        return false;
      }
      return true;
    }
  );
  const imagePayload = await _img.compositeElementInfoImg.call(void 0, {
    inputImgBase64: screenshotBase64,
    elementsPositionInfo: elementsPositionInfoWithoutText,
    size
  });
  return imagePayload;
}
function buildYamlFlowFromPlans(plans, sleep) {
  const flow = [];
  for (const plan2 of plans) {
    const type = plan2.type;
    const locate = _optionalChain([plan2, 'access', _8 => _8.locate, 'optionalAccess', _9 => _9.prompt]);
    if (type === "Tap") {
      flow.push({
        aiTap: locate
      });
    } else if (type === "Hover") {
      flow.push({
        aiHover: locate
      });
    } else if (type === "Input") {
      const param = plan2.param;
      flow.push({
        aiInput: param.value,
        locate
      });
    } else if (type === "KeyboardPress") {
      const param = plan2.param;
      flow.push({
        aiKeyboardPress: param.value,
        locate
      });
    } else if (type === "Scroll") {
      const param = plan2.param;
      flow.push({
        aiScroll: null,
        locate,
        direction: param.direction,
        scrollType: param.scrollType,
        distance: param.distance
      });
    } else if (type === "Sleep") {
      const param = plan2.param;
      flow.push({
        sleep: param.timeMs
      });
    } else if (type === "AndroidBackButton" || type === "AndroidHomeButton" || type === "AndroidRecentAppsButton") {
    } else if (type === "Error" || type === "ExpectedFalsyCondition" || type === "Assert" || type === "AssertWithoutThrow" || type === "Finished") {
    } else {
      console.warn(
        `Cannot convert action ${type} to yaml flow. This should be a bug of Midscene.`
      );
    }
  }
  if (sleep) {
    flow.push({
      sleep
    });
  }
  return flow;
}

// src/ai-model/prompt/assertion.ts

var defaultAssertionPrompt = "You are a senior testing engineer. User will give an assertion and a screenshot of a page. By carefully viewing the screenshot, please tell whether the assertion is truthy.";
var defaultAssertionResponseJsonFormat = `Return in the following JSON format:
{
  pass: boolean, // whether the assertion is truthy
  thought: string | null, // string, if the result is falsy, give the reason why it is falsy. Otherwise, put null.
}`;
var getUiTarsAssertionResponseJsonFormat = () => `## Output Json String Format
\`\`\`
"{
  "pass": <<is a boolean value from the enum [true, false], true means the assertion is truthy>>, 
  "thought": "<<is a string, give the reason why the assertion is falsy or truthy. Otherwise.>>"
}"
\`\`\`

## Rules **MUST** follow
- Make sure to return **only** the JSON, with **no additional** text or explanations.
- Use ${_env.getPreferredLanguage.call(void 0, )} in \`thought\` part.
- You **MUST** strictly follow up the **Output Json String Format**.`;
function systemPromptToAssert(model) {
  return `${defaultAssertionPrompt}

${model.isUITars ? getUiTarsAssertionResponseJsonFormat() : defaultAssertionResponseJsonFormat}`;
}
var assertSchema = {
  type: "json_schema",
  json_schema: {
    name: "assert",
    strict: true,
    schema: {
      type: "object",
      properties: {
        pass: {
          type: "boolean",
          description: "Whether the assertion passed or failed"
        },
        thought: {
          type: ["string", "null"],
          description: "The thought process behind the assertion"
        }
      },
      required: ["pass", "thought"],
      additionalProperties: false
    }
  }
};

// src/ai-model/prompt/llm-locator.ts
var _prompts = require('@langchain/core/prompts');

// src/ai-model/prompt/common.ts
function bboxDescription(vlMode) {
  if (vlMode === "gemini") {
    return "2d bounding box as [ymin, xmin, ymax, xmax]";
  }
  return "2d bounding box as [xmin, ymin, xmax, ymax]";
}

// src/ai-model/prompt/llm-locator.ts
function systemPromptToLocateElement(vlMode) {
  if (vlMode) {
    const bboxComment = bboxDescription(vlMode);
    return `
## Role:
You are an expert in software testing.

## Objective:
- Identify elements in screenshots and text that match the user's description.
- Give the coordinates of the element that matches the user's description best in the screenshot.

## Output Format:
\`\`\`json
{
  "bbox": [number, number, number, number],  // ${bboxComment}
  "errors"?: string[]
}
\`\`\`

Fields:
* \`bbox\` is the bounding box of the element that matches the user's description best in the screenshot
* \`errors\` is an optional array of error messages (if any)

For example, when an element is found:
\`\`\`json
{
  "bbox": [100, 100, 200, 200],
  "errors": []
}
\`\`\`

When no element is found:
\`\`\`json
{
  "bbox": [],
  "errors": ["I can see ..., but {some element} is not found"]
}
\`\`\`
`;
  }
  return `
## Role:
You are an expert in software page image (2D) and page element text analysis.

## Objective:
- Identify elements in screenshots and text that match the user's description.
- Return JSON data containing the selection reason and element ID.

## Skills:
- Image analysis and recognition
- Multilingual text understanding
- Software UI design and testing

## Workflow:
1. Receive the user's element description, screenshot, and element description information. Note that the text may contain non-English characters (e.g., Chinese), indicating that the application may be non-English.
2. Based on the user's description, locate the target element ID in the list of element descriptions and the screenshot.
3. Found the required number of elements
4. Return JSON data containing the selection reason and element ID.

## Constraints:
- Strictly adhere to the specified location when describing the required element; do not select elements from other locations.
- Elements in the image with NodeType other than "TEXT Node" have been highlighted to identify the element among multiple non-text elements.
- Accurately identify element information based on the user's description and return the corresponding element ID from the element description information, not extracted from the image.
- If no elements are found, the "elements" array should be empty.
- The returned data must conform to the specified JSON format.
- The returned value id information must use the id from element info (important: **use id not indexId, id is hash content**)

## Output Format:

Please return the result in JSON format as follows:

\`\`\`json
{
  "elements": [
    // If no matching elements are found, return an empty array []
    {
      "reason": "PLACEHOLDER", // The thought process for finding the element, replace PLACEHOLDER with your thought process
      "text": "PLACEHOLDER", // Replace PLACEHOLDER with the text of elementInfo, if none, leave empty
      "id": "PLACEHOLDER" // Replace PLACEHOLDER with the ID (important: **use id not indexId, id is hash content**) of elementInfo
    }
    // More elements...
  ],
  "errors": [] // Array of strings containing any error messages
}
\`\`\`

## Example:
Example 1:
Input Example:
\`\`\`json
// Description: "Shopping cart icon in the upper right corner"
{
  "description": "PLACEHOLDER", // Description of the target element
  "screenshot": "path/screenshot.png",
  "text": '{
      "pageSize": {
        "width": 400, // Width of the page
        "height": 905 // Height of the page
      },
      "elementInfos": [
        {
          "id": "1231", // ID of the element
          "indexId": "0", // Index of the element，The image is labeled to the left of the element
          "attributes": { // Attributes of the element
            "nodeType": "IMG Node", // Type of element, types include: TEXT Node, IMG Node, BUTTON Node, INPUT Node
            "src": "https://ap-southeast-3.m",
            "class": ".img"
          },
          "content": "", // Text content of the element
          "rect": {
            "left": 280, // Distance from the left side of the page
            "top": 8, // Distance from the top of the page
            "width": 44, // Width of the element
            "height": 44 // Height of the element
          }
        },
        {
          "id": "66551", // ID of the element
          "indexId": "1", // Index of the element,The image is labeled to the left of the element
          "attributes": { // Attributes of the element
            "nodeType": "IMG Node", // Type of element, types include: TEXT Node, IMG Node, BUTTON Node, INPUT Node
            "src": "data:image/png;base64,iVBORw0KGgoAAAANSU...",
            "class": ".icon"
          },
          "content": "", // Text content of the element
          "rect": {
            "left": 350, // Distance from the left side of the page
            "top": 16, // Distance from the top of the page
            "width": 25, // Width of the element
            "height": 25 // Height of the element
          }
        },
        ...
        {
          "id": "12344",
          "indexId": "2", // Index of the element，The image is labeled to the left of the element
          "attributes": {
            "nodeType": "TEXT Node",
            "class": ".product-name"
          },
          "center": [
            288,
            834
          ],
          "content": "Mango Drink",
          "rect": {
            "left": 188,
            "top": 827,
            "width": 199,
            "height": 13
          }
        },
        ...
      ]
    }
  '
}
\`\`\`
Output Example:
\`\`\`json
{
  "elements": [
    {
      // Describe the reason for finding this element, replace with actual value in practice
      "reason": "Reason for finding element 4: It is located in the upper right corner, is an image type, and according to the screenshot, it is a shopping cart icon button",
      "text": "",
      // ID(**use id not indexId**) of this element, replace with actual value in practice, **use id not indexId**
      "id": "1231"
    }
  ],
  "errors": []
}
\`\`\`
  
  `;
}
var locatorSchema = {
  type: "json_schema",
  json_schema: {
    name: "find_elements",
    strict: true,
    schema: {
      type: "object",
      properties: {
        elements: {
          type: "array",
          items: {
            type: "object",
            properties: {
              reason: {
                type: "string",
                description: "Reason for finding this element"
              },
              text: {
                type: "string",
                description: "Text content of the element"
              },
              id: {
                type: "string",
                description: "ID of this element"
              }
            },
            required: ["reason", "text", "id"],
            additionalProperties: false
          },
          description: "List of found elements"
        },
        errors: {
          type: "array",
          items: {
            type: "string"
          },
          description: "List of error messages, if any"
        }
      },
      required: ["elements", "errors"],
      additionalProperties: false
    }
  }
};
var findElementPrompt = new (0, _prompts.PromptTemplate)({
  template: `
Here is the item user want to find:
=====================================
{targetElementDescription}
=====================================

{pageDescription}
  `,
  inputVariables: ["pageDescription", "targetElementDescription"]
});

// src/ai-model/prompt/llm-planning.ts


// src/image/index.ts










// src/ai-model/prompt/util.ts








function describeSize(size) {
  return `${size.width} x ${size.height}`;
}
var distanceThreshold = 16;
function elementByPositionWithElementInfo(treeRoot, position, options) {
  const requireStrictDistance = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _10 => _10.requireStrictDistance]), () => ( true));
  const filterPositionElements = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _11 => _11.filterPositionElements]), () => ( false));
  _utils.assert.call(void 0, typeof position !== "undefined", "position is required for query");
  const matchingElements = [];
  function dfs(node) {
    if (_optionalChain([node, 'optionalAccess', _12 => _12.node])) {
      const item = node.node;
      if (item.rect.left <= position.x && position.x <= item.rect.left + item.rect.width && item.rect.top <= position.y && position.y <= item.rect.top + item.rect.height) {
        if (filterPositionElements && _optionalChain([item, 'access', _13 => _13.attributes, 'optionalAccess', _14 => _14.nodeType]) === _constants.NodeType.POSITION) {
          return;
        }
        matchingElements.push(item);
      }
    }
    for (const child of node.children) {
      dfs(child);
    }
  }
  dfs(treeRoot);
  if (matchingElements.length === 0) {
    return void 0;
  }
  const element = matchingElements.reduce((smallest, current) => {
    const smallestArea = smallest.rect.width * smallest.rect.height;
    const currentArea = current.rect.width * current.rect.height;
    return currentArea < smallestArea ? current : smallest;
  });
  const distanceToCenter = distance(
    { x: element.center[0], y: element.center[1] },
    position
  );
  if (requireStrictDistance) {
    return distanceToCenter <= distanceThreshold ? element : void 0;
  }
  return element;
}
function distance(point1, point2) {
  return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
}
var samplePageDescription = `
And the page is described as follows:
====================
The size of the page: 1280 x 720
Some of the elements are marked with a rectangle in the screenshot corresponding to the markerId, some are not.

Description of all the elements in screenshot:
<div id="969f1637" markerId="1" left="100" top="100" width="100" height="100"> // The markerId indicated by the rectangle label in the screenshot
  <h4 id="b211ecb2" markerId="5" left="150" top="150" width="90" height="60">
    The username is accepted
  </h4>
  ...many more
</div>
====================
`;
async function describeUserPage(context, opt) {
  const { screenshotBase64 } = context;
  let width;
  let height;
  if (context.size) {
    ({ width, height } = context.size);
  } else {
    const imgSize = await _img.imageInfoOfBase64.call(void 0, screenshotBase64);
    ({ width, height } = imgSize);
  }
  const treeRoot = context.tree;
  const idElementMap = {};
  const flatElements = _extractor.treeToList.call(void 0, treeRoot);
  flatElements.forEach((element) => {
    idElementMap[element.id] = element;
    if (typeof element.indexId !== "undefined") {
      idElementMap[`${element.indexId}`] = element;
    }
  });
  const contentTree = await _extractor.descriptionOfTree.call(void 0, 
    treeRoot,
    _optionalChain([opt, 'optionalAccess', _15 => _15.truncateTextLength]),
    _optionalChain([opt, 'optionalAccess', _16 => _16.filterNonTextContent])
  );
  const sizeDescription = describeSize({ width, height });
  const pageDescription = _env.vlLocateMode.call(void 0, ) ? "" : `The size of the page: ${sizeDescription} 
 Some of the elements are marked with a rectangle in the screenshot, some are not. 
 The page elements tree:
${contentTree}`;
  return {
    description: pageDescription,
    elementById(idOrIndexId) {
      _utils.assert.call(void 0, typeof idOrIndexId !== "undefined", "id is required for query");
      const item = idElementMap[`${idOrIndexId}`];
      return item;
    },
    elementByPosition(position, size) {
      return elementByPositionWithElementInfo(treeRoot, position);
    },
    insertElementByPosition(position) {
      const element = _extractor.generateElementByPosition.call(void 0, position);
      treeRoot.children.push({
        node: element,
        children: []
      });
      flatElements.push(element);
      idElementMap[element.id] = element;
      return element;
    },
    size: { width, height }
  };
}

// src/ai-model/prompt/llm-planning.ts
var vlCoTLog = `"what_the_user_wants_to_do_next_by_instruction": string, // What the user wants to do according to the instruction and previous logs. `;
var vlCurrentLog = `"log": string, // Log what the next one action (ONLY ONE!) you can do according to the screenshot and the instruction. The typical log looks like "Now i want to use action '{{ action-type }}' to do .. first". If no action should be done, log the reason. ". Use the same language as the user's instruction.`;
var llmCurrentLog = `"log": string, // Log what the next actions you can do according to the screenshot and the instruction. The typical log looks like "Now i want to use action '{{ action-type }}' to do ..". If no action should be done, log the reason. ". Use the same language as the user's instruction.`;
var commonOutputFields = `"error"?: string, // Error messages about unexpected situations, if any. Only think it is an error when the situation is not expected according to the instruction. Use the same language as the user's instruction.
  "more_actions_needed_by_instruction": boolean, // Consider if there is still more action(s) to do after the action in "Log" is done, according to the instruction. If so, set this field to true. Otherwise, set it to false.`;
var vlLocateParam = "locate: {bbox: [number, number, number, number], prompt: string }";
var systemTemplateOfVLPlanning = ({
  pageType,
  vlMode
}) => `
Target: User will give you a screenshot, an instruction and some previous logs indicating what have been done. Please tell what the next one action is (or null if no action should be done) to do the tasks the instruction requires. 

Restriction:
- Don't give extra actions or plans beyond the instruction. ONLY plan for what the instruction requires. For example, don't try to submit the form if the instruction is only to fill something.
- Always give ONLY ONE action in \`log\` field (or null if no action should be done), instead of multiple actions. Supported actions are Tap, Hover, Input, KeyboardPress, Scroll${pageType === "android" ? ", AndroidBackButton, AndroidHomeButton, AndroidRecentAppsButton." : "."}
- Don't repeat actions in the previous logs.
- Bbox is the bounding box of the element to be located. It's an array of 4 numbers, representing ${bboxDescription(vlMode)}.

Supporting actions:
- Tap: { type: "Tap", ${vlLocateParam} }
- Hover: { type: "Hover", ${vlLocateParam} }
- Input: { type: "Input", ${vlLocateParam}, param: { value: string } } // \`value\` is the final that should be filled in the input box. No matter what modifications are required, just provide the final value to replace the existing input value. 
- KeyboardPress: { type: "KeyboardPress", param: { value: string } }
- Scroll: { type: "Scroll", ${vlLocateParam} | null, param: { direction: 'down'(default) | 'up' | 'right' | 'left', scrollType: 'once' (default) | 'untilBottom' | 'untilTop' | 'untilRight' | 'untilLeft', distance: null | number }} // locate is the element to scroll. If it's a page scroll, put \`null\` in the \`locate\` field.
${pageType === "android" ? `- AndroidBackButton: { type: "AndroidBackButton", param: {} }
- AndroidHomeButton: { type: "AndroidHomeButton", param: {} }
- AndroidRecentAppsButton: { type: "AndroidRecentAppsButton", param: {} }` : ""}

Field description:
* The \`prompt\` field inside the \`locate\` field is a short description that could be used to locate the element.

Return in JSON format:
{
  ${vlCoTLog}
  ${vlCurrentLog}
  ${commonOutputFields}
  "action": 
    {
      // one of the supporting actions
    } | null,
  ,
  "sleep"?: number, // The sleep time after the action, in milliseconds.
}

For example, when the instruction is "click 'Confirm' button, and click 'Yes' in popup" and the log is "I will use action Tap to click 'Confirm' button", by viewing the screenshot and previous logs, you should consider: We have already clicked the 'Confirm' button, so next we should find and click 'Yes' in popup.

this and output the JSON:

{
  "what_the_user_wants_to_do_next_by_instruction": "We have already clicked the 'Confirm' button, so next we should find and click 'Yes' in popup",
  "log": "I will use action Tap to click 'Yes' in popup",
  "more_actions_needed_by_instruction": false,
  "action": {
    "type": "Tap",
    "locate": {
      "bbox": [100, 100, 200, 200],
      "prompt": "The 'Yes' button in popup"
    }
  }
}
`;
var llmLocateParam = `locate: {{"id": string, "prompt": string}} | null`;
var systemTemplateOfLLM = ({ pageType }) => `
## Role

You are a versatile professional in software UI automation. Your outstanding contributions will impact the user experience of billions of users.

## Objective

- Decompose the instruction user asked into a series of actions
- Locate the target element if possible
- If the instruction cannot be accomplished, give a further plan.

## Workflow

1. Receive the screenshot, element description of screenshot(if any), user's instruction and previous logs.
2. Decompose the user's task into a sequence of actions, and place it in the \`actions\` field. There are different types of actions (Tap / Hover / Input / KeyboardPress / Scroll / FalsyConditionStatement / Sleep ${pageType === "android" ? "/ AndroidBackButton / AndroidHomeButton / AndroidRecentAppsButton" : ""}). The "About the action" section below will give you more details.
3. Precisely locate the target element if it's already shown in the screenshot, put the location info in the \`locate\` field of the action.
4. If some target elements is not shown in the screenshot, consider the user's instruction is not feasible on this page. Follow the next steps.
5. Consider whether the user's instruction will be accomplished after all the actions
 - If yes, set \`taskWillBeAccomplished\` to true
 - If no, don't plan more actions by closing the array. Get ready to reevaluate the task. Some talent people like you will handle this. Give him a clear description of what have been done and what to do next. Put your new plan in the \`furtherPlan\` field. The "How to compose the \`taskWillBeAccomplished\` and \`furtherPlan\` fields" section will give you more details.

## Constraints

- All the actions you composed MUST be based on the page context information you get.
- Trust the "What have been done" field about the task (if any), don't repeat actions in it.
- Respond only with valid JSON. Do not write an introduction or summary or markdown prefix like \`\`\`json\`\`\`.
- If the screenshot and the instruction are totally irrelevant, set reason in the \`error\` field.

## About the \`actions\` field

The \`locate\` param is commonly used in the \`param\` field of the action, means to locate the target element to perform the action, it conforms to the following scheme:

type LocateParam = {{
  "id": string, // the id of the element found. It should either be the id marked with a rectangle in the screenshot or the id described in the description.
  "prompt"?: string // the description of the element to find. It can only be omitted when locate is null.
}} | null // If it's not on the page, the LocateParam should be null

## Supported actions

Each action has a \`type\` and corresponding \`param\`. To be detailed:
- type: 'Tap'
  * {{ ${llmLocateParam} }}
- type: 'Hover'
  * {{ ${llmLocateParam} }}
- type: 'Input', replace the value in the input field
  * {{ ${llmLocateParam}, param: {{ value: string }} }}
  * \`value\` is the final value that should be filled in the input field. No matter what modifications are required, just provide the final value user should see after the action is done. 
- type: 'KeyboardPress', press a key
  * {{ param: {{ value: string }} }}
- type: 'Scroll', scroll up or down.
  * {{ 
      ${llmLocateParam}, 
      param: {{ 
        direction: 'down'(default) | 'up' | 'right' | 'left', 
        scrollType: 'once' (default) | 'untilBottom' | 'untilTop' | 'untilRight' | 'untilLeft', 
        distance: null | number 
      }} 
    }}
    * To scroll some specific element, put the element at the center of the region in the \`locate\` field. If it's a page scroll, put \`null\` in the \`locate\` field. 
    * \`param\` is required in this action. If some fields are not specified, use direction \`down\`, \`once\` scroll type, and \`null\` distance.
  * {{ param: {{ button: 'Back' | 'Home' | 'RecentApp' }} }}
- type: 'ExpectedFalsyCondition'
  * {{ param: {{ reason: string }} }}
  * use this action when the conditional statement talked about in the instruction is falsy.
- type: 'Sleep'
  * {{ param: {{ timeMs: number }} }}
${pageType === "android" ? `- type: 'AndroidBackButton', trigger the system "back" operation on Android devices
  * {{ param: {{}} }}
- type: 'AndroidHomeButton', trigger the system "home" operation on Android devices
  * {{ param: {{}} }}
- type: 'AndroidRecentAppsButton', trigger the system "recent apps" operation on Android devices
  * {{ param: {{}} }}` : ""}
`;
var outputTemplate = `
## Output JSON Format:

The JSON format is as follows:

{{
  "actions": [
    // ... some actions
  ],
  ${llmCurrentLog}
  ${commonOutputFields}
}}

## Examples

### Example: Decompose a task

When the instruction is 'Click the language switch button, wait 1s, click "English"', and not log is provided

By viewing the page screenshot and description, you should consider this and output the JSON:

* The main steps should be: tap the switch button, sleep, and tap the 'English' option
* The language switch button is shown in the screenshot, but it's not marked with a rectangle. So we have to use the page description to find the element. By carefully checking the context information (coordinates, attributes, content, etc.), you can find the element.
* The "English" option button is not shown in the screenshot now, it means it may only show after the previous actions are finished. So don't plan any action to do this.
* Log what these action do: Click the language switch button to open the language options. Wait for 1 second.
* The task cannot be accomplished (because we cannot see the "English" option now), so the \`more_actions_needed_by_instruction\` field is true.

{{
  "actions":[
    {{
      "type": "Tap", 
      "thought": "Click the language switch button to open the language options.",
      "param": null,
      "locate": {{ id: "c81c4e9a33", prompt: "The language switch button" }},
    }},
    {{
      "type": "Sleep",
      "thought": "Wait for 1 second to ensure the language options are displayed.",
      "param": {{ "timeMs": 1000 }},
    }}
  ],
  "error": null,
  "more_actions_needed_by_instruction": true,
  "log": "Click the language switch button to open the language options. Wait for 1 second",
}}

### Example: What NOT to do
Wrong output:
{{
  "actions":[
    {{
      "type": "Tap",
      "thought": "Click the language switch button to open the language options.",
      "param": null,
      "locate": {{
        {{ "id": "c81c4e9a33" }}, // WRONG: prompt is missing
      }}
    }},
    {{
      "type": "Tap", 
      "thought": "Click the English option",
      "param": null,
      "locate": null, // This means the 'English' option is not shown in the screenshot, the task cannot be accomplished
    }}
  ],
  "more_actions_needed_by_instruction": false, // WRONG: should be true
  "log": "Click the language switch button to open the language options",
}}

Reason:
* The \`prompt\` is missing in the first 'Locate' action
* Since the option button is not shown in the screenshot, there are still more actions to be done, so the \`more_actions_needed_by_instruction\` field should be true
`;
async function systemPromptToTaskPlanning({
  pageType,
  vlMode
}) {
  if (vlMode) {
    return systemTemplateOfVLPlanning({ pageType, vlMode });
  }
  const promptTemplate = new (0, _prompts.PromptTemplate)({
    template: `${systemTemplateOfLLM({ pageType })}

${outputTemplate}`,
    inputVariables: ["pageDescription"]
  });
  return await promptTemplate.format({
    pageDescription: samplePageDescription
  });
}
var planSchema = {
  type: "json_schema",
  json_schema: {
    name: "action_items",
    strict: true,
    schema: {
      type: "object",
      strict: true,
      properties: {
        actions: {
          //  TODO
          type: "array",
          items: {
            type: "object",
            strict: true,
            properties: {
              thought: {
                type: "string",
                description: "Reasons for generating this task, and why this task is feasible on this page"
              },
              type: {
                type: "string",
                description: 'Type of action, one of "Tap", "Hover" , "Input", "KeyboardPress", "Scroll", "ExpectedFalsyCondition", "Sleep", "AndroidBackButton", "AndroidHomeButton", "AndroidRecentAppsButton"'
              },
              param: {
                anyOf: [
                  { type: "null" },
                  {
                    type: "object",
                    properties: { value: { type: ["string", "number"] } },
                    required: ["value"],
                    additionalProperties: false
                  },
                  {
                    type: "object",
                    properties: { timeMs: { type: ["number", "string"] } },
                    required: ["timeMs"],
                    additionalProperties: false
                  },
                  {
                    type: "object",
                    properties: {
                      direction: { type: "string" },
                      scrollType: { type: "string" },
                      distance: { type: ["number", "string", "null"] }
                    },
                    required: ["direction", "scrollType", "distance"],
                    additionalProperties: false
                  },
                  {
                    type: "object",
                    properties: { reason: { type: "string" } },
                    required: ["reason"],
                    additionalProperties: false
                  },
                  {
                    type: "object",
                    properties: { button: { type: "string" } },
                    required: ["button"],
                    additionalProperties: false
                  }
                ],
                description: "Parameter of the action, can be null ONLY when the type field is Tap or Hover"
              },
              locate: {
                type: ["object", "null"],
                properties: {
                  id: { type: "string" },
                  prompt: { type: "string" }
                },
                required: ["id", "prompt"],
                additionalProperties: false,
                description: "Location information for the target element"
              }
            },
            required: ["thought", "type", "param", "locate"],
            additionalProperties: false
          },
          description: "List of actions to be performed"
        },
        more_actions_needed_by_instruction: {
          type: "boolean",
          description: "If all the actions described in the instruction have been covered by this action and logs, set this field to false."
        },
        log: {
          type: "string",
          description: "Log what these planned actions do. Do not include further actions that have not been planned."
        },
        error: {
          type: ["string", "null"],
          description: "Error messages about unexpected situations"
        }
      },
      required: [
        "actions",
        "more_actions_needed_by_instruction",
        "log",
        "error"
      ],
      additionalProperties: false
    }
  }
};
var generateTaskBackgroundContext = (userInstruction, log, userActionContext) => {
  if (log) {
    return `
Here is the user's instruction:

<instruction>
  <high_priority_knowledge>
    ${userActionContext}
  </high_priority_knowledge>

  ${userInstruction}
</instruction>

These are the logs from previous executions, which indicate what was done in the previous actions.
Do NOT repeat these actions.
<previous_logs>
${log}
</previous_logs>
`;
  }
  return `
Here is the user's instruction:
<instruction>
  <high_priority_knowledge>
    ${userActionContext}
  </high_priority_knowledge>

  ${userInstruction}
</instruction>
`;
};
var automationUserPrompt = (vlMode) => {
  if (vlMode) {
    return new (0, _prompts.PromptTemplate)({
      template: "{taskBackgroundContext}",
      inputVariables: ["taskBackgroundContext"]
    });
  }
  return new (0, _prompts.PromptTemplate)({
    template: `
pageDescription:
=====================================
{pageDescription}
=====================================

{taskBackgroundContext}`,
    inputVariables: ["pageDescription", "taskBackgroundContext"]
  });
};

// src/ai-model/service-caller/index.ts
function checkAIConfig() {
  if (_env.getAIConfig.call(void 0, _env.OPENAI_API_KEY))
    return true;
  if (_env.getAIConfig.call(void 0, _env.MIDSCENE_USE_AZURE_OPENAI))
    return true;
  if (_env.getAIConfig.call(void 0, _env.ANTHROPIC_API_KEY))
    return true;
  return Boolean(_env.getAIConfig.call(void 0, _env.MIDSCENE_OPENAI_INIT_CONFIG_JSON));
}
var debugConfigInitialized = false;
function initDebugConfig() {
  if (debugConfigInitialized)
    return;
  const shouldPrintTiming = _env.getAIConfigInBoolean.call(void 0, _env.MIDSCENE_DEBUG_AI_PROFILE);
  let debugConfig = "";
  if (shouldPrintTiming) {
    console.warn(
      "MIDSCENE_DEBUG_AI_PROFILE is deprecated, use DEBUG=midscene:ai:profile instead"
    );
    debugConfig = "ai:profile";
  }
  const shouldPrintAIResponse = _env.getAIConfigInBoolean.call(void 0, 
    _env.MIDSCENE_DEBUG_AI_RESPONSE
  );
  if (shouldPrintAIResponse) {
    console.warn(
      "MIDSCENE_DEBUG_AI_RESPONSE is deprecated, use DEBUG=midscene:ai:response instead"
    );
    if (debugConfig) {
      debugConfig = "ai:*";
    } else {
      debugConfig = "ai:call";
    }
  }
  if (debugConfig) {
    _logger.enableDebug.call(void 0, debugConfig);
  }
  debugConfigInitialized = true;
}
var defaultModel = "gpt-4o";
function getModelName() {
  let modelName = defaultModel;
  const nameInConfig = _env.getAIConfig.call(void 0, _env.MIDSCENE_MODEL_NAME);
  if (nameInConfig) {
    modelName = nameInConfig;
  }
  return modelName;
}
async function createChatClient({
  AIActionTypeValue
}) {
  initDebugConfig();
  let openai;
  const extraConfig = _env.getAIConfigInJson.call(void 0, _env.MIDSCENE_OPENAI_INIT_CONFIG_JSON);
  const socksProxy = _env.getAIConfig.call(void 0, _env.MIDSCENE_OPENAI_SOCKS_PROXY);
  const httpProxy = _env.getAIConfig.call(void 0, _env.MIDSCENE_OPENAI_HTTP_PROXY);
  let proxyAgent = void 0;
  if (httpProxy) {
    proxyAgent = new (0, _httpsproxyagent.HttpsProxyAgent)(httpProxy);
  } else if (socksProxy) {
    proxyAgent = new (0, _socksproxyagent.SocksProxyAgent)(socksProxy);
  }
  if (_env.getAIConfig.call(void 0, _env.OPENAI_USE_AZURE)) {
    openai = new (0, _openai.AzureOpenAI)({
      baseURL: _env.getAIConfig.call(void 0, _env.OPENAI_BASE_URL),
      apiKey: _env.getAIConfig.call(void 0, _env.OPENAI_API_KEY),
      httpAgent: proxyAgent,
      ...extraConfig,
      dangerouslyAllowBrowser: true
    });
  } else if (_env.getAIConfig.call(void 0, _env.MIDSCENE_USE_AZURE_OPENAI)) {
    const extraAzureConfig = _env.getAIConfigInJson.call(void 0, 
      _env.MIDSCENE_AZURE_OPENAI_INIT_CONFIG_JSON
    );
    const scope = _env.getAIConfig.call(void 0, _env.MIDSCENE_AZURE_OPENAI_SCOPE);
    let tokenProvider = void 0;
    if (scope) {
      _utils.assert.call(void 0, 
        !_utils.ifInBrowser,
        "Azure OpenAI is not supported in browser with Midscene."
      );
      const credential = new (0, _identity.DefaultAzureCredential)();
      _utils.assert.call(void 0, scope, "MIDSCENE_AZURE_OPENAI_SCOPE is required");
      tokenProvider = _identity.getBearerTokenProvider.call(void 0, credential, scope);
      openai = new (0, _openai.AzureOpenAI)({
        azureADTokenProvider: tokenProvider,
        endpoint: _env.getAIConfig.call(void 0, _env.AZURE_OPENAI_ENDPOINT),
        apiVersion: _env.getAIConfig.call(void 0, _env.AZURE_OPENAI_API_VERSION),
        deployment: _env.getAIConfig.call(void 0, _env.AZURE_OPENAI_DEPLOYMENT),
        ...extraConfig,
        ...extraAzureConfig
      });
    } else {
      openai = new (0, _openai.AzureOpenAI)({
        apiKey: _env.getAIConfig.call(void 0, _env.AZURE_OPENAI_KEY),
        endpoint: _env.getAIConfig.call(void 0, _env.AZURE_OPENAI_ENDPOINT),
        apiVersion: _env.getAIConfig.call(void 0, _env.AZURE_OPENAI_API_VERSION),
        deployment: _env.getAIConfig.call(void 0, _env.AZURE_OPENAI_DEPLOYMENT),
        dangerouslyAllowBrowser: true,
        ...extraConfig,
        ...extraAzureConfig
      });
    }
  } else if (!_env.getAIConfig.call(void 0, _env.MIDSCENE_USE_ANTHROPIC_SDK)) {
    const baseURL = _env.getAIConfig.call(void 0, _env.OPENAI_BASE_URL);
    if (typeof baseURL === "string") {
      if (!/^https?:\/\//.test(baseURL)) {
        throw new Error(
          `OPENAI_BASE_URL must be a valid URL starting with http:// or https://, but got: ${baseURL}
Please check your config.`
        );
      }
    }
    openai = new (0, _openai2.default)({
      baseURL: _env.getAIConfig.call(void 0, _env.OPENAI_BASE_URL),
      apiKey: _env.getAIConfig.call(void 0, _env.OPENAI_API_KEY),
      httpAgent: proxyAgent,
      ...extraConfig,
      defaultHeaders: {
        ..._optionalChain([extraConfig, 'optionalAccess', _17 => _17.defaultHeaders]) || {},
        [_env.MIDSCENE_API_TYPE]: AIActionTypeValue.toString()
      },
      dangerouslyAllowBrowser: true
    });
  }
  if (openai && _env.getAIConfigInBoolean.call(void 0, _env.MIDSCENE_LANGSMITH_DEBUG)) {
    if (_utils.ifInBrowser) {
      throw new Error("langsmith is not supported in browser");
    }
    console.log("DEBUGGING MODE: langsmith wrapper enabled");
    const { wrapOpenAI } = await Promise.resolve().then(() => require("langsmith/wrappers"));
    openai = wrapOpenAI(openai);
  }
  if (typeof openai !== "undefined") {
    return {
      completion: openai.chat.completions,
      style: "openai"
    };
  }
  if (_env.getAIConfig.call(void 0, _env.MIDSCENE_USE_ANTHROPIC_SDK)) {
    const apiKey = _env.getAIConfig.call(void 0, _env.ANTHROPIC_API_KEY);
    _utils.assert.call(void 0, apiKey, "ANTHROPIC_API_KEY is required");
    openai = new (0, _sdk.Anthropic)({
      apiKey,
      httpAgent: proxyAgent,
      dangerouslyAllowBrowser: true
    });
  }
  if (typeof openai !== "undefined" && openai.messages) {
    return {
      completion: openai.messages,
      style: "anthropic"
    };
  }
  throw new Error("Openai SDK or Anthropic SDK is not initialized");
}
async function call(messages, AIActionTypeValue, responseFormat) {
  const { completion, style } = await createChatClient({
    AIActionTypeValue
  });
  const maxTokens = _env.getAIConfig.call(void 0, _env.OPENAI_MAX_TOKENS);
  const debugCall = _logger.getDebug.call(void 0, "ai:call");
  const debugProfileStats = _logger.getDebug.call(void 0, "ai:profile:stats");
  const debugProfileDetail = _logger.getDebug.call(void 0, "ai:profile:detail");
  const startTime = Date.now();
  const model = getModelName();
  let content;
  let usage;
  const commonConfig = {
    temperature: _env.vlLocateMode.call(void 0, ) === "vlm-ui-tars" ? 0 : 0.1,
    stream: false,
    max_tokens: typeof maxTokens === "number" ? maxTokens : Number.parseInt(maxTokens || "2048", 10),
    ..._env.vlLocateMode.call(void 0, ) === "qwen-vl" ? {
      vl_high_resolution_images: true
    } : {}
  };
  if (style === "openai") {
    debugCall(`sending request to ${model}`);
    let result;
    try {
      result = await completion.create({
        model,
        messages,
        response_format: responseFormat,
        ...commonConfig
      });
    } catch (e) {
      const newError = new Error(
        `failed to call AI model service: ${e.message}. Trouble shooting: https://midscenejs.com/model-provider.html`,
        {
          cause: e
        }
      );
      throw newError;
    }
    debugProfileStats(
      `model, ${model}, mode, ${_env.vlLocateMode.call(void 0, ) || "default"}, ui-tars-version, ${_env.uiTarsModelVersion.call(void 0, )}, prompt-tokens, ${_optionalChain([result, 'access', _18 => _18.usage, 'optionalAccess', _19 => _19.prompt_tokens]) || ""}, completion-tokens, ${_optionalChain([result, 'access', _20 => _20.usage, 'optionalAccess', _21 => _21.completion_tokens]) || ""}, total-tokens, ${_optionalChain([result, 'access', _22 => _22.usage, 'optionalAccess', _23 => _23.total_tokens]) || ""}, cost-ms, ${Date.now() - startTime}, requestId, ${result._request_id || ""}`
    );
    debugProfileDetail(`model usage detail: ${JSON.stringify(result.usage)}`);
    _utils.assert.call(void 0, 
      result.choices,
      `invalid response from LLM service: ${JSON.stringify(result)}`
    );
    content = result.choices[0].message.content;
    debugCall(`response: ${content}`);
    _utils.assert.call(void 0, content, "empty content");
    usage = result.usage;
  } else if (style === "anthropic") {
    const convertImageContent = (content2) => {
      if (content2.type === "image_url") {
        const imgBase64 = content2.image_url.url;
        _utils.assert.call(void 0, imgBase64, "image_url is required");
        return {
          source: {
            type: "base64",
            media_type: imgBase64.includes("data:image/png;base64,") ? "image/png" : "image/jpeg",
            data: imgBase64.split(",")[1]
          },
          type: "image"
        };
      }
      return content2;
    };
    const result = await completion.create({
      model,
      system: "You are a versatile professional in software UI automation",
      messages: messages.map((m) => ({
        role: "user",
        content: Array.isArray(m.content) ? m.content.map(convertImageContent) : m.content
      })),
      response_format: responseFormat,
      ...commonConfig
    });
    content = result.content[0].text;
    _utils.assert.call(void 0, content, "empty content");
    usage = result.usage;
  }
  return { content: content || "", usage };
}
async function callToGetJSONObject(messages, AIActionTypeValue) {
  let responseFormat;
  const model = getModelName();
  if (model.includes("gpt-4")) {
    switch (AIActionTypeValue) {
      case 0 /* ASSERT */:
        responseFormat = assertSchema;
        break;
      case 1 /* INSPECT_ELEMENT */:
        responseFormat = locatorSchema;
        break;
      case 3 /* PLAN */:
        responseFormat = planSchema;
        break;
      case 2 /* EXTRACT_DATA */:
      case 4 /* DESCRIBE_ELEMENT */:
        responseFormat = { type: "json_object" /* JSON */ };
        break;
    }
  }
  if (model === "gpt-4o-2024-05-13") {
    responseFormat = { type: "json_object" /* JSON */ };
  }
  const response = await call(messages, AIActionTypeValue, responseFormat);
  _utils.assert.call(void 0, response, "empty response");
  const jsonContent = safeParseJson(response.content);
  return { content: jsonContent, usage: response.usage };
}
function extractJSONFromCodeBlock(response) {
  try {
    const jsonMatch = response.match(/^\s*(\{[\s\S]*\})\s*$/);
    if (jsonMatch) {
      return jsonMatch[1];
    }
    const codeBlockMatch = response.match(
      /```(?:json)?\s*(\{[\s\S]*?\})\s*```/
    );
    if (codeBlockMatch) {
      return codeBlockMatch[1];
    }
    const jsonLikeMatch = response.match(/\{[\s\S]*\}/);
    if (jsonLikeMatch) {
      return jsonLikeMatch[0];
    }
  } catch (e2) {
  }
  return response;
}
function preprocessDoubaoBboxJson(input) {
  if (input.includes("bbox")) {
    while (/\d+\s+\d+/.test(input)) {
      input = input.replace(/(\d+)\s+(\d+)/g, "$1,$2");
    }
  }
  return input;
}
function safeParseJson(input) {
  const cleanJsonString = extractJSONFromCodeBlock(input);
  if (_optionalChain([cleanJsonString, 'optionalAccess', _24 => _24.match, 'call', _25 => _25(/\((\d+),(\d+)\)/)])) {
    return _optionalChain([cleanJsonString, 'access', _26 => _26.match, 'call', _27 => _27(/\((\d+),(\d+)\)/), 'optionalAccess', _28 => _28.slice, 'call', _29 => _29(1), 'access', _30 => _30.map, 'call', _31 => _31(Number)]);
  }
  try {
    return JSON.parse(cleanJsonString);
  } catch (e3) {
  }
  try {
    return _dirtyjson2.default.parse(cleanJsonString);
  } catch (e) {
  }
  if (_env.vlLocateMode.call(void 0, ) === "doubao-vision" || _env.vlLocateMode.call(void 0, ) === "vlm-ui-tars") {
    const jsonString = preprocessDoubaoBboxJson(cleanJsonString);
    return _dirtyjson2.default.parse(jsonString);
  }
  throw Error(`failed to parse json response: ${input}`);
}

// src/ai-model/inspect.ts










// src/ai-model/prompt/extraction.ts

function systemPromptToExtract() {
  return `
You are a versatile professional in software UI design and testing. Your outstanding contributions will impact the user experience of billions of users.

The user will give you a screenshot, the contents of it (optional), and some data requirements in <DATA_DEMAND>. You need to extract the data according to the <DATA_DEMAND>.

If a key specifies a JSON data type (such as Number, String, Boolean, Object, Array), ensure the returned value strictly matches that data type.

Return in the following JSON format:
{
  data: any, // the extracted data. Make sure both the value and scheme meet the DATA_DEMAND. If you want to write some description in this field, use the same language as the DATA_DEMAND.
  errors: [], // string[], error message if any
}

# Example 1
For example, if the DATA_DEMAND is:

<DATA_DEMAND>
{
  "name": "name shows on the left panel, string",
  "age": "age shows on the right panel, number",
  "isAdmin": "if the user is admin, boolean"
}
</DATA_DEMAND>

By viewing the screenshot and page contents, you can extract the following data:

{
  data: {
    name: "John",
    age: 30,
    isAdmin: true
  },
}

# Example 2
If the DATA_DEMAND is:

<DATA_DEMAND>
the todo items list, string[]
</DATA_DEMAND>

By viewing the screenshot and page contents, you can extract the following data:

{
  data: ["todo 1", "todo 2", "todo 3"],
}

# Example 3
If the DATA_DEMAND is:

<DATA_DEMAND>
the page title, string
</DATA_DEMAND>

By viewing the screenshot and page contents, you can extract the following data:

{
  data: "todo list",
}

`;
}
var extractDataQueryPrompt = async (pageDescription, dataQuery) => {
  let dataQueryText = "";
  if (typeof dataQuery === "string") {
    dataQueryText = dataQuery;
  } else {
    dataQueryText = JSON.stringify(dataQuery, null, 2);
  }
  const extractDataPrompt = new (0, _prompts.PromptTemplate)({
    template: `
<PageDescription>
{pageDescription}
</PageDescription>

<DATA_DEMAND>
{dataQuery}
</DATA_DEMAND>
  `,
    inputVariables: ["pageDescription", "dataQuery"]
  });
  return await extractDataPrompt.format({
    pageDescription,
    dataQuery: dataQueryText
  });
};

// src/ai-model/prompt/llm-section-locator.ts

function systemPromptToLocateSection(vlMode) {
  return `
You goal is to find out one section containing the target element in the screenshot, put it in the \`bbox\` field. If the user describe the target element with some reference elements, you should also find the section containing the reference elements, put it in the \`references_bbox\` field.

Usually, it should be approximately an area not more than 300x300px. Changes of the size are allowed if there are many elements to cover.

return in this JSON format:
\`\`\`json
{
  "bbox": [number, number, number, number],
  "references_bbox"?: [
    [number, number, number, number],
    [number, number, number, number],
    ...
  ],
  "error"?: string
}
\`\`\`

In which, all the numbers in the \`bbox\` and \`references_bbox\` represent ${bboxDescription(vlMode)}.

For example, if the user describe the target element as "the delete button on the second row with title 'Peter'", you should put the bounding box of the delete button in the \`bbox\` field, and the bounding box of the second row in the \`references_bbox\` field.

the return value should be like this:
\`\`\`json
{
  "bbox": [100, 100, 200, 200],
  "references_bbox": [[100, 100, 200, 200]]
}
\`\`\`
`;
}
var sectionLocatorInstruction = new (0, _prompts.PromptTemplate)({
  template: `Here is the target element user interested in:
<targetDescription>
{sectionDescription}
</targetDescription>
  `,
  inputVariables: ["sectionDescription"]
});

// src/ai-model/inspect.ts
var liteContextConfig = {
  filterNonTextContent: true,
  truncateTextLength: 200
};
var debugInspect = _logger.getDebug.call(void 0, "ai:inspect");
var debugSection = _logger.getDebug.call(void 0, "ai:section");
async function AiLocateElement(options) {
  const { context, targetElementDescription, callAI } = options;
  const { screenshotBase64 } = context;
  const { description, elementById, insertElementByPosition } = await describeUserPage(context);
  _utils.assert.call(void 0, 
    targetElementDescription,
    "cannot find the target element description"
  );
  const userInstructionPrompt = await findElementPrompt.format({
    pageDescription: description,
    targetElementDescription
  });
  const systemPrompt = systemPromptToLocateElement(_env.vlLocateMode.call(void 0, ));
  let imagePayload = screenshotBase64;
  if (options.searchConfig) {
    _utils.assert.call(void 0, 
      options.searchConfig.rect,
      "searchArea is provided but its rect cannot be found. Failed to locate element"
    );
    _utils.assert.call(void 0, 
      options.searchConfig.imageBase64,
      "searchArea is provided but its imageBase64 cannot be found. Failed to locate element"
    );
    imagePayload = options.searchConfig.imageBase64;
  } else if (_env.vlLocateMode.call(void 0, ) === "qwen-vl") {
    imagePayload = await _img.paddingToMatchBlockByBase64.call(void 0, imagePayload);
  } else if (!_env.vlLocateMode.call(void 0, )) {
    imagePayload = await markupImageForLLM(
      screenshotBase64,
      context.tree,
      context.size
    );
  }
  let referenceImagePayload;
  if (_optionalChain([options, 'access', _32 => _32.referenceImage, 'optionalAccess', _33 => _33.rect]) && options.referenceImage.base64) {
    referenceImagePayload = await _img.cropByRect.call(void 0, 
      options.referenceImage.base64,
      options.referenceImage.rect,
      _env.getAIConfigInBoolean.call(void 0, _env.MIDSCENE_USE_QWEN_VL)
    );
  }
  const msgs = [
    { role: "system", content: systemPrompt },
    {
      role: "user",
      content: [
        {
          type: "image_url",
          image_url: {
            url: imagePayload,
            detail: "high"
          }
        },
        {
          type: "text",
          text: userInstructionPrompt
        }
      ]
    }
  ];
  const callAIFn = callAI || callToGetJSONObject;
  const res = await callAIFn(msgs, 1 /* INSPECT_ELEMENT */);
  const rawResponse = JSON.stringify(res.content);
  let resRect;
  let matchedElements = "elements" in res.content ? res.content.elements : [];
  let errors = "errors" in res.content ? res.content.errors : [];
  try {
    if ("bbox" in res.content && Array.isArray(res.content.bbox)) {
      resRect = adaptBboxToRect(
        res.content.bbox,
        _optionalChain([options, 'access', _34 => _34.searchConfig, 'optionalAccess', _35 => _35.rect, 'optionalAccess', _36 => _36.width]) || context.size.width,
        _optionalChain([options, 'access', _37 => _37.searchConfig, 'optionalAccess', _38 => _38.rect, 'optionalAccess', _39 => _39.height]) || context.size.height,
        _optionalChain([options, 'access', _40 => _40.searchConfig, 'optionalAccess', _41 => _41.rect, 'optionalAccess', _42 => _42.left]),
        _optionalChain([options, 'access', _43 => _43.searchConfig, 'optionalAccess', _44 => _44.rect, 'optionalAccess', _45 => _45.top])
      );
      debugInspect("resRect", resRect);
      const rectCenter = {
        x: resRect.left + resRect.width / 2,
        y: resRect.top + resRect.height / 2
      };
      let element = elementByPositionWithElementInfo(context.tree, rectCenter);
      const distanceToCenter = element ? distance({ x: element.center[0], y: element.center[1] }, rectCenter) : 0;
      if (!element || distanceToCenter > distanceThreshold) {
        element = insertElementByPosition(rectCenter);
      }
      if (element) {
        matchedElements = [element];
        errors = [];
      }
    }
  } catch (e) {
    const msg = e instanceof Error ? `Failed to parse bbox: ${e.message}` : "unknown error in locate";
    if (!errors || _optionalChain([errors, 'optionalAccess', _46 => _46.length]) === 0) {
      errors = [msg];
    } else {
      errors.push(`(${msg})`);
    }
  }
  return {
    rect: resRect,
    parseResult: {
      elements: matchedElements,
      errors
    },
    rawResponse,
    elementById,
    usage: res.usage
  };
}
async function AiLocateSection(options) {
  const { context, sectionDescription } = options;
  const { screenshotBase64 } = context;
  const systemPrompt = systemPromptToLocateSection(_env.vlLocateMode.call(void 0, ));
  const sectionLocatorInstructionText = await sectionLocatorInstruction.format({
    sectionDescription
  });
  const msgs = [
    { role: "system", content: systemPrompt },
    {
      role: "user",
      content: [
        {
          type: "image_url",
          image_url: {
            url: screenshotBase64,
            detail: "high"
          }
        },
        {
          type: "text",
          text: sectionLocatorInstructionText
        }
      ]
    }
  ];
  const result = await callAiFn(
    msgs,
    2 /* EXTRACT_DATA */
  );
  let sectionRect;
  const sectionBbox = result.content.bbox;
  if (sectionBbox) {
    const targetRect = adaptBboxToRect(
      sectionBbox,
      context.size.width,
      context.size.height
    );
    debugSection("original targetRect %j", targetRect);
    const referenceBboxList = result.content.references_bbox || [];
    debugSection("referenceBboxList %j", referenceBboxList);
    const referenceRects = referenceBboxList.filter((bbox) => Array.isArray(bbox)).map((bbox) => {
      return adaptBboxToRect(bbox, context.size.width, context.size.height);
    });
    debugSection("referenceRects %j", referenceRects);
    const mergedRect = mergeRects([targetRect, ...referenceRects]);
    debugSection("mergedRect %j", mergedRect);
    sectionRect = expandSearchArea(mergedRect, context.size);
    debugSection("expanded sectionRect %j", sectionRect);
  }
  let imageBase64 = screenshotBase64;
  if (sectionRect) {
    imageBase64 = await _img.cropByRect.call(void 0, 
      screenshotBase64,
      sectionRect,
      _env.getAIConfigInBoolean.call(void 0, _env.MIDSCENE_USE_QWEN_VL)
    );
  }
  return {
    rect: sectionRect,
    imageBase64,
    error: result.content.error,
    rawResponse: JSON.stringify(result.content),
    usage: result.usage
  };
}
async function AiExtractElementInfo(options) {
  const { dataQuery, context } = options;
  const systemPrompt = systemPromptToExtract();
  const { screenshotBase64 } = context;
  const { description, elementById } = await describeUserPage(
    context,
    liteContextConfig
  );
  const extractDataPromptText = await extractDataQueryPrompt(
    description,
    dataQuery
  );
  const msgs = [
    { role: "system", content: systemPrompt },
    {
      role: "user",
      content: [
        {
          type: "image_url",
          image_url: {
            url: screenshotBase64,
            detail: "high"
          }
        },
        {
          type: "text",
          text: extractDataPromptText
        }
      ]
    }
  ];
  const result = await callAiFn(
    msgs,
    2 /* EXTRACT_DATA */
  );
  return {
    parseResult: result.content,
    elementById,
    usage: result.usage
  };
}
async function AiAssert(options) {
  const { assertion, context } = options;
  _utils.assert.call(void 0, assertion, "assertion should be a string");
  const { screenshotBase64 } = context;
  const systemPrompt = systemPromptToAssert({
    isUITars: _env.getAIConfigInBoolean.call(void 0, _env.MIDSCENE_USE_VLM_UI_TARS)
  });
  const msgs = [
    { role: "system", content: systemPrompt },
    {
      role: "user",
      content: [
        {
          type: "image_url",
          image_url: {
            url: screenshotBase64,
            detail: "high"
          }
        },
        {
          type: "text",
          text: `
Here is the assertion. Please tell whether it is truthy according to the screenshot.
=====================================
${assertion}
=====================================
  `
        }
      ]
    }
  ];
  const { content: assertResult, usage } = await callAiFn(
    msgs,
    0 /* ASSERT */
  );
  return {
    content: assertResult,
    usage
  };
}

// src/ai-model/llm-planning.ts



async function plan(userInstruction, opts) {
  const { callAI, context } = opts || {};
  const { screenshotBase64, size } = context;
  const { description: pageDescription, elementById } = await describeUserPage(context);
  const systemPrompt = await systemPromptToTaskPlanning({
    pageType: opts.pageType,
    vlMode: _env.vlLocateMode.call(void 0, )
  });
  const taskBackgroundContextText = generateTaskBackgroundContext(
    userInstruction,
    opts.log,
    opts.actionContext
  );
  const userInstructionPrompt = await automationUserPrompt(
    _env.vlLocateMode.call(void 0, )
  ).format({
    pageDescription,
    taskBackgroundContext: taskBackgroundContextText
  });
  let imagePayload = screenshotBase64;
  if (_env.vlLocateMode.call(void 0, ) === "qwen-vl") {
    imagePayload = await _img.paddingToMatchBlockByBase64.call(void 0, imagePayload);
  } else if (!_env.vlLocateMode.call(void 0, )) {
    imagePayload = await markupImageForLLM(
      screenshotBase64,
      context.tree,
      context.size
    );
  }
  warnGPT4oSizeLimit(size);
  const msgs = [
    { role: "system", content: systemPrompt },
    {
      role: "user",
      content: [
        {
          type: "image_url",
          image_url: {
            url: imagePayload,
            detail: "high"
          }
        },
        {
          type: "text",
          text: userInstructionPrompt
        }
      ]
    }
  ];
  const call2 = callAI || callAiFn;
  const { content, usage } = await call2(msgs, 3 /* PLAN */);
  const rawResponse = JSON.stringify(content, void 0, 2);
  const planFromAI = content;
  const actions = (_optionalChain([planFromAI, 'access', _47 => _47.action, 'optionalAccess', _48 => _48.type]) ? [planFromAI.action] : planFromAI.actions) || [];
  const returnValue = {
    ...planFromAI,
    actions,
    rawResponse,
    usage,
    yamlFlow: buildYamlFlowFromPlans(actions, planFromAI.sleep)
  };
  _utils.assert.call(void 0, planFromAI, "can't get plans from AI");
  if (_env.vlLocateMode.call(void 0, )) {
    actions.forEach((action) => {
      if (action.locate) {
        try {
          action.locate = fillBboxParam(action.locate, size.width, size.height);
        } catch (e) {
          throw new Error(
            `Failed to fill locate param: ${planFromAI.error} (${e instanceof Error ? e.message : "unknown error"})`,
            {
              cause: e
            }
          );
        }
      }
    });
    _utils.assert.call(void 0, !planFromAI.error, `Failed to plan actions: ${planFromAI.error}`);
  } else {
    actions.forEach((action) => {
      if (_optionalChain([action, 'access', _49 => _49.locate, 'optionalAccess', _50 => _50.id])) {
        const element = elementById(action.locate.id);
        if (element) {
          action.locate.id = element.id;
        }
      }
    });
  }
  if (actions.length === 0 && returnValue.more_actions_needed_by_instruction && !returnValue.sleep) {
    console.warn(
      "No actions planned for the prompt, but model said more actions are needed:",
      userInstruction
    );
  }
  return returnValue;
}

// src/ai-model/ui-tars-planning.ts






var _keyboardlayout = require('@midscene_spp/shared/keyboard-layout');


var _actionparser = require('@ui-tars/action-parser');

// src/ai-model/prompt/ui-tars-planning.ts

function getUiTarsPlanningPrompt() {
  const preferredLanguage = _env.getPreferredLanguage.call(void 0, );
  return `
You are a GUI agent. You are given a task and your action history, with screenshots. You need to perform the next action to complete the task. 

## Output Format
\`\`\`
Thought: ...
Action: ...
\`\`\`

## Action Space

click(start_box='[x1, y1, x2, y2]')
left_double(start_box='[x1, y1, x2, y2]')
right_single(start_box='[x1, y1, x2, y2]')
drag(start_box='[x1, y1, x2, y2]', end_box='[x3, y3, x4, y4]')
hotkey(key='')
type(content='xxx') # Use escape characters \\', \\", and \\n in content part to ensure we can parse the content in normal python string format. If you want to submit your input, use \\n at the end of content. 
scroll(start_box='[x1, y1, x2, y2]', direction='down or up or right or left')
wait() #Sleep for 5s and take a screenshot to check for any changes.
finished(content='xxx') # Use escape characters \\', \\", and \\n in content part to ensure we can parse the content in normal python string format.


## Note
- Use ${preferredLanguage} in \`Thought\` part.
- Write a small plan and finally summarize your next action (with its target element) in one sentence in \`Thought\` part.

## User Instruction
`;
}
var getSummary = (prediction) => prediction.replace(/Reflection:[\s\S]*?(?=Action_Summary:|Action:|$)/g, "").trim();

// src/ai-model/ui-tars-planning.ts
var debug = _logger.getDebug.call(void 0, "ui-tars-planning");
var bboxSize = 10;
var pointToBbox = (point, width, height) => {
  return [
    Math.round(Math.max(point.x - bboxSize / 2, 0)),
    Math.round(Math.max(point.y - bboxSize / 2, 0)),
    Math.round(Math.min(point.x + bboxSize / 2, width)),
    Math.round(Math.min(point.y + bboxSize / 2, height))
  ];
};
async function vlmPlanning(options) {
  const { conversationHistory, userInstruction, size } = options;
  const systemPrompt = getUiTarsPlanningPrompt() + userInstruction;
  const res = await call(
    [
      {
        role: "user",
        content: systemPrompt
      },
      ...conversationHistory
    ],
    1 /* INSPECT_ELEMENT */
  );
  const convertedText = convertBboxToCoordinates(res.content);
  const modelVer = _env.uiTarsModelVersion.call(void 0, );
  const { parsed } = _actionparser.actionParser.call(void 0, {
    prediction: convertedText,
    factor: [1e3, 1e3],
    screenContext: {
      width: size.width,
      height: size.height
    },
    modelVer: modelVer || void 0
  });
  debug("modelVer", modelVer, "parsed", JSON.stringify(parsed));
  const transformActions = [];
  parsed.forEach((action) => {
    if (action.action_type === "click") {
      _utils.assert.call(void 0, action.action_inputs.start_box, "start_box is required");
      const point = getPoint(action.action_inputs.start_box, size);
      transformActions.push({
        type: "Locate",
        param: {},
        locate: {
          prompt: action.thought || "",
          bbox: pointToBbox(
            { x: point[0], y: point[1] },
            size.width,
            size.height
          )
        }
      });
      transformActions.push({
        type: "Tap",
        locate: {
          prompt: action.thought || "",
          bbox: pointToBbox(
            { x: point[0], y: point[1] },
            size.width,
            size.height
          )
        },
        param: action.thought || ""
      });
    } else if (action.action_type === "drag") {
      _utils.assert.call(void 0, action.action_inputs.start_box, "start_box is required");
      _utils.assert.call(void 0, action.action_inputs.end_box, "end_box is required");
      const startPoint = getPoint(action.action_inputs.start_box, size);
      const endPoint = getPoint(action.action_inputs.end_box, size);
      transformActions.push({
        type: "Drag",
        param: {
          start_box: { x: startPoint[0], y: startPoint[1] },
          end_box: { x: endPoint[0], y: endPoint[1] }
        },
        locate: null,
        thought: action.thought || ""
      });
    } else if (action.action_type === "type") {
      transformActions.push({
        type: "Input",
        param: {
          value: action.action_inputs.content
        },
        locate: null,
        thought: action.thought || ""
      });
    } else if (action.action_type === "scroll") {
      transformActions.push({
        type: "Scroll",
        param: {
          direction: action.action_inputs.direction
        },
        locate: null,
        thought: action.thought || ""
      });
    } else if (action.action_type === "finished") {
      transformActions.push({
        type: "Finished",
        param: {},
        locate: null,
        thought: action.thought || ""
      });
    } else if (action.action_type === "hotkey") {
      if (!action.action_inputs.key) {
        console.warn(
          "No key found in action: hotkey. Will not perform action."
        );
      } else {
        const keys = _keyboardlayout.transformHotkeyInput.call(void 0, action.action_inputs.key);
        transformActions.push({
          type: "KeyboardPress",
          param: {
            value: keys
          },
          locate: null,
          thought: action.thought || ""
        });
      }
    } else if (action.action_type === "wait") {
      transformActions.push({
        type: "Sleep",
        param: {
          timeMs: 1e3
        },
        locate: null,
        thought: action.thought || ""
      });
    } else if (action.action_type === "androidBackButton") {
      transformActions.push({
        type: "AndroidBackButton",
        param: {},
        locate: null,
        thought: action.thought || ""
      });
    } else if (action.action_type === "androidHomeButton") {
      transformActions.push({
        type: "AndroidHomeButton",
        param: {},
        locate: null,
        thought: action.thought || ""
      });
    } else if (action.action_type === "androidRecentAppsButton") {
      transformActions.push({
        type: "AndroidRecentAppsButton",
        param: {}
      });
    }
  });
  if (transformActions.length === 0) {
    throw new Error(`No actions found, response: ${res.content}`, {
      cause: {
        prediction: res.content,
        parsed
      }
    });
  }
  return {
    actions: transformActions,
    actionsFromModel: parsed,
    action_summary: getSummary(res.content),
    yamlFlow: buildYamlFlowFromPlans(transformActions)
  };
}
function convertBboxToCoordinates(text) {
  const pattern = /<bbox>(\d+)\s+(\d+)\s+(\d+)\s+(\d+)<\/bbox>/g;
  function replaceMatch(match, x1, y1, x2, y2) {
    const x1Num = Number.parseInt(x1, 10);
    const y1Num = Number.parseInt(y1, 10);
    const x2Num = Number.parseInt(x2, 10);
    const y2Num = Number.parseInt(y2, 10);
    const x = Math.floor((x1Num + x2Num) / 2);
    const y = Math.floor((y1Num + y2Num) / 2);
    return `(${x},${y})`;
  }
  const cleanedText = text.replace(/\[EOS\]/g, "");
  return cleanedText.replace(pattern, replaceMatch).trim();
}
function getPoint(startBox, size) {
  const [x, y] = JSON.parse(startBox);
  return [x * size.width, y * size.height];
}
async function resizeImageForUiTars(imageBase64, size) {
  if (_env.vlLocateMode.call(void 0, ) === "vlm-ui-tars" && _env.uiTarsModelVersion.call(void 0, ) === _env.UITarsModelVersion.V1_5) {
    debug("ui-tars-v1.5, will check image size", size);
    const currentPixels = size.width * size.height;
    const maxPixels = 16384 * 28 * 28;
    if (currentPixels > maxPixels) {
      const resizeFactor = Math.sqrt(maxPixels / currentPixels);
      const newWidth = Math.floor(size.width * resizeFactor);
      const newHeight = Math.floor(size.height * resizeFactor);
      debug(
        "resize image for ui-tars, new width: %s, new height: %s",
        newWidth,
        newHeight
      );
      const resizedImage = await _img.resizeImgBase64.call(void 0, imageBase64, {
        width: newWidth,
        height: newHeight
      });
      return resizedImage;
    }
  }
  return imageBase64;
}
















exports.systemPromptToLocateElement = systemPromptToLocateElement; exports.elementByPositionWithElementInfo = elementByPositionWithElementInfo; exports.describeUserPage = describeUserPage; exports.callToGetJSONObject = callToGetJSONObject; exports.callAiFn = callAiFn; exports.adaptBboxToRect = adaptBboxToRect; exports.expandSearchArea = expandSearchArea; exports.AiLocateElement = AiLocateElement; exports.AiLocateSection = AiLocateSection; exports.AiExtractElementInfo = AiExtractElementInfo; exports.AiAssert = AiAssert; exports.plan = plan; exports.vlmPlanning = vlmPlanning; exports.resizeImageForUiTars = resizeImageForUiTars;

//# sourceMappingURL=chunk-ZPVQ5TDQ.js.map