@aiscene/core 9.0.1 → 9.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/agent/agent.mjs +51 -1
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/tasks.mjs +8 -6
- package/dist/es/agent/tasks.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/ai-model/auto-glm/planning.mjs +2 -1
- package/dist/es/ai-model/auto-glm/planning.mjs.map +1 -1
- package/dist/es/ai-model/conversation-history.mjs +4 -0
- package/dist/es/ai-model/conversation-history.mjs.map +1 -1
- package/dist/es/ai-model/inspect.mjs +4 -3
- package/dist/es/ai-model/inspect.mjs.map +1 -1
- package/dist/es/ai-model/prompt/util.mjs +18 -1
- package/dist/es/ai-model/prompt/util.mjs.map +1 -1
- package/dist/es/ai-model/ui-tars-planning.mjs +6 -2
- package/dist/es/ai-model/ui-tars-planning.mjs.map +1 -1
- package/dist/es/index.mjs +2 -2
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/service/index.mjs +21 -1
- package/dist/es/service/index.mjs.map +1 -1
- package/dist/es/types.mjs +1 -20
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +2 -2
- package/dist/lib/agent/agent.js +51 -1
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/tasks.js +7 -5
- package/dist/lib/agent/tasks.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/ai-model/auto-glm/planning.js +2 -1
- package/dist/lib/ai-model/auto-glm/planning.js.map +1 -1
- package/dist/lib/ai-model/conversation-history.js +4 -0
- package/dist/lib/ai-model/conversation-history.js.map +1 -1
- package/dist/lib/ai-model/inspect.js +4 -3
- package/dist/lib/ai-model/inspect.js.map +1 -1
- package/dist/lib/ai-model/prompt/util.js +26 -0
- package/dist/lib/ai-model/prompt/util.js.map +1 -1
- package/dist/lib/ai-model/ui-tars-planning.js +6 -2
- package/dist/lib/ai-model/ui-tars-planning.js.map +1 -1
- package/dist/lib/index.js +1 -7
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/service/index.js +21 -1
- package/dist/lib/service/index.js.map +1 -1
- package/dist/lib/types.js +8 -35
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/utils.js +2 -2
- package/dist/types/agent/agent.d.ts +12 -1
- package/dist/types/agent/index.d.ts +1 -1
- package/dist/types/agent/tasks.d.ts +2 -0
- package/dist/types/ai-model/conversation-history.d.ts +2 -1
- package/dist/types/ai-model/inspect.d.ts +3 -0
- package/dist/types/ai-model/prompt/util.d.ts +4 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/service/index.d.ts +3 -0
- package/dist/types/types.d.ts +21 -35
- package/dist/types/yaml.d.ts +3 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/prompt/util.mjs","sources":["../../../../src/ai-model/prompt/util.ts"],"sourcesContent":["import type { SubGoal, SubGoalStatus } from '@/types';\n\n/**\n * Extract content from an XML tag in a string, searching from the end.\n * This approach handles cases where models prepend thinking content (like <think>...</think>)\n * before the actual response tags, or when there are incomplete/nested tags.\n *\n * Strategy: Find the LAST closing tag, then search backwards for the nearest opening tag.\n * This ensures we get the last complete tag pair, even if there are incomplete tags before it.\n *\n * @param xmlString - The XML string to parse\n * @param tagName - The name of the tag to extract (case-insensitive)\n * @returns The trimmed content of the tag, or undefined if not found\n */\nexport function extractXMLTag(\n xmlString: string,\n tagName: string,\n): string | undefined {\n const lowerXmlString = xmlString.toLowerCase();\n const lowerTagName = tagName.toLowerCase();\n const closeTag = `</${lowerTagName}>`;\n const openTag = `<${lowerTagName}>`;\n\n // Find the last closing tag\n const lastCloseIndex = lowerXmlString.lastIndexOf(closeTag);\n if (lastCloseIndex === -1) {\n // Fallback: handle half-open tags like `<action-type>Input` without\n // matching close tag. Extract until the next XML tag boundary.\n const lastOpenIndex = lowerXmlString.lastIndexOf(openTag);\n if (lastOpenIndex === -1) {\n return undefined;\n }\n\n const contentStart = lastOpenIndex + openTag.length;\n const remaining = xmlString.substring(contentStart);\n const nextTagIndex = remaining.indexOf('<');\n const content =\n nextTagIndex === -1 ? remaining : remaining.substring(0, nextTagIndex);\n\n return content.trim();\n }\n\n // Search backwards from the closing tag to find the nearest opening tag\n const searchArea = lowerXmlString.substring(0, lastCloseIndex);\n const lastOpenIndex = searchArea.lastIndexOf(openTag);\n if (lastOpenIndex === -1) {\n return undefined;\n }\n\n // Extract content between the tags (use original string to preserve case)\n const contentStart = lastOpenIndex + openTag.length;\n const contentEnd = lastCloseIndex;\n const content = xmlString.substring(contentStart, contentEnd);\n\n return content.trim();\n}\n\n/**\n * Parse sub-goals from XML content\n * Handles both formats:\n * - <sub-goal index=\"1\" status=\"pending\">description</sub-goal>\n * - <sub-goal index=\"1\" status=\"finished\" />\n */\nexport function parseSubGoalsFromXML(xmlContent: string): SubGoal[] {\n const subGoals: SubGoal[] = [];\n\n // Match both self-closing and regular sub-goal tags\n const regex =\n /<sub-goal\\s+index=\"(\\d+)\"\\s+status=\"(pending|finished)\"(?:\\s*\\/>|>([\\s\\S]*?)<\\/sub-goal>)/gi;\n\n let match: RegExpExecArray | null;\n match = regex.exec(xmlContent);\n while (match !== null) {\n const index = Number.parseInt(match[1], 10);\n const status = match[2] as SubGoalStatus;\n const description = match[3]?.trim() || '';\n\n subGoals.push({ index, status, description });\n match = regex.exec(xmlContent);\n }\n\n return subGoals;\n}\n\n/**\n * Extract indexes of sub-goals marked as finished from <mark-sub-goal-done> content\n */\nexport function parseMarkFinishedIndexes(xmlContent: string): number[] {\n const indexes: number[] = [];\n\n // Match self-closing sub-goal tags with status=\"finished\"\n const regex = /<sub-goal\\s+index=\"(\\d+)\"\\s+status=\"finished\"\\s*\\/>/gi;\n\n let match: RegExpExecArray | null;\n match = regex.exec(xmlContent);\n while (match !== null) {\n indexes.push(Number.parseInt(match[1], 10));\n match = regex.exec(xmlContent);\n }\n\n return indexes;\n}\n\nexport const distanceThreshold = 16;\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"],"names":["extractXMLTag","xmlString","tagName","lowerXmlString","lowerTagName","closeTag","openTag","lastCloseIndex","lastOpenIndex","contentStart","remaining","nextTagIndex","content","searchArea","contentEnd","parseSubGoalsFromXML","xmlContent","subGoals","regex","match","index","Number","status","description","parseMarkFinishedIndexes","indexes","distanceThreshold","distance","point1","point2","Math"],"mappings":"AAcO,SAASA,cACdC,SAAiB,EACjBC,OAAe;IAEf,MAAMC,iBAAiBF,UAAU,WAAW;IAC5C,MAAMG,eAAeF,QAAQ,WAAW;IACxC,MAAMG,WAAW,CAAC,EAAE,EAAED,aAAa,CAAC,CAAC;IACrC,MAAME,UAAU,CAAC,CAAC,EAAEF,aAAa,CAAC,CAAC;IAGnC,MAAMG,iBAAiBJ,eAAe,WAAW,CAACE;IAClD,IAAIE,AAAmB,OAAnBA,gBAAuB;QAGzB,MAAMC,gBAAgBL,eAAe,WAAW,CAACG;QACjD,IAAIE,AAAkB,OAAlBA,eACF;QAGF,MAAMC,eAAeD,gBAAgBF,QAAQ,MAAM;QACnD,MAAMI,YAAYT,UAAU,SAAS,CAACQ;QACtC,MAAME,eAAeD,UAAU,OAAO,CAAC;QACvC,MAAME,UACJD,AAAiB,OAAjBA,eAAsBD,YAAYA,UAAU,SAAS,CAAC,GAAGC;QAE3D,OAAOC,QAAQ,IAAI;IACrB;IAGA,MAAMC,aAAaV,eAAe,SAAS,CAAC,GAAGI;IAC/C,MAAMC,gBAAgBK,WAAW,WAAW,CAACP;IAC7C,IAAIE,AAAkB,OAAlBA,eACF;IAIF,MAAMC,eAAeD,gBAAgBF,QAAQ,MAAM;IACnD,MAAMQ,aAAaP;IACnB,MAAMK,UAAUX,UAAU,SAAS,CAACQ,cAAcK;IAElD,OAAOF,QAAQ,IAAI;AACrB;AAQO,SAASG,qBAAqBC,UAAkB;IACrD,MAAMC,WAAsB,EAAE;IAG9B,MAAMC,QACJ;IAEF,IAAIC;IACJA,QAAQD,MAAM,IAAI,CAACF;IACnB,MAAOG,AAAU,SAAVA,MAAgB;QACrB,MAAMC,QAAQC,OAAO,QAAQ,CAACF,KAAK,CAAC,EAAE,EAAE;QACxC,MAAMG,SAASH,KAAK,CAAC,EAAE;QACvB,MAAMI,cAAcJ,KAAK,CAAC,EAAE,EAAE,UAAU;QAExCF,SAAS,IAAI,CAAC;YAAEG;YAAOE;YAAQC;QAAY;QAC3CJ,QAAQD,MAAM,IAAI,CAACF;IACrB;IAEA,OAAOC;AACT;AAKO,SAASO,yBAAyBR,UAAkB;IACzD,MAAMS,UAAoB,EAAE;IAG5B,MAAMP,QAAQ;IAEd,IAAIC;IACJA,QAAQD,MAAM,IAAI,CAACF;IACnB,MAAOG,AAAU,SAAVA,MAAgB;QACrBM,QAAQ,IAAI,CAACJ,OAAO,QAAQ,CAACF,KAAK,CAAC,EAAE,EAAE;QACvCA,QAAQD,MAAM,IAAI,CAACF;IACrB;IAEA,OAAOS;AACT;AAEO,MAAMC,oBAAoB;AAE1B,SAASC,SACdC,MAAgC,EAChCC,MAAgC;IAEhC,OAAOC,KAAK,IAAI,CAAEF,AAAAA,CAAAA,OAAO,CAAC,GAAGC,OAAO,CAAC,AAAD,KAAM,IAAKD,AAAAA,CAAAA,OAAO,CAAC,GAAGC,OAAO,CAAC,AAAD,KAAM;AACzE"}
|
|
1
|
+
{"version":3,"file":"ai-model/prompt/util.mjs","sources":["../../../../src/ai-model/prompt/util.ts"],"sourcesContent":["import type { AIMemory, SubGoal, SubGoalStatus } from '@/types';\n\n/**\n * Extract content from an XML tag in a string, searching from the end.\n * This approach handles cases where models prepend thinking content (like <think>...</think>)\n * before the actual response tags, or when there are incomplete/nested tags.\n *\n * Strategy: Find the LAST closing tag, then search backwards for the nearest opening tag.\n * This ensures we get the last complete tag pair, even if there are incomplete tags before it.\n *\n * @param xmlString - The XML string to parse\n * @param tagName - The name of the tag to extract (case-insensitive)\n * @returns The trimmed content of the tag, or undefined if not found\n */\nexport function extractXMLTag(\n xmlString: string,\n tagName: string,\n): string | undefined {\n const lowerXmlString = xmlString.toLowerCase();\n const lowerTagName = tagName.toLowerCase();\n const closeTag = `</${lowerTagName}>`;\n const openTag = `<${lowerTagName}>`;\n\n // Find the last closing tag\n const lastCloseIndex = lowerXmlString.lastIndexOf(closeTag);\n if (lastCloseIndex === -1) {\n // Fallback: handle half-open tags like `<action-type>Input` without\n // matching close tag. Extract until the next XML tag boundary.\n const lastOpenIndex = lowerXmlString.lastIndexOf(openTag);\n if (lastOpenIndex === -1) {\n return undefined;\n }\n\n const contentStart = lastOpenIndex + openTag.length;\n const remaining = xmlString.substring(contentStart);\n const nextTagIndex = remaining.indexOf('<');\n const content =\n nextTagIndex === -1 ? remaining : remaining.substring(0, nextTagIndex);\n\n return content.trim();\n }\n\n // Search backwards from the closing tag to find the nearest opening tag\n const searchArea = lowerXmlString.substring(0, lastCloseIndex);\n const lastOpenIndex = searchArea.lastIndexOf(openTag);\n if (lastOpenIndex === -1) {\n return undefined;\n }\n\n // Extract content between the tags (use original string to preserve case)\n const contentStart = lastOpenIndex + openTag.length;\n const contentEnd = lastCloseIndex;\n const content = xmlString.substring(contentStart, contentEnd);\n\n return content.trim();\n}\n\n/**\n * Parse sub-goals from XML content\n * Handles both formats:\n * - <sub-goal index=\"1\" status=\"pending\">description</sub-goal>\n * - <sub-goal index=\"1\" status=\"finished\" />\n */\nexport function parseSubGoalsFromXML(xmlContent: string): SubGoal[] {\n const subGoals: SubGoal[] = [];\n\n // Match both self-closing and regular sub-goal tags\n const regex =\n /<sub-goal\\s+index=\"(\\d+)\"\\s+status=\"(pending|finished)\"(?:\\s*\\/>|>([\\s\\S]*?)<\\/sub-goal>)/gi;\n\n let match: RegExpExecArray | null;\n match = regex.exec(xmlContent);\n while (match !== null) {\n const index = Number.parseInt(match[1], 10);\n const status = match[2] as SubGoalStatus;\n const description = match[3]?.trim() || '';\n\n subGoals.push({ index, status, description });\n match = regex.exec(xmlContent);\n }\n\n return subGoals;\n}\n\n/**\n * Extract indexes of sub-goals marked as finished from <mark-sub-goal-done> content\n */\nexport function parseMarkFinishedIndexes(xmlContent: string): number[] {\n const indexes: number[] = [];\n\n // Match self-closing sub-goal tags with status=\"finished\"\n const regex = /<sub-goal\\s+index=\"(\\d+)\"\\s+status=\"finished\"\\s*\\/>/gi;\n\n let match: RegExpExecArray | null;\n match = regex.exec(xmlContent);\n while (match !== null) {\n indexes.push(Number.parseInt(match[1], 10));\n match = regex.exec(xmlContent);\n }\n\n return indexes;\n}\n\nexport function normalizeAIMemory(memory?: AIMemory): string[] {\n const memoryList = Array.isArray(memory)\n ? memory\n : memory !== undefined\n ? [memory]\n : [];\n\n return memoryList\n .map((item) => item.trim())\n .filter((item) => item.length > 0);\n}\n\nexport function formatAIMemories(memories?: readonly string[]): string {\n if (!memories?.length) {\n return '';\n }\n\n return `User-provided memories:\n---\n${memories.join('\\n---\\n')}\n`;\n}\n\nexport function appendAIMemoryToText(\n text: string,\n memories?: readonly string[],\n): string {\n const memoryText = formatAIMemories(memories);\n return memoryText ? `${memoryText}\\n${text}` : text;\n}\n\nexport const distanceThreshold = 16;\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"],"names":["extractXMLTag","xmlString","tagName","lowerXmlString","lowerTagName","closeTag","openTag","lastCloseIndex","lastOpenIndex","contentStart","remaining","nextTagIndex","content","searchArea","contentEnd","parseSubGoalsFromXML","xmlContent","subGoals","regex","match","index","Number","status","description","parseMarkFinishedIndexes","indexes","normalizeAIMemory","memory","memoryList","Array","undefined","item","formatAIMemories","memories","appendAIMemoryToText","text","memoryText","distanceThreshold","distance","point1","point2","Math"],"mappings":"AAcO,SAASA,cACdC,SAAiB,EACjBC,OAAe;IAEf,MAAMC,iBAAiBF,UAAU,WAAW;IAC5C,MAAMG,eAAeF,QAAQ,WAAW;IACxC,MAAMG,WAAW,CAAC,EAAE,EAAED,aAAa,CAAC,CAAC;IACrC,MAAME,UAAU,CAAC,CAAC,EAAEF,aAAa,CAAC,CAAC;IAGnC,MAAMG,iBAAiBJ,eAAe,WAAW,CAACE;IAClD,IAAIE,AAAmB,OAAnBA,gBAAuB;QAGzB,MAAMC,gBAAgBL,eAAe,WAAW,CAACG;QACjD,IAAIE,AAAkB,OAAlBA,eACF;QAGF,MAAMC,eAAeD,gBAAgBF,QAAQ,MAAM;QACnD,MAAMI,YAAYT,UAAU,SAAS,CAACQ;QACtC,MAAME,eAAeD,UAAU,OAAO,CAAC;QACvC,MAAME,UACJD,AAAiB,OAAjBA,eAAsBD,YAAYA,UAAU,SAAS,CAAC,GAAGC;QAE3D,OAAOC,QAAQ,IAAI;IACrB;IAGA,MAAMC,aAAaV,eAAe,SAAS,CAAC,GAAGI;IAC/C,MAAMC,gBAAgBK,WAAW,WAAW,CAACP;IAC7C,IAAIE,AAAkB,OAAlBA,eACF;IAIF,MAAMC,eAAeD,gBAAgBF,QAAQ,MAAM;IACnD,MAAMQ,aAAaP;IACnB,MAAMK,UAAUX,UAAU,SAAS,CAACQ,cAAcK;IAElD,OAAOF,QAAQ,IAAI;AACrB;AAQO,SAASG,qBAAqBC,UAAkB;IACrD,MAAMC,WAAsB,EAAE;IAG9B,MAAMC,QACJ;IAEF,IAAIC;IACJA,QAAQD,MAAM,IAAI,CAACF;IACnB,MAAOG,AAAU,SAAVA,MAAgB;QACrB,MAAMC,QAAQC,OAAO,QAAQ,CAACF,KAAK,CAAC,EAAE,EAAE;QACxC,MAAMG,SAASH,KAAK,CAAC,EAAE;QACvB,MAAMI,cAAcJ,KAAK,CAAC,EAAE,EAAE,UAAU;QAExCF,SAAS,IAAI,CAAC;YAAEG;YAAOE;YAAQC;QAAY;QAC3CJ,QAAQD,MAAM,IAAI,CAACF;IACrB;IAEA,OAAOC;AACT;AAKO,SAASO,yBAAyBR,UAAkB;IACzD,MAAMS,UAAoB,EAAE;IAG5B,MAAMP,QAAQ;IAEd,IAAIC;IACJA,QAAQD,MAAM,IAAI,CAACF;IACnB,MAAOG,AAAU,SAAVA,MAAgB;QACrBM,QAAQ,IAAI,CAACJ,OAAO,QAAQ,CAACF,KAAK,CAAC,EAAE,EAAE;QACvCA,QAAQD,MAAM,IAAI,CAACF;IACrB;IAEA,OAAOS;AACT;AAEO,SAASC,kBAAkBC,MAAiB;IACjD,MAAMC,aAAaC,MAAM,OAAO,CAACF,UAC7BA,SACAA,AAAWG,WAAXH,SACE;QAACA;KAAO,GACR,EAAE;IAER,OAAOC,WACJ,GAAG,CAAC,CAACG,OAASA,KAAK,IAAI,IACvB,MAAM,CAAC,CAACA,OAASA,KAAK,MAAM,GAAG;AACpC;AAEO,SAASC,iBAAiBC,QAA4B;IAC3D,IAAI,CAACA,UAAU,QACb,OAAO;IAGT,OAAO,CAAC;;AAEV,EAAEA,SAAS,IAAI,CAAC,WAAW;AAC3B,CAAC;AACD;AAEO,SAASC,qBACdC,IAAY,EACZF,QAA4B;IAE5B,MAAMG,aAAaJ,iBAAiBC;IACpC,OAAOG,aAAa,GAAGA,WAAW,EAAE,EAAED,MAAM,GAAGA;AACjD;AAEO,MAAME,oBAAoB;AAE1B,SAASC,SACdC,MAAgC,EAChCC,MAAgC;IAEhC,OAAOC,KAAK,IAAI,CAAEF,AAAAA,CAAAA,OAAO,CAAC,GAAGC,OAAO,CAAC,AAAD,KAAM,IAAKD,AAAAA,CAAAA,OAAO,CAAC,GAAGC,OAAO,CAAC,AAAD,KAAM;AACzE"}
|
|
@@ -18,8 +18,12 @@ const pointToBbox = (point, width, height)=>[
|
|
|
18
18
|
async function uiTarsPlanning(userInstruction, options) {
|
|
19
19
|
const { conversationHistory, context, modelConfig, actionContext } = options;
|
|
20
20
|
const { uiTarsModelVersion } = modelConfig;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const memoriesText = conversationHistory.memoriesToText();
|
|
22
|
+
const instructionBody = actionContext ? [
|
|
23
|
+
`<high_priority_knowledge>${actionContext}</high_priority_knowledge>`,
|
|
24
|
+
`<user_instruction>${userInstruction}</user_instruction>`
|
|
25
|
+
].join('\n') : userInstruction;
|
|
26
|
+
const instruction = memoriesText ? `${memoriesText}\n${instructionBody}` : instructionBody;
|
|
23
27
|
const systemPrompt = getUiTarsPlanningPrompt() + instruction;
|
|
24
28
|
const screenshotBase64 = context.screenshot.base64;
|
|
25
29
|
conversationHistory.append({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/ui-tars-planning.mjs","sources":["../../../src/ai-model/ui-tars-planning.ts"],"sourcesContent":["import type {\n PlanningAIResponse,\n PlanningAction,\n Size,\n UIContext,\n} from '@/types';\nimport { type IModelConfig, UITarsModelVersion } from '@aiscene/shared/env';\nimport { getDebug } from '@aiscene/shared/logger';\nimport { transformHotkeyInput } from '@aiscene/shared/us-keyboard-layout';\nimport { assert } from '@aiscene/shared/utils';\nimport { actionParser } from '@ui-tars/action-parser';\nimport type { ConversationHistory } from './conversation-history';\nimport { getSummary, getUiTarsPlanningPrompt } from './prompt/ui-tars-planning';\nimport {\n AIResponseParseError,\n callAIWithStringResponse,\n} from './service-caller/index';\n\ntype ActionType =\n | 'click'\n | 'left_double'\n | 'right_single'\n | 'drag'\n | 'type'\n | 'hotkey'\n | 'finished'\n | 'scroll'\n | 'wait';\n\nconst debug = getDebug('ui-tars-planning');\nconst warnLog = getDebug('ui-tars-planning', { console: true });\nconst bboxSize = 10;\nconst pointToBbox = (\n point: { x: number; y: number },\n width: number,\n height: number,\n): [number, number, number, number] => {\n return [\n Math.round(Math.max(point.x - bboxSize / 2, 0)),\n Math.round(Math.max(point.y - bboxSize / 2, 0)),\n Math.round(Math.min(point.x + bboxSize / 2, width)),\n Math.round(Math.min(point.y + bboxSize / 2, height)),\n ];\n};\n\nexport async function uiTarsPlanning(\n userInstruction: string,\n options: {\n conversationHistory: ConversationHistory;\n context: UIContext;\n modelConfig: IModelConfig;\n actionContext?: string;\n abortSignal?: AbortSignal;\n },\n): Promise<PlanningAIResponse> {\n const { conversationHistory, context, modelConfig, actionContext } = options;\n const { uiTarsModelVersion } = modelConfig;\n\n let instruction = userInstruction;\n if (actionContext) {\n instruction = `<high_priority_knowledge>${actionContext}</high_priority_knowledge>\\n<user_instruction>${userInstruction}</user_instruction>`;\n }\n\n const systemPrompt = getUiTarsPlanningPrompt() + instruction;\n\n const screenshotBase64 = context.screenshot.base64;\n\n conversationHistory.append({\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: screenshotBase64,\n },\n },\n ],\n });\n\n const res = await callAIWithStringResponse(\n [\n {\n role: 'user',\n content: systemPrompt,\n },\n ...conversationHistory.snapshot(),\n ],\n modelConfig,\n {\n abortSignal: options.abortSignal,\n },\n );\n\n let convertedText: string;\n let parsed: ReturnType<typeof actionParser>['parsed'];\n\n try {\n convertedText = convertBboxToCoordinates(res.content);\n\n const { shotSize } = context;\n const parseResult = actionParser({\n prediction: convertedText,\n factor: [1000, 1000],\n screenContext: {\n width: shotSize.width,\n height: shotSize.height,\n },\n modelVer: uiTarsModelVersion,\n });\n parsed = parseResult.parsed;\n } catch (parseError) {\n // Throw AIResponseParseError with usage and rawResponse preserved\n const errorMessage =\n parseError instanceof Error ? parseError.message : String(parseError);\n throw new AIResponseParseError(\n `Parse error: ${errorMessage}`,\n JSON.stringify(res.content, undefined, 2),\n res.usage,\n );\n }\n\n const { shotSize } = context;\n\n debug(\n 'ui-tars modelVer',\n uiTarsModelVersion,\n ', parsed',\n JSON.stringify(parsed),\n );\n\n const transformActions: PlanningAction[] = [];\n const unhandledActions: Array<{ type: string; thought: string }> = [];\n let shouldContinue = true;\n parsed.forEach((action) => {\n const actionType = (action.action_type || '').toLowerCase();\n if (actionType === 'click') {\n assert(action.action_inputs.start_box, 'start_box is required');\n const point = getPoint(action.action_inputs.start_box, shotSize);\n\n const locate = {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: point[0], y: point[1] },\n shotSize.width,\n shotSize.height,\n ),\n };\n\n transformActions.push({\n type: 'Tap',\n param: {\n locate: locate,\n },\n });\n } else if (actionType === 'left_double') {\n assert(action.action_inputs.start_box, 'start_box is required');\n const point = getPoint(action.action_inputs.start_box, shotSize);\n\n const locate = {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: point[0], y: point[1] },\n shotSize.width,\n shotSize.height,\n ),\n };\n\n transformActions.push({\n type: 'DoubleClick',\n param: {\n locate: locate,\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'right_single') {\n assert(action.action_inputs.start_box, 'start_box is required');\n const point = getPoint(action.action_inputs.start_box, shotSize);\n\n const locate = {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: point[0], y: point[1] },\n shotSize.width,\n shotSize.height,\n ),\n };\n\n transformActions.push({\n type: 'RightClick',\n param: {\n locate: locate,\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'drag') {\n assert(action.action_inputs.start_box, 'start_box is required');\n assert(action.action_inputs.end_box, 'end_box is required');\n const startPoint = getPoint(action.action_inputs.start_box, shotSize);\n const endPoint = getPoint(action.action_inputs.end_box, shotSize);\n transformActions.push({\n type: 'DragAndDrop',\n param: {\n from: {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: startPoint[0], y: startPoint[1] },\n shotSize.width,\n shotSize.height,\n ),\n },\n to: {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: endPoint[0], y: endPoint[1] },\n shotSize.width,\n shotSize.height,\n ),\n },\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'type') {\n transformActions.push({\n type: 'Input',\n param: {\n value: action.action_inputs.content,\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'scroll') {\n transformActions.push({\n type: 'Scroll',\n param: {\n direction: action.action_inputs.direction,\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'finished') {\n shouldContinue = false;\n transformActions.push({\n type: 'Finished',\n param: {},\n thought: action.thought || '',\n });\n } else if (actionType === 'hotkey') {\n if (!action.action_inputs.key) {\n warnLog('No key found in action: hotkey. Will not perform action.');\n } else {\n const keys = transformHotkeyInput(action.action_inputs.key);\n\n transformActions.push({\n type: 'KeyboardPress',\n param: {\n keyName: keys.join('+'),\n },\n thought: action.thought || '',\n });\n }\n } else if (actionType === 'wait') {\n transformActions.push({\n type: 'Sleep',\n param: {\n timeMs: 1000,\n },\n thought: action.thought || '',\n });\n } else if (actionType) {\n // Track unhandled action types\n unhandledActions.push({\n type: actionType,\n thought: action.thought || '',\n });\n debug('Unhandled action type:', actionType, 'thought:', action.thought);\n }\n });\n\n if (transformActions.length === 0) {\n const errorDetails: string[] = [];\n\n // Check if parsing failed\n if (parsed.length === 0) {\n errorDetails.push('Action parser returned no actions');\n\n // Check if response has Thought but no Action\n if (\n res.content.includes('Thought:') &&\n !res.content.includes('Action:')\n ) {\n errorDetails.push(\n 'Response contains \"Thought:\" but missing \"Action:\" line',\n );\n } else {\n errorDetails.push('Response may be malformed or empty');\n }\n }\n\n // Check if we have unhandled action types\n if (unhandledActions.length > 0) {\n const types = unhandledActions.map((a) => a.type).join(', ');\n errorDetails.push(`Unhandled action types: ${types}`);\n }\n\n const errorMessage = [\n 'No actions found in UI-TARS response.',\n ...errorDetails,\n ].join('\\n');\n\n // Throw AIResponseParseError with usage and rawResponse preserved\n throw new AIResponseParseError(\n errorMessage,\n JSON.stringify(res.content, undefined, 2),\n res.usage,\n );\n }\n\n debug('transformActions', JSON.stringify(transformActions, null, 2));\n const log = getSummary(res.content);\n\n conversationHistory.append({\n role: 'assistant',\n content: log,\n });\n\n return {\n actions: transformActions,\n log,\n usage: res.usage,\n rawResponse: JSON.stringify(res.content, undefined, 2),\n shouldContinuePlanning: shouldContinue,\n };\n}\n\n/**\n * Converts bounding box notation to coordinate points\n * @param text - The text containing bbox tags to be converted\n * @returns The text with bbox tags replaced by coordinate points\n */\nfunction convertBboxToCoordinates(text: string): string {\n // Match the four numbers after <bbox>\n const pattern = /<bbox>(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)<\\/bbox>/g;\n\n function replaceMatch(\n match: string,\n x1: string,\n y1: string,\n x2: string,\n y2: string,\n ): string {\n // Convert strings to numbers and calculate center point\n const x1Num = Number.parseInt(x1, 10);\n const y1Num = Number.parseInt(y1, 10);\n const x2Num = Number.parseInt(x2, 10);\n const y2Num = Number.parseInt(y2, 10);\n\n // Use Math.floor to truncate and calculate center point\n const x = Math.floor((x1Num + x2Num) / 2);\n const y = Math.floor((y1Num + y2Num) / 2);\n\n // Return formatted coordinate string\n return `(${x},${y})`;\n }\n\n // Remove [EOS] and replace <bbox> coordinates\n const cleanedText = text.replace(/\\[EOS\\]/g, '');\n return cleanedText.replace(pattern, replaceMatch).trim();\n}\n\nfunction getPoint(startBox: string, size: { width: number; height: number }) {\n const [x, y] = JSON.parse(startBox);\n return [x * size.width, y * size.height];\n}\n\ninterface BaseAction {\n action_type: ActionType;\n action_inputs: Record<string, any>;\n reflection: string | null;\n thought: string | null;\n}\n\ninterface ClickAction extends BaseAction {\n action_type: 'click';\n action_inputs: {\n start_box: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface DragAction extends BaseAction {\n action_type: 'drag';\n action_inputs: {\n start_box: string; // JSON string of [x, y] coordinates\n end_box: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface WaitAction extends BaseAction {\n action_type: 'wait';\n action_inputs: {\n time: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface LeftDoubleAction extends BaseAction {\n action_type: 'left_double';\n action_inputs: {\n start_box: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface RightSingleAction extends BaseAction {\n action_type: 'right_single';\n action_inputs: {\n start_box: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface TypeAction extends BaseAction {\n action_type: 'type';\n action_inputs: {\n content: string;\n };\n}\n\ninterface HotkeyAction extends BaseAction {\n action_type: 'hotkey';\n action_inputs: {\n key: string;\n };\n}\n\ninterface ScrollAction extends BaseAction {\n action_type: 'scroll';\n action_inputs: {\n direction: 'up' | 'down';\n };\n}\n\ninterface FinishedAction extends BaseAction {\n action_type: 'finished';\n action_inputs: Record<string, never>;\n}\n\nexport type Action =\n | ClickAction\n | LeftDoubleAction\n | RightSingleAction\n | DragAction\n | TypeAction\n | HotkeyAction\n | ScrollAction\n | FinishedAction\n | WaitAction;\n"],"names":["debug","getDebug","warnLog","bboxSize","pointToBbox","point","width","height","Math","uiTarsPlanning","userInstruction","options","conversationHistory","context","modelConfig","actionContext","uiTarsModelVersion","instruction","systemPrompt","getUiTarsPlanningPrompt","screenshotBase64","res","callAIWithStringResponse","convertedText","parsed","convertBboxToCoordinates","shotSize","parseResult","actionParser","parseError","errorMessage","Error","String","AIResponseParseError","JSON","undefined","transformActions","unhandledActions","shouldContinue","action","actionType","assert","getPoint","locate","startPoint","endPoint","keys","transformHotkeyInput","errorDetails","types","a","log","getSummary","text","pattern","replaceMatch","match","x1","y1","x2","y2","x1Num","Number","y1Num","x2Num","y2Num","x","y","cleanedText","startBox","size"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-model/ui-tars-planning.mjs","sources":["../../../src/ai-model/ui-tars-planning.ts"],"sourcesContent":["import type {\n PlanningAIResponse,\n PlanningAction,\n Size,\n UIContext,\n} from '@/types';\nimport { type IModelConfig, UITarsModelVersion } from '@aiscene/shared/env';\nimport { getDebug } from '@aiscene/shared/logger';\nimport { transformHotkeyInput } from '@aiscene/shared/us-keyboard-layout';\nimport { assert } from '@aiscene/shared/utils';\nimport { actionParser } from '@ui-tars/action-parser';\nimport type { ConversationHistory } from './conversation-history';\nimport { getSummary, getUiTarsPlanningPrompt } from './prompt/ui-tars-planning';\nimport {\n AIResponseParseError,\n callAIWithStringResponse,\n} from './service-caller/index';\n\ntype ActionType =\n | 'click'\n | 'left_double'\n | 'right_single'\n | 'drag'\n | 'type'\n | 'hotkey'\n | 'finished'\n | 'scroll'\n | 'wait';\n\nconst debug = getDebug('ui-tars-planning');\nconst warnLog = getDebug('ui-tars-planning', { console: true });\nconst bboxSize = 10;\nconst pointToBbox = (\n point: { x: number; y: number },\n width: number,\n height: number,\n): [number, number, number, number] => {\n return [\n Math.round(Math.max(point.x - bboxSize / 2, 0)),\n Math.round(Math.max(point.y - bboxSize / 2, 0)),\n Math.round(Math.min(point.x + bboxSize / 2, width)),\n Math.round(Math.min(point.y + bboxSize / 2, height)),\n ];\n};\n\nexport async function uiTarsPlanning(\n userInstruction: string,\n options: {\n conversationHistory: ConversationHistory;\n context: UIContext;\n modelConfig: IModelConfig;\n actionContext?: string;\n abortSignal?: AbortSignal;\n },\n): Promise<PlanningAIResponse> {\n const { conversationHistory, context, modelConfig, actionContext } = options;\n const { uiTarsModelVersion } = modelConfig;\n\n const memoriesText = conversationHistory.memoriesToText();\n const instructionBody = actionContext\n ? [\n `<high_priority_knowledge>${actionContext}</high_priority_knowledge>`,\n `<user_instruction>${userInstruction}</user_instruction>`,\n ].join('\\n')\n : userInstruction;\n const instruction = memoriesText\n ? `${memoriesText}\\n${instructionBody}`\n : instructionBody;\n\n const systemPrompt = getUiTarsPlanningPrompt() + instruction;\n\n const screenshotBase64 = context.screenshot.base64;\n\n conversationHistory.append({\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: screenshotBase64,\n },\n },\n ],\n });\n\n const res = await callAIWithStringResponse(\n [\n {\n role: 'user',\n content: systemPrompt,\n },\n ...conversationHistory.snapshot(),\n ],\n modelConfig,\n {\n abortSignal: options.abortSignal,\n },\n );\n\n let convertedText: string;\n let parsed: ReturnType<typeof actionParser>['parsed'];\n\n try {\n convertedText = convertBboxToCoordinates(res.content);\n\n const { shotSize } = context;\n const parseResult = actionParser({\n prediction: convertedText,\n factor: [1000, 1000],\n screenContext: {\n width: shotSize.width,\n height: shotSize.height,\n },\n modelVer: uiTarsModelVersion,\n });\n parsed = parseResult.parsed;\n } catch (parseError) {\n // Throw AIResponseParseError with usage and rawResponse preserved\n const errorMessage =\n parseError instanceof Error ? parseError.message : String(parseError);\n throw new AIResponseParseError(\n `Parse error: ${errorMessage}`,\n JSON.stringify(res.content, undefined, 2),\n res.usage,\n );\n }\n\n const { shotSize } = context;\n\n debug(\n 'ui-tars modelVer',\n uiTarsModelVersion,\n ', parsed',\n JSON.stringify(parsed),\n );\n\n const transformActions: PlanningAction[] = [];\n const unhandledActions: Array<{ type: string; thought: string }> = [];\n let shouldContinue = true;\n parsed.forEach((action) => {\n const actionType = (action.action_type || '').toLowerCase();\n if (actionType === 'click') {\n assert(action.action_inputs.start_box, 'start_box is required');\n const point = getPoint(action.action_inputs.start_box, shotSize);\n\n const locate = {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: point[0], y: point[1] },\n shotSize.width,\n shotSize.height,\n ),\n };\n\n transformActions.push({\n type: 'Tap',\n param: {\n locate: locate,\n },\n });\n } else if (actionType === 'left_double') {\n assert(action.action_inputs.start_box, 'start_box is required');\n const point = getPoint(action.action_inputs.start_box, shotSize);\n\n const locate = {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: point[0], y: point[1] },\n shotSize.width,\n shotSize.height,\n ),\n };\n\n transformActions.push({\n type: 'DoubleClick',\n param: {\n locate: locate,\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'right_single') {\n assert(action.action_inputs.start_box, 'start_box is required');\n const point = getPoint(action.action_inputs.start_box, shotSize);\n\n const locate = {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: point[0], y: point[1] },\n shotSize.width,\n shotSize.height,\n ),\n };\n\n transformActions.push({\n type: 'RightClick',\n param: {\n locate: locate,\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'drag') {\n assert(action.action_inputs.start_box, 'start_box is required');\n assert(action.action_inputs.end_box, 'end_box is required');\n const startPoint = getPoint(action.action_inputs.start_box, shotSize);\n const endPoint = getPoint(action.action_inputs.end_box, shotSize);\n transformActions.push({\n type: 'DragAndDrop',\n param: {\n from: {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: startPoint[0], y: startPoint[1] },\n shotSize.width,\n shotSize.height,\n ),\n },\n to: {\n prompt: action.thought || '',\n bbox: pointToBbox(\n { x: endPoint[0], y: endPoint[1] },\n shotSize.width,\n shotSize.height,\n ),\n },\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'type') {\n transformActions.push({\n type: 'Input',\n param: {\n value: action.action_inputs.content,\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'scroll') {\n transformActions.push({\n type: 'Scroll',\n param: {\n direction: action.action_inputs.direction,\n },\n thought: action.thought || '',\n });\n } else if (actionType === 'finished') {\n shouldContinue = false;\n transformActions.push({\n type: 'Finished',\n param: {},\n thought: action.thought || '',\n });\n } else if (actionType === 'hotkey') {\n if (!action.action_inputs.key) {\n warnLog('No key found in action: hotkey. Will not perform action.');\n } else {\n const keys = transformHotkeyInput(action.action_inputs.key);\n\n transformActions.push({\n type: 'KeyboardPress',\n param: {\n keyName: keys.join('+'),\n },\n thought: action.thought || '',\n });\n }\n } else if (actionType === 'wait') {\n transformActions.push({\n type: 'Sleep',\n param: {\n timeMs: 1000,\n },\n thought: action.thought || '',\n });\n } else if (actionType) {\n // Track unhandled action types\n unhandledActions.push({\n type: actionType,\n thought: action.thought || '',\n });\n debug('Unhandled action type:', actionType, 'thought:', action.thought);\n }\n });\n\n if (transformActions.length === 0) {\n const errorDetails: string[] = [];\n\n // Check if parsing failed\n if (parsed.length === 0) {\n errorDetails.push('Action parser returned no actions');\n\n // Check if response has Thought but no Action\n if (\n res.content.includes('Thought:') &&\n !res.content.includes('Action:')\n ) {\n errorDetails.push(\n 'Response contains \"Thought:\" but missing \"Action:\" line',\n );\n } else {\n errorDetails.push('Response may be malformed or empty');\n }\n }\n\n // Check if we have unhandled action types\n if (unhandledActions.length > 0) {\n const types = unhandledActions.map((a) => a.type).join(', ');\n errorDetails.push(`Unhandled action types: ${types}`);\n }\n\n const errorMessage = [\n 'No actions found in UI-TARS response.',\n ...errorDetails,\n ].join('\\n');\n\n // Throw AIResponseParseError with usage and rawResponse preserved\n throw new AIResponseParseError(\n errorMessage,\n JSON.stringify(res.content, undefined, 2),\n res.usage,\n );\n }\n\n debug('transformActions', JSON.stringify(transformActions, null, 2));\n const log = getSummary(res.content);\n\n conversationHistory.append({\n role: 'assistant',\n content: log,\n });\n\n return {\n actions: transformActions,\n log,\n usage: res.usage,\n rawResponse: JSON.stringify(res.content, undefined, 2),\n shouldContinuePlanning: shouldContinue,\n };\n}\n\n/**\n * Converts bounding box notation to coordinate points\n * @param text - The text containing bbox tags to be converted\n * @returns The text with bbox tags replaced by coordinate points\n */\nfunction convertBboxToCoordinates(text: string): string {\n // Match the four numbers after <bbox>\n const pattern = /<bbox>(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)<\\/bbox>/g;\n\n function replaceMatch(\n match: string,\n x1: string,\n y1: string,\n x2: string,\n y2: string,\n ): string {\n // Convert strings to numbers and calculate center point\n const x1Num = Number.parseInt(x1, 10);\n const y1Num = Number.parseInt(y1, 10);\n const x2Num = Number.parseInt(x2, 10);\n const y2Num = Number.parseInt(y2, 10);\n\n // Use Math.floor to truncate and calculate center point\n const x = Math.floor((x1Num + x2Num) / 2);\n const y = Math.floor((y1Num + y2Num) / 2);\n\n // Return formatted coordinate string\n return `(${x},${y})`;\n }\n\n // Remove [EOS] and replace <bbox> coordinates\n const cleanedText = text.replace(/\\[EOS\\]/g, '');\n return cleanedText.replace(pattern, replaceMatch).trim();\n}\n\nfunction getPoint(startBox: string, size: { width: number; height: number }) {\n const [x, y] = JSON.parse(startBox);\n return [x * size.width, y * size.height];\n}\n\ninterface BaseAction {\n action_type: ActionType;\n action_inputs: Record<string, any>;\n reflection: string | null;\n thought: string | null;\n}\n\ninterface ClickAction extends BaseAction {\n action_type: 'click';\n action_inputs: {\n start_box: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface DragAction extends BaseAction {\n action_type: 'drag';\n action_inputs: {\n start_box: string; // JSON string of [x, y] coordinates\n end_box: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface WaitAction extends BaseAction {\n action_type: 'wait';\n action_inputs: {\n time: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface LeftDoubleAction extends BaseAction {\n action_type: 'left_double';\n action_inputs: {\n start_box: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface RightSingleAction extends BaseAction {\n action_type: 'right_single';\n action_inputs: {\n start_box: string; // JSON string of [x, y] coordinates\n };\n}\n\ninterface TypeAction extends BaseAction {\n action_type: 'type';\n action_inputs: {\n content: string;\n };\n}\n\ninterface HotkeyAction extends BaseAction {\n action_type: 'hotkey';\n action_inputs: {\n key: string;\n };\n}\n\ninterface ScrollAction extends BaseAction {\n action_type: 'scroll';\n action_inputs: {\n direction: 'up' | 'down';\n };\n}\n\ninterface FinishedAction extends BaseAction {\n action_type: 'finished';\n action_inputs: Record<string, never>;\n}\n\nexport type Action =\n | ClickAction\n | LeftDoubleAction\n | RightSingleAction\n | DragAction\n | TypeAction\n | HotkeyAction\n | ScrollAction\n | FinishedAction\n | WaitAction;\n"],"names":["debug","getDebug","warnLog","bboxSize","pointToBbox","point","width","height","Math","uiTarsPlanning","userInstruction","options","conversationHistory","context","modelConfig","actionContext","uiTarsModelVersion","memoriesText","instructionBody","instruction","systemPrompt","getUiTarsPlanningPrompt","screenshotBase64","res","callAIWithStringResponse","convertedText","parsed","convertBboxToCoordinates","shotSize","parseResult","actionParser","parseError","errorMessage","Error","String","AIResponseParseError","JSON","undefined","transformActions","unhandledActions","shouldContinue","action","actionType","assert","getPoint","locate","startPoint","endPoint","keys","transformHotkeyInput","errorDetails","types","a","log","getSummary","text","pattern","replaceMatch","match","x1","y1","x2","y2","x1Num","Number","y1Num","x2Num","y2Num","x","y","cleanedText","startBox","size"],"mappings":"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"}
|
package/dist/es/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { TaskRunner } from "./task-runner.mjs";
|
|
|
4
4
|
import { getVersion } from "./utils.mjs";
|
|
5
5
|
import { AiLocateElement, PointSchema, RectSchema, SizeSchema, TMultimodalPromptSchema, TUserPromptSchema, getMidsceneLocationSchema, plan, runConnectivityTest } from "./ai-model/index.mjs";
|
|
6
6
|
import { MIDSCENE_MODEL_NAME } from "@aiscene/shared/env";
|
|
7
|
-
import {
|
|
7
|
+
import { ExecutionDump, GroupedActionDump, ReportActionDump, ServiceError } from "./types.mjs";
|
|
8
8
|
import { Agent, createAgent } from "./agent/index.mjs";
|
|
9
9
|
import { escapeContent, generateDumpScriptTag, generateImageScriptTag, parseDumpScript, parseDumpScriptAttributes, parseImageScripts, restoreImageReferences, unescapeContent } from "./dump/index.mjs";
|
|
10
10
|
import { ReportGenerator, nullReportGenerator } from "./report-generator.mjs";
|
|
@@ -14,6 +14,6 @@ import { ScreenshotItem } from "./screenshot-item.mjs";
|
|
|
14
14
|
import { ScreenshotStore } from "./dump/screenshot-store.mjs";
|
|
15
15
|
import { executionToMarkdown, reportToMarkdown } from "./report-markdown.mjs";
|
|
16
16
|
const src = service;
|
|
17
|
-
export {
|
|
17
|
+
export { Agent, AiLocateElement, ExecutionDump, GroupedActionDump, MIDSCENE_MODEL_NAME, PointSchema, RectSchema, ReportActionDump, ReportGenerator, ReportMergingTool, ScreenshotItem, ScreenshotStore, service as Service, ServiceError, SizeSchema, TMultimodalPromptSchema, TUserPromptSchema, TaskRunner, collectDedupedExecutions, createAgent, createReportCliCommands, dedupeExecutionsKeepLatest, src as default, escapeContent, executionToMarkdown, generateDumpScriptTag, generateImageScriptTag, getMidsceneLocationSchema, getVersion, nullReportGenerator, parseDumpScript, parseDumpScriptAttributes, parseImageScripts, plan, reportFileToMarkdown, reportToMarkdown, restoreImageReferences, runConnectivityTest, splitReportFile, splitReportHtmlByExecution, unescapeContent, z };
|
|
18
18
|
|
|
19
19
|
//# sourceMappingURL=index.mjs.map
|
package/dist/es/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/index.ts"],"sourcesContent":["import { z } from 'zod';\nimport Service from './service/index';\nimport { TaskRunner } from './task-runner';\nimport { getVersion } from './utils';\n\nexport {\n plan,\n AiLocateElement,\n runConnectivityTest,\n getMidsceneLocationSchema,\n PointSchema,\n SizeSchema,\n RectSchema,\n TMultimodalPromptSchema,\n TUserPromptSchema,\n type TMultimodalPrompt,\n type TUserPrompt,\n type ConnectivityCheckResultItem,\n type ConnectivityTestConfig,\n type ConnectivityTestResult,\n} from './ai-model/index';\n\nexport {\n MIDSCENE_MODEL_NAME,\n type CreateOpenAIClientFn,\n} from '@aiscene/shared/env';\n\nexport type * from './types';\nexport {\n ServiceError,\n
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../src/index.ts"],"sourcesContent":["import { z } from 'zod';\nimport Service from './service/index';\nimport { TaskRunner } from './task-runner';\nimport { getVersion } from './utils';\n\nexport {\n plan,\n AiLocateElement,\n runConnectivityTest,\n getMidsceneLocationSchema,\n PointSchema,\n SizeSchema,\n RectSchema,\n TMultimodalPromptSchema,\n TUserPromptSchema,\n type TMultimodalPrompt,\n type TUserPrompt,\n type ConnectivityCheckResultItem,\n type ConnectivityTestConfig,\n type ConnectivityTestResult,\n} from './ai-model/index';\n\nexport {\n MIDSCENE_MODEL_NAME,\n type CreateOpenAIClientFn,\n} from '@aiscene/shared/env';\n\nexport type * from './types';\nexport {\n ServiceError,\n ExecutionDump,\n ReportActionDump,\n GroupedActionDump,\n type IExecutionDump,\n type IReportActionDump,\n type IGroupedActionDump,\n type ReportMeta,\n type GroupMeta,\n} from './types';\n\nexport { z };\n\nexport default Service;\nexport { TaskRunner, Service, getVersion };\n\nexport type {\n MidsceneYamlScript,\n MidsceneYamlTask,\n MidsceneYamlFlowItem,\n MidsceneYamlConfigResult,\n MidsceneYamlConfig,\n MidsceneYamlScriptWebEnv,\n MidsceneYamlScriptAndroidEnv,\n MidsceneYamlScriptIOSEnv,\n MidsceneYamlScriptEnv,\n LocateOption,\n DetailedLocateParam,\n} from './yaml';\n\nexport { Agent, type AgentOpt, type AiActOptions, createAgent } from './agent';\n\n// Dump utilities\nexport {\n restoreImageReferences,\n escapeContent,\n unescapeContent,\n parseImageScripts,\n parseDumpScript,\n parseDumpScriptAttributes,\n generateImageScriptTag,\n generateDumpScriptTag,\n} from './dump';\n\n// Report generator\nexport type { IReportGenerator } from './report-generator';\nexport { ReportGenerator, nullReportGenerator } from './report-generator';\nexport {\n collectDedupedExecutions,\n ReportMergingTool,\n dedupeExecutionsKeepLatest,\n splitReportHtmlByExecution,\n} from './report';\nexport {\n createReportCliCommands,\n reportFileToMarkdown,\n splitReportFile,\n type ConsumeReportFileAction,\n type ReportFileToMarkdownOptions,\n type ReportCliCommandDefinition,\n type ReportCliCommandEntry,\n type SplitReportFileOptions,\n} from './report-cli';\n\n// ScreenshotItem\nexport { ScreenshotItem } from './screenshot-item';\nexport { ScreenshotStore, type ScreenshotRef } from './dump/screenshot-store';\n\nexport {\n executionToMarkdown,\n reportToMarkdown,\n type ExecutionMarkdownOptions,\n type ExecutionMarkdownResult,\n type ReportMarkdownResult,\n type MarkdownAttachment,\n} from './report-markdown';\n"],"names":["Service"],"mappings":";;;;;;;;;;;;;;;AA0CA,YAAeA"}
|
|
@@ -2,6 +2,7 @@ import { isAutoGLM } from "../ai-model/auto-glm/util.mjs";
|
|
|
2
2
|
import { AIResponseParseError, AiExtractElementInfo, AiLocateElement, callAIWithObjectResponse } from "../ai-model/index.mjs";
|
|
3
3
|
import { AiLocateSection, buildSearchAreaConfig } from "../ai-model/inspect.mjs";
|
|
4
4
|
import { elementDescriberInstruction } from "../ai-model/prompt/describe.mjs";
|
|
5
|
+
import { formatAIMemories, normalizeAIMemory } from "../ai-model/prompt/util.mjs";
|
|
5
6
|
import { expandSearchArea } from "../common.mjs";
|
|
6
7
|
import { ServiceError } from "../types.mjs";
|
|
7
8
|
import { compositeElementInfoImg, cropByRect } from "@aiscene/shared/img";
|
|
@@ -20,6 +21,11 @@ function _define_property(obj, key, value) {
|
|
|
20
21
|
}
|
|
21
22
|
const debug = getDebug('ai:service');
|
|
22
23
|
class Service {
|
|
24
|
+
async getAIMemory() {
|
|
25
|
+
const memories = await this.memoryRetrieverFn?.() || [];
|
|
26
|
+
const normalizedMemories = normalizeAIMemory(memories);
|
|
27
|
+
return normalizedMemories.length ? normalizedMemories : void 0;
|
|
28
|
+
}
|
|
23
29
|
async locate(query, opt, modelConfig, abortSignal) {
|
|
24
30
|
const queryPrompt = 'string' == typeof query ? query : query.prompt;
|
|
25
31
|
assert(queryPrompt, 'query is required for locate');
|
|
@@ -37,6 +43,7 @@ class Service {
|
|
|
37
43
|
searchAreaPrompt = void 0;
|
|
38
44
|
}
|
|
39
45
|
const context = opt?.context || await this.contextRetrieverFn();
|
|
46
|
+
const memories = await this.getAIMemory();
|
|
40
47
|
let searchArea;
|
|
41
48
|
let searchAreaRawResponse;
|
|
42
49
|
let searchAreaUsage;
|
|
@@ -63,6 +70,7 @@ class Service {
|
|
|
63
70
|
context,
|
|
64
71
|
sectionDescription: searchAreaPrompt,
|
|
65
72
|
modelConfig,
|
|
73
|
+
memories,
|
|
66
74
|
abortSignal
|
|
67
75
|
});
|
|
68
76
|
assert(searchAreaResponse.rect, `cannot find search area for "${searchAreaPrompt}"${searchAreaResponse.error ? `: ${searchAreaResponse.error}` : ''}`);
|
|
@@ -76,6 +84,7 @@ class Service {
|
|
|
76
84
|
targetElementDescription: queryPrompt,
|
|
77
85
|
searchConfig: searchAreaResponse,
|
|
78
86
|
modelConfig,
|
|
87
|
+
memories,
|
|
79
88
|
abortSignal
|
|
80
89
|
});
|
|
81
90
|
const timeCost = Date.now() - startTime;
|
|
@@ -141,7 +150,8 @@ class Service {
|
|
|
141
150
|
multimodalPrompt,
|
|
142
151
|
extractOption: opt,
|
|
143
152
|
modelConfig,
|
|
144
|
-
pageDescription
|
|
153
|
+
pageDescription,
|
|
154
|
+
memories: await this.getAIMemory()
|
|
145
155
|
});
|
|
146
156
|
parseResult = result.parseResult;
|
|
147
157
|
rawResponse = result.rawResponse;
|
|
@@ -236,6 +246,8 @@ class Service {
|
|
|
236
246
|
const croppedResult = await cropByRect(imagePayload, searchArea, 'qwen2.5-vl' === modelFamily);
|
|
237
247
|
imagePayload = croppedResult.imageBase64;
|
|
238
248
|
}
|
|
249
|
+
const memories = await this.getAIMemory();
|
|
250
|
+
const memoryText = formatAIMemories(memories);
|
|
239
251
|
const msgs = [
|
|
240
252
|
{
|
|
241
253
|
role: 'system',
|
|
@@ -244,6 +256,12 @@ class Service {
|
|
|
244
256
|
{
|
|
245
257
|
role: 'user',
|
|
246
258
|
content: [
|
|
259
|
+
...memoryText ? [
|
|
260
|
+
{
|
|
261
|
+
type: 'text',
|
|
262
|
+
text: memoryText
|
|
263
|
+
}
|
|
264
|
+
] : [],
|
|
247
265
|
{
|
|
248
266
|
type: 'image_url',
|
|
249
267
|
image_url: {
|
|
@@ -263,10 +281,12 @@ class Service {
|
|
|
263
281
|
constructor(context, opt){
|
|
264
282
|
_define_property(this, "contextRetrieverFn", void 0);
|
|
265
283
|
_define_property(this, "taskInfo", void 0);
|
|
284
|
+
_define_property(this, "memoryRetrieverFn", void 0);
|
|
266
285
|
assert(context, 'context is required for Service');
|
|
267
286
|
if ('function' == typeof context) this.contextRetrieverFn = context;
|
|
268
287
|
else this.contextRetrieverFn = ()=>Promise.resolve(context);
|
|
269
288
|
if (void 0 !== opt?.taskInfo) this.taskInfo = opt.taskInfo;
|
|
289
|
+
this.memoryRetrieverFn = opt?.memoryRetrieverFn;
|
|
270
290
|
}
|
|
271
291
|
}
|
|
272
292
|
export { Service as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service/index.mjs","sources":["../../../src/service/index.ts"],"sourcesContent":["import { isAutoGLM, isUITars } from '@/ai-model/auto-glm/util';\nimport {\n AIResponseParseError,\n AiExtractElementInfo,\n AiLocateElement,\n callAIWithObjectResponse,\n} from '@/ai-model/index';\nimport { AiLocateSection, buildSearchAreaConfig } from '@/ai-model/inspect';\nimport { elementDescriberInstruction } from '@/ai-model/prompt/describe';\nimport { type AIArgs, expandSearchArea } from '@/common';\nimport type {\n AIDescribeElementResponse,\n AIUsageInfo,\n DetailedLocateParam,\n LocateResultElement,\n LocateResultWithDump,\n PartialServiceDumpFromSDK,\n PlanningLocateParam,\n Rect,\n ServiceExtractOption,\n ServiceExtractParam,\n ServiceExtractResult,\n ServiceTaskInfo,\n UIContext,\n} from '@/types';\nimport { ServiceError } from '@/types';\nimport type { IModelConfig } from '@aiscene/shared/env';\nimport { compositeElementInfoImg, cropByRect } from '@aiscene/shared/img';\nimport { getDebug } from '@aiscene/shared/logger';\nimport { assert } from '@aiscene/shared/utils';\nimport type { TMultimodalPrompt } from '../common';\nimport { createServiceDump } from './utils';\n\nexport interface LocateOpts {\n context?: UIContext;\n planLocatedElement?: LocateResultElement;\n}\n\nexport type AnyValue<T> = {\n [K in keyof T]: unknown extends T[K] ? any : T[K];\n};\n\ninterface ServiceOptions {\n taskInfo?: Omit<ServiceTaskInfo, 'durationMs'>;\n}\n\nconst debug = getDebug('ai:service');\nexport default class Service {\n contextRetrieverFn: () => Promise<UIContext> | UIContext;\n\n taskInfo?: Omit<ServiceTaskInfo, 'durationMs'>;\n\n constructor(\n context: UIContext | (() => Promise<UIContext> | UIContext),\n opt?: ServiceOptions,\n ) {\n assert(context, 'context is required for Service');\n if (typeof context === 'function') {\n this.contextRetrieverFn = context;\n } else {\n this.contextRetrieverFn = () => Promise.resolve(context);\n }\n\n if (typeof opt?.taskInfo !== 'undefined') {\n this.taskInfo = opt.taskInfo;\n }\n }\n\n async locate(\n query: PlanningLocateParam,\n opt: LocateOpts,\n modelConfig: IModelConfig,\n abortSignal?: AbortSignal,\n ): Promise<LocateResultWithDump> {\n const queryPrompt = typeof query === 'string' ? query : query.prompt;\n assert(queryPrompt, 'query is required for locate');\n\n assert(typeof query === 'object', 'query should be an object for locate');\n\n const hasPlanLocatedElement = !!opt?.planLocatedElement?.rect;\n\n let searchAreaPrompt;\n if (query.deepLocate && !hasPlanLocatedElement) {\n searchAreaPrompt = query.prompt;\n }\n\n const { modelFamily } = modelConfig;\n\n if (searchAreaPrompt && !modelFamily) {\n console.warn(\n 'The \"deepLocate\" feature is not supported with multimodal LLM. Please config VL model for Midscene. https://midscenejs.com/model-config',\n );\n searchAreaPrompt = undefined;\n }\n\n if (searchAreaPrompt && isAutoGLM(modelFamily)) {\n console.warn('The \"deepLocate\" feature is not supported with AutoGLM.');\n searchAreaPrompt = undefined;\n }\n\n const context = opt?.context || (await this.contextRetrieverFn());\n\n let searchArea: Rect | undefined = undefined;\n let searchAreaRawResponse: string | undefined = undefined;\n let searchAreaUsage: AIUsageInfo | undefined = undefined;\n let searchAreaResponse:\n | Awaited<ReturnType<typeof AiLocateSection>>\n | undefined = undefined;\n if (query.deepLocate && hasPlanLocatedElement) {\n const searchAreaConfig = await buildSearchAreaConfig({\n context,\n baseRect: opt.planLocatedElement!.rect,\n modelFamily,\n });\n searchArea = searchAreaConfig.rect;\n\n searchAreaRawResponse = JSON.stringify({\n source: 'plan-located-element',\n rect: opt.planLocatedElement!.rect,\n });\n searchAreaResponse = {\n rect: searchArea,\n imageBase64: searchAreaConfig.imageBase64,\n scale: searchAreaConfig.scale,\n rawResponse: searchAreaRawResponse,\n };\n } else if (searchAreaPrompt) {\n searchAreaResponse = await AiLocateSection({\n context,\n sectionDescription: searchAreaPrompt,\n modelConfig,\n abortSignal,\n });\n assert(\n searchAreaResponse.rect,\n `cannot find search area for \"${searchAreaPrompt}\"${\n searchAreaResponse.error ? `: ${searchAreaResponse.error}` : ''\n }`,\n );\n searchAreaRawResponse = searchAreaResponse.rawResponse;\n searchAreaUsage = searchAreaResponse.usage;\n searchArea = searchAreaResponse.rect;\n }\n\n const startTime = Date.now();\n const { parseResult, rect, rawResponse, usage, reasoning_content } =\n await AiLocateElement({\n context,\n targetElementDescription: queryPrompt,\n searchConfig: searchAreaResponse,\n modelConfig,\n abortSignal,\n });\n\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse: JSON.stringify(rawResponse),\n formatResponse: JSON.stringify(parseResult),\n usage,\n searchArea,\n searchAreaRawResponse,\n searchAreaUsage,\n reasoning_content,\n };\n\n let errorLog: string | undefined;\n if (parseResult.errors?.length) {\n errorLog = `failed to locate element: \\n${parseResult.errors.join('\\n')}`;\n }\n\n const dumpData: PartialServiceDumpFromSDK = {\n type: 'locate',\n userQuery: {\n element: queryPrompt,\n },\n matchedElement: [],\n matchedRect: rect,\n data: null,\n taskInfo,\n deepLocate: !!searchArea,\n error: errorLog,\n };\n\n const elements = parseResult.elements || [];\n\n const dump = createServiceDump({\n ...dumpData,\n matchedElement: elements,\n });\n\n if (errorLog) {\n throw new ServiceError(errorLog, dump);\n }\n\n if (elements.length > 1) {\n throw new ServiceError(\n `locate: multiple elements found, length = ${elements.length}`,\n dump,\n );\n }\n\n if (elements.length === 1) {\n return {\n element: {\n center: elements[0]!.center,\n rect: elements[0]!.rect,\n description: elements[0]!.description,\n },\n rect,\n dump,\n };\n }\n\n return {\n element: null,\n rect,\n dump,\n };\n }\n\n async extract<T>(\n dataDemand: ServiceExtractParam,\n modelConfig: IModelConfig,\n opt?: ServiceExtractOption,\n pageDescription?: string,\n multimodalPrompt?: TMultimodalPrompt,\n context?: UIContext,\n ): Promise<ServiceExtractResult<T>> {\n assert(context, 'context is required for extract');\n assert(\n typeof dataDemand === 'object' || typeof dataDemand === 'string',\n `dataDemand should be object or string, but get ${typeof dataDemand}`,\n );\n\n const startTime = Date.now();\n\n let parseResult: Awaited<\n ReturnType<typeof AiExtractElementInfo<T>>\n >['parseResult'];\n let rawResponse: string;\n let usage: Awaited<ReturnType<typeof AiExtractElementInfo<T>>>['usage'];\n let reasoning_content: string | undefined;\n\n try {\n const result = await AiExtractElementInfo<T>({\n context,\n dataQuery: dataDemand,\n multimodalPrompt,\n extractOption: opt,\n modelConfig,\n pageDescription,\n });\n parseResult = result.parseResult;\n rawResponse = result.rawResponse;\n usage = result.usage;\n reasoning_content = result.reasoning_content;\n } catch (error) {\n if (error instanceof AIResponseParseError) {\n // Create dump with usage and rawResponse from the error\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse: error.rawResponse,\n usage: error.usage,\n };\n const dump = createServiceDump({\n type: 'extract',\n userQuery: { dataDemand },\n matchedElement: [],\n data: null,\n taskInfo,\n error: error.message,\n });\n throw new ServiceError(error.message, dump);\n }\n throw error;\n }\n\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse,\n formatResponse: JSON.stringify(parseResult),\n usage,\n reasoning_content,\n };\n\n let errorLog: string | undefined;\n if (parseResult.errors?.length) {\n errorLog = `AI response error: \\n${parseResult.errors.join('\\n')}`;\n }\n\n const dumpData: PartialServiceDumpFromSDK = {\n type: 'extract',\n userQuery: {\n dataDemand,\n },\n matchedElement: [],\n data: null,\n taskInfo,\n error: errorLog,\n };\n\n const { data, thought } = parseResult || {};\n\n const dump = createServiceDump({\n ...dumpData,\n data,\n });\n\n if (errorLog && !data) {\n throw new ServiceError(errorLog, dump);\n }\n\n return {\n data,\n thought,\n usage,\n reasoning_content,\n dump,\n };\n }\n\n async describe(\n target: Rect | [number, number],\n modelConfig: IModelConfig,\n opt?: {\n deepLocate?: boolean;\n },\n ): Promise<Pick<AIDescribeElementResponse, 'description'>> {\n assert(target, 'target is required for service.describe');\n const context = await this.contextRetrieverFn();\n const { shotSize } = context;\n const screenshotBase64 = context.screenshot.base64;\n assert(screenshotBase64, 'screenshot is required for service.describe');\n // The result of the \"describe\" function will be used for positioning, so essentially it is a form of grounding.\n const { modelFamily } = modelConfig;\n const systemPrompt = elementDescriberInstruction();\n\n // Convert [x,y] center point to Rect if needed\n const defaultRectSize = 30;\n const targetRect: Rect = Array.isArray(target)\n ? {\n left: Math.floor(target[0] - defaultRectSize / 2),\n top: Math.floor(target[1] - defaultRectSize / 2),\n width: defaultRectSize,\n height: defaultRectSize,\n }\n : target;\n\n let imagePayload = await compositeElementInfoImg({\n inputImgBase64: screenshotBase64,\n size: shotSize,\n elementsPositionInfo: [\n {\n rect: targetRect,\n },\n ],\n borderThickness: 3,\n });\n\n if (opt?.deepLocate) {\n const searchArea = expandSearchArea(targetRect, shotSize);\n // Always crop in describe mode. Unlike locate's deepLocate (where\n // cropping too small loses context for finding elements), describe's\n // deepLocate intentionally zooms in so the model produces a more\n // precise description from a focused view. expandSearchArea already\n // guarantees a minimum 400x400 area with surrounding context.\n debug('describe: cropping to searchArea', searchArea);\n const croppedResult = await cropByRect(\n imagePayload,\n searchArea,\n modelFamily === 'qwen2.5-vl',\n );\n imagePayload = croppedResult.imageBase64;\n }\n\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n ],\n },\n ];\n\n const res = await callAIWithObjectResponse<AIDescribeElementResponse>(\n msgs,\n modelConfig,\n );\n\n const { content } = res;\n assert(!content.error, `describe failed: ${content.error}`);\n assert(content.description, 'failed to describe the element');\n return content;\n }\n}\n"],"names":["debug","getDebug","Service","query","opt","modelConfig","abortSignal","queryPrompt","assert","hasPlanLocatedElement","searchAreaPrompt","modelFamily","console","undefined","isAutoGLM","context","searchArea","searchAreaRawResponse","searchAreaUsage","searchAreaResponse","searchAreaConfig","buildSearchAreaConfig","JSON","AiLocateSection","startTime","Date","parseResult","rect","rawResponse","usage","reasoning_content","AiLocateElement","timeCost","taskInfo","errorLog","dumpData","elements","dump","createServiceDump","ServiceError","dataDemand","pageDescription","multimodalPrompt","result","AiExtractElementInfo","error","AIResponseParseError","data","thought","target","shotSize","screenshotBase64","systemPrompt","elementDescriberInstruction","defaultRectSize","targetRect","Array","Math","imagePayload","compositeElementInfoImg","expandSearchArea","croppedResult","cropByRect","msgs","res","callAIWithObjectResponse","content","Promise"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service/index.mjs","sources":["../../../src/service/index.ts"],"sourcesContent":["import { isAutoGLM, isUITars } from '@/ai-model/auto-glm/util';\nimport {\n AIResponseParseError,\n AiExtractElementInfo,\n AiLocateElement,\n callAIWithObjectResponse,\n} from '@/ai-model/index';\nimport { AiLocateSection, buildSearchAreaConfig } from '@/ai-model/inspect';\nimport { elementDescriberInstruction } from '@/ai-model/prompt/describe';\nimport { formatAIMemories, normalizeAIMemory } from '@/ai-model/prompt/util';\nimport { type AIArgs, expandSearchArea } from '@/common';\nimport type {\n AIDescribeElementResponse,\n AIUsageInfo,\n DetailedLocateParam,\n LocateResultElement,\n LocateResultWithDump,\n PartialServiceDumpFromSDK,\n PlanningLocateParam,\n Rect,\n ServiceExtractOption,\n ServiceExtractParam,\n ServiceExtractResult,\n ServiceTaskInfo,\n UIContext,\n} from '@/types';\nimport { ServiceError } from '@/types';\nimport type { IModelConfig } from '@aiscene/shared/env';\nimport { compositeElementInfoImg, cropByRect } from '@aiscene/shared/img';\nimport { getDebug } from '@aiscene/shared/logger';\nimport { assert } from '@aiscene/shared/utils';\nimport type { TMultimodalPrompt } from '../common';\nimport { createServiceDump } from './utils';\n\nexport interface LocateOpts {\n context?: UIContext;\n planLocatedElement?: LocateResultElement;\n}\n\nexport type AnyValue<T> = {\n [K in keyof T]: unknown extends T[K] ? any : T[K];\n};\n\ninterface ServiceOptions {\n taskInfo?: Omit<ServiceTaskInfo, 'durationMs'>;\n memoryRetrieverFn?: () => readonly string[] | Promise<readonly string[]>;\n}\n\nconst debug = getDebug('ai:service');\nexport default class Service {\n contextRetrieverFn: () => Promise<UIContext> | UIContext;\n\n taskInfo?: Omit<ServiceTaskInfo, 'durationMs'>;\n private memoryRetrieverFn?: () =>\n | readonly string[]\n | Promise<readonly string[]>;\n\n constructor(\n context: UIContext | (() => Promise<UIContext> | UIContext),\n opt?: ServiceOptions,\n ) {\n assert(context, 'context is required for Service');\n if (typeof context === 'function') {\n this.contextRetrieverFn = context;\n } else {\n this.contextRetrieverFn = () => Promise.resolve(context);\n }\n\n if (typeof opt?.taskInfo !== 'undefined') {\n this.taskInfo = opt.taskInfo;\n }\n this.memoryRetrieverFn = opt?.memoryRetrieverFn;\n }\n\n private async getAIMemory(): Promise<string[] | undefined> {\n const memories = (await this.memoryRetrieverFn?.()) || [];\n const normalizedMemories = normalizeAIMemory(memories);\n return normalizedMemories.length ? normalizedMemories : undefined;\n }\n\n async locate(\n query: PlanningLocateParam,\n opt: LocateOpts,\n modelConfig: IModelConfig,\n abortSignal?: AbortSignal,\n ): Promise<LocateResultWithDump> {\n const queryPrompt = typeof query === 'string' ? query : query.prompt;\n assert(queryPrompt, 'query is required for locate');\n\n assert(typeof query === 'object', 'query should be an object for locate');\n\n const hasPlanLocatedElement = !!opt?.planLocatedElement?.rect;\n\n let searchAreaPrompt;\n if (query.deepLocate && !hasPlanLocatedElement) {\n searchAreaPrompt = query.prompt;\n }\n\n const { modelFamily } = modelConfig;\n\n if (searchAreaPrompt && !modelFamily) {\n console.warn(\n 'The \"deepLocate\" feature is not supported with multimodal LLM. Please config VL model for Midscene. https://midscenejs.com/model-config',\n );\n searchAreaPrompt = undefined;\n }\n\n if (searchAreaPrompt && isAutoGLM(modelFamily)) {\n console.warn('The \"deepLocate\" feature is not supported with AutoGLM.');\n searchAreaPrompt = undefined;\n }\n\n const context = opt?.context || (await this.contextRetrieverFn());\n const memories = await this.getAIMemory();\n\n let searchArea: Rect | undefined = undefined;\n let searchAreaRawResponse: string | undefined = undefined;\n let searchAreaUsage: AIUsageInfo | undefined = undefined;\n let searchAreaResponse:\n | Awaited<ReturnType<typeof AiLocateSection>>\n | undefined = undefined;\n if (query.deepLocate && hasPlanLocatedElement) {\n const searchAreaConfig = await buildSearchAreaConfig({\n context,\n baseRect: opt.planLocatedElement!.rect,\n modelFamily,\n });\n searchArea = searchAreaConfig.rect;\n\n searchAreaRawResponse = JSON.stringify({\n source: 'plan-located-element',\n rect: opt.planLocatedElement!.rect,\n });\n searchAreaResponse = {\n rect: searchArea,\n imageBase64: searchAreaConfig.imageBase64,\n scale: searchAreaConfig.scale,\n rawResponse: searchAreaRawResponse,\n };\n } else if (searchAreaPrompt) {\n searchAreaResponse = await AiLocateSection({\n context,\n sectionDescription: searchAreaPrompt,\n modelConfig,\n memories,\n abortSignal,\n });\n assert(\n searchAreaResponse.rect,\n `cannot find search area for \"${searchAreaPrompt}\"${\n searchAreaResponse.error ? `: ${searchAreaResponse.error}` : ''\n }`,\n );\n searchAreaRawResponse = searchAreaResponse.rawResponse;\n searchAreaUsage = searchAreaResponse.usage;\n searchArea = searchAreaResponse.rect;\n }\n\n const startTime = Date.now();\n const { parseResult, rect, rawResponse, usage, reasoning_content } =\n await AiLocateElement({\n context,\n targetElementDescription: queryPrompt,\n searchConfig: searchAreaResponse,\n modelConfig,\n memories,\n abortSignal,\n });\n\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse: JSON.stringify(rawResponse),\n formatResponse: JSON.stringify(parseResult),\n usage,\n searchArea,\n searchAreaRawResponse,\n searchAreaUsage,\n reasoning_content,\n };\n\n let errorLog: string | undefined;\n if (parseResult.errors?.length) {\n errorLog = `failed to locate element: \\n${parseResult.errors.join('\\n')}`;\n }\n\n const dumpData: PartialServiceDumpFromSDK = {\n type: 'locate',\n userQuery: {\n element: queryPrompt,\n },\n matchedElement: [],\n matchedRect: rect,\n data: null,\n taskInfo,\n deepLocate: !!searchArea,\n error: errorLog,\n };\n\n const elements = parseResult.elements || [];\n\n const dump = createServiceDump({\n ...dumpData,\n matchedElement: elements,\n });\n\n if (errorLog) {\n throw new ServiceError(errorLog, dump);\n }\n\n if (elements.length > 1) {\n throw new ServiceError(\n `locate: multiple elements found, length = ${elements.length}`,\n dump,\n );\n }\n\n if (elements.length === 1) {\n return {\n element: {\n center: elements[0]!.center,\n rect: elements[0]!.rect,\n description: elements[0]!.description,\n },\n rect,\n dump,\n };\n }\n\n return {\n element: null,\n rect,\n dump,\n };\n }\n\n async extract<T>(\n dataDemand: ServiceExtractParam,\n modelConfig: IModelConfig,\n opt?: ServiceExtractOption,\n pageDescription?: string,\n multimodalPrompt?: TMultimodalPrompt,\n context?: UIContext,\n ): Promise<ServiceExtractResult<T>> {\n assert(context, 'context is required for extract');\n assert(\n typeof dataDemand === 'object' || typeof dataDemand === 'string',\n `dataDemand should be object or string, but get ${typeof dataDemand}`,\n );\n\n const startTime = Date.now();\n\n let parseResult: Awaited<\n ReturnType<typeof AiExtractElementInfo<T>>\n >['parseResult'];\n let rawResponse: string;\n let usage: Awaited<ReturnType<typeof AiExtractElementInfo<T>>>['usage'];\n let reasoning_content: string | undefined;\n\n try {\n const result = await AiExtractElementInfo<T>({\n context,\n dataQuery: dataDemand,\n multimodalPrompt,\n extractOption: opt,\n modelConfig,\n pageDescription,\n memories: await this.getAIMemory(),\n });\n parseResult = result.parseResult;\n rawResponse = result.rawResponse;\n usage = result.usage;\n reasoning_content = result.reasoning_content;\n } catch (error) {\n if (error instanceof AIResponseParseError) {\n // Create dump with usage and rawResponse from the error\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse: error.rawResponse,\n usage: error.usage,\n };\n const dump = createServiceDump({\n type: 'extract',\n userQuery: { dataDemand },\n matchedElement: [],\n data: null,\n taskInfo,\n error: error.message,\n });\n throw new ServiceError(error.message, dump);\n }\n throw error;\n }\n\n const timeCost = Date.now() - startTime;\n const taskInfo: ServiceTaskInfo = {\n ...(this.taskInfo ? this.taskInfo : {}),\n durationMs: timeCost,\n rawResponse,\n formatResponse: JSON.stringify(parseResult),\n usage,\n reasoning_content,\n };\n\n let errorLog: string | undefined;\n if (parseResult.errors?.length) {\n errorLog = `AI response error: \\n${parseResult.errors.join('\\n')}`;\n }\n\n const dumpData: PartialServiceDumpFromSDK = {\n type: 'extract',\n userQuery: {\n dataDemand,\n },\n matchedElement: [],\n data: null,\n taskInfo,\n error: errorLog,\n };\n\n const { data, thought } = parseResult || {};\n\n const dump = createServiceDump({\n ...dumpData,\n data,\n });\n\n if (errorLog && !data) {\n throw new ServiceError(errorLog, dump);\n }\n\n return {\n data,\n thought,\n usage,\n reasoning_content,\n dump,\n };\n }\n\n async describe(\n target: Rect | [number, number],\n modelConfig: IModelConfig,\n opt?: {\n deepLocate?: boolean;\n },\n ): Promise<Pick<AIDescribeElementResponse, 'description'>> {\n assert(target, 'target is required for service.describe');\n const context = await this.contextRetrieverFn();\n const { shotSize } = context;\n const screenshotBase64 = context.screenshot.base64;\n assert(screenshotBase64, 'screenshot is required for service.describe');\n // The result of the \"describe\" function will be used for positioning, so essentially it is a form of grounding.\n const { modelFamily } = modelConfig;\n const systemPrompt = elementDescriberInstruction();\n\n // Convert [x,y] center point to Rect if needed\n const defaultRectSize = 30;\n const targetRect: Rect = Array.isArray(target)\n ? {\n left: Math.floor(target[0] - defaultRectSize / 2),\n top: Math.floor(target[1] - defaultRectSize / 2),\n width: defaultRectSize,\n height: defaultRectSize,\n }\n : target;\n\n let imagePayload = await compositeElementInfoImg({\n inputImgBase64: screenshotBase64,\n size: shotSize,\n elementsPositionInfo: [\n {\n rect: targetRect,\n },\n ],\n borderThickness: 3,\n });\n\n if (opt?.deepLocate) {\n const searchArea = expandSearchArea(targetRect, shotSize);\n // Always crop in describe mode. Unlike locate's deepLocate (where\n // cropping too small loses context for finding elements), describe's\n // deepLocate intentionally zooms in so the model produces a more\n // precise description from a focused view. expandSearchArea already\n // guarantees a minimum 400x400 area with surrounding context.\n debug('describe: cropping to searchArea', searchArea);\n const croppedResult = await cropByRect(\n imagePayload,\n searchArea,\n modelFamily === 'qwen2.5-vl',\n );\n imagePayload = croppedResult.imageBase64;\n }\n\n const memories = await this.getAIMemory();\n const memoryText = formatAIMemories(memories);\n const msgs: AIArgs = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n ...(memoryText\n ? [\n {\n type: 'text' as const,\n text: memoryText,\n },\n ]\n : []),\n {\n type: 'image_url',\n image_url: {\n url: imagePayload,\n detail: 'high',\n },\n },\n ],\n },\n ];\n\n const res = await callAIWithObjectResponse<AIDescribeElementResponse>(\n msgs,\n modelConfig,\n );\n\n const { content } = res;\n assert(!content.error, `describe failed: ${content.error}`);\n assert(content.description, 'failed to describe the element');\n return content;\n }\n}\n"],"names":["debug","getDebug","Service","memories","normalizedMemories","normalizeAIMemory","undefined","query","opt","modelConfig","abortSignal","queryPrompt","assert","hasPlanLocatedElement","searchAreaPrompt","modelFamily","console","isAutoGLM","context","searchArea","searchAreaRawResponse","searchAreaUsage","searchAreaResponse","searchAreaConfig","buildSearchAreaConfig","JSON","AiLocateSection","startTime","Date","parseResult","rect","rawResponse","usage","reasoning_content","AiLocateElement","timeCost","taskInfo","errorLog","dumpData","elements","dump","createServiceDump","ServiceError","dataDemand","pageDescription","multimodalPrompt","result","AiExtractElementInfo","error","AIResponseParseError","data","thought","target","shotSize","screenshotBase64","systemPrompt","elementDescriberInstruction","defaultRectSize","targetRect","Array","Math","imagePayload","compositeElementInfoImg","expandSearchArea","croppedResult","cropByRect","memoryText","formatAIMemories","msgs","res","callAIWithObjectResponse","content","Promise"],"mappings":"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"}
|
package/dist/es/types.mjs
CHANGED
|
@@ -23,25 +23,6 @@ class ServiceError extends Error {
|
|
|
23
23
|
this.dump = dump;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
class ActionAssertionError extends Error {
|
|
27
|
-
constructor(message, options = {}){
|
|
28
|
-
super(message), _define_property(this, "actionName", void 0), _define_property(this, "detail", void 0);
|
|
29
|
-
this.name = 'ActionAssertionError';
|
|
30
|
-
this.actionName = options.actionName;
|
|
31
|
-
this.detail = options.detail;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function isActionAssertionError(err) {
|
|
35
|
-
const isMatch = (e)=>e instanceof ActionAssertionError || 'object' == typeof e && null !== e && 'ActionAssertionError' === e.name;
|
|
36
|
-
let cursor = err;
|
|
37
|
-
for(let depth = 0; depth < 8 && cursor; depth++){
|
|
38
|
-
if (isMatch(cursor)) return true;
|
|
39
|
-
const next = cursor.cause;
|
|
40
|
-
if (next === cursor) break;
|
|
41
|
-
cursor = next;
|
|
42
|
-
}
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
26
|
function replacerForDumpSerialization(_key, value) {
|
|
46
27
|
if (value && value.constructor?.name === 'Page') return '[Page object]';
|
|
47
28
|
if (value && value.constructor?.name === 'Browser') return '[Browser object]';
|
|
@@ -218,6 +199,6 @@ class ReportActionDump {
|
|
|
218
199
|
}
|
|
219
200
|
}
|
|
220
201
|
const GroupedActionDump = ReportActionDump;
|
|
221
|
-
export {
|
|
202
|
+
export { ExecutionDump, GroupedActionDump, ReportActionDump, ServiceError, UIContext };
|
|
222
203
|
|
|
223
204
|
//# sourceMappingURL=types.mjs.map
|