@godscene/core 1.7.11

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.
Files changed (189) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -0
  3. package/dist/es/agent/agent.mjs +767 -0
  4. package/dist/es/agent/common.mjs +0 -0
  5. package/dist/es/agent/execution-session.mjs +39 -0
  6. package/dist/es/agent/index.mjs +6 -0
  7. package/dist/es/agent/task-builder.mjs +343 -0
  8. package/dist/es/agent/task-cache.mjs +212 -0
  9. package/dist/es/agent/tasks.mjs +428 -0
  10. package/dist/es/agent/ui-utils.mjs +101 -0
  11. package/dist/es/agent/utils.mjs +167 -0
  12. package/dist/es/ai-model/auto-glm/actions.mjs +237 -0
  13. package/dist/es/ai-model/auto-glm/index.mjs +6 -0
  14. package/dist/es/ai-model/auto-glm/parser.mjs +237 -0
  15. package/dist/es/ai-model/auto-glm/planning.mjs +69 -0
  16. package/dist/es/ai-model/auto-glm/prompt.mjs +220 -0
  17. package/dist/es/ai-model/auto-glm/util.mjs +7 -0
  18. package/dist/es/ai-model/connectivity.mjs +136 -0
  19. package/dist/es/ai-model/conversation-history.mjs +193 -0
  20. package/dist/es/ai-model/index.mjs +12 -0
  21. package/dist/es/ai-model/inspect.mjs +395 -0
  22. package/dist/es/ai-model/llm-planning.mjs +231 -0
  23. package/dist/es/ai-model/prompt/common.mjs +5 -0
  24. package/dist/es/ai-model/prompt/describe.mjs +64 -0
  25. package/dist/es/ai-model/prompt/extraction.mjs +129 -0
  26. package/dist/es/ai-model/prompt/llm-locator.mjs +49 -0
  27. package/dist/es/ai-model/prompt/llm-planning.mjs +584 -0
  28. package/dist/es/ai-model/prompt/llm-section-locator.mjs +42 -0
  29. package/dist/es/ai-model/prompt/order-sensitive-judge.mjs +33 -0
  30. package/dist/es/ai-model/prompt/playwright-generator.mjs +115 -0
  31. package/dist/es/ai-model/prompt/ui-tars-planning.mjs +34 -0
  32. package/dist/es/ai-model/prompt/util.mjs +57 -0
  33. package/dist/es/ai-model/prompt/yaml-generator.mjs +201 -0
  34. package/dist/es/ai-model/service-caller/codex-app-server.mjs +573 -0
  35. package/dist/es/ai-model/service-caller/image-detail.mjs +4 -0
  36. package/dist/es/ai-model/service-caller/index.mjs +648 -0
  37. package/dist/es/ai-model/service-caller/request-timeout.mjs +47 -0
  38. package/dist/es/ai-model/ui-tars-planning.mjs +247 -0
  39. package/dist/es/common.mjs +382 -0
  40. package/dist/es/device/device-options.mjs +0 -0
  41. package/dist/es/device/index.mjs +340 -0
  42. package/dist/es/dump/html-utils.mjs +290 -0
  43. package/dist/es/dump/index.mjs +3 -0
  44. package/dist/es/dump/screenshot-restoration.mjs +30 -0
  45. package/dist/es/dump/screenshot-store.mjs +125 -0
  46. package/dist/es/index.mjs +17 -0
  47. package/dist/es/report-cli.mjs +149 -0
  48. package/dist/es/report-generator.mjs +203 -0
  49. package/dist/es/report-markdown.mjs +216 -0
  50. package/dist/es/report.mjs +287 -0
  51. package/dist/es/screenshot-item.mjs +120 -0
  52. package/dist/es/service/index.mjs +272 -0
  53. package/dist/es/service/utils.mjs +13 -0
  54. package/dist/es/skill/index.mjs +35 -0
  55. package/dist/es/task-runner.mjs +261 -0
  56. package/dist/es/task-timing.mjs +10 -0
  57. package/dist/es/tree.mjs +11 -0
  58. package/dist/es/types.mjs +202 -0
  59. package/dist/es/utils.mjs +232 -0
  60. package/dist/es/yaml/builder.mjs +11 -0
  61. package/dist/es/yaml/index.mjs +4 -0
  62. package/dist/es/yaml/player.mjs +425 -0
  63. package/dist/es/yaml/utils.mjs +100 -0
  64. package/dist/es/yaml.mjs +0 -0
  65. package/dist/lib/agent/agent.js +815 -0
  66. package/dist/lib/agent/common.js +5 -0
  67. package/dist/lib/agent/execution-session.js +73 -0
  68. package/dist/lib/agent/index.js +76 -0
  69. package/dist/lib/agent/task-builder.js +380 -0
  70. package/dist/lib/agent/task-cache.js +264 -0
  71. package/dist/lib/agent/tasks.js +471 -0
  72. package/dist/lib/agent/ui-utils.js +153 -0
  73. package/dist/lib/agent/utils.js +238 -0
  74. package/dist/lib/ai-model/auto-glm/actions.js +271 -0
  75. package/dist/lib/ai-model/auto-glm/index.js +64 -0
  76. package/dist/lib/ai-model/auto-glm/parser.js +280 -0
  77. package/dist/lib/ai-model/auto-glm/planning.js +103 -0
  78. package/dist/lib/ai-model/auto-glm/prompt.js +257 -0
  79. package/dist/lib/ai-model/auto-glm/util.js +44 -0
  80. package/dist/lib/ai-model/connectivity.js +180 -0
  81. package/dist/lib/ai-model/conversation-history.js +227 -0
  82. package/dist/lib/ai-model/index.js +127 -0
  83. package/dist/lib/ai-model/inspect.js +441 -0
  84. package/dist/lib/ai-model/llm-planning.js +268 -0
  85. package/dist/lib/ai-model/prompt/common.js +39 -0
  86. package/dist/lib/ai-model/prompt/describe.js +98 -0
  87. package/dist/lib/ai-model/prompt/extraction.js +169 -0
  88. package/dist/lib/ai-model/prompt/llm-locator.js +86 -0
  89. package/dist/lib/ai-model/prompt/llm-planning.js +621 -0
  90. package/dist/lib/ai-model/prompt/llm-section-locator.js +79 -0
  91. package/dist/lib/ai-model/prompt/order-sensitive-judge.js +70 -0
  92. package/dist/lib/ai-model/prompt/playwright-generator.js +176 -0
  93. package/dist/lib/ai-model/prompt/ui-tars-planning.js +71 -0
  94. package/dist/lib/ai-model/prompt/util.js +103 -0
  95. package/dist/lib/ai-model/prompt/yaml-generator.js +262 -0
  96. package/dist/lib/ai-model/service-caller/codex-app-server.js +622 -0
  97. package/dist/lib/ai-model/service-caller/image-detail.js +38 -0
  98. package/dist/lib/ai-model/service-caller/index.js +716 -0
  99. package/dist/lib/ai-model/service-caller/request-timeout.js +93 -0
  100. package/dist/lib/ai-model/ui-tars-planning.js +281 -0
  101. package/dist/lib/common.js +491 -0
  102. package/dist/lib/device/device-options.js +18 -0
  103. package/dist/lib/device/index.js +467 -0
  104. package/dist/lib/dump/html-utils.js +366 -0
  105. package/dist/lib/dump/index.js +58 -0
  106. package/dist/lib/dump/screenshot-restoration.js +64 -0
  107. package/dist/lib/dump/screenshot-store.js +165 -0
  108. package/dist/lib/index.js +184 -0
  109. package/dist/lib/report-cli.js +189 -0
  110. package/dist/lib/report-generator.js +244 -0
  111. package/dist/lib/report-markdown.js +253 -0
  112. package/dist/lib/report.js +333 -0
  113. package/dist/lib/screenshot-item.js +154 -0
  114. package/dist/lib/service/index.js +306 -0
  115. package/dist/lib/service/utils.js +47 -0
  116. package/dist/lib/skill/index.js +69 -0
  117. package/dist/lib/task-runner.js +298 -0
  118. package/dist/lib/task-timing.js +44 -0
  119. package/dist/lib/tree.js +51 -0
  120. package/dist/lib/types.js +298 -0
  121. package/dist/lib/utils.js +314 -0
  122. package/dist/lib/yaml/builder.js +55 -0
  123. package/dist/lib/yaml/index.js +79 -0
  124. package/dist/lib/yaml/player.js +459 -0
  125. package/dist/lib/yaml/utils.js +153 -0
  126. package/dist/lib/yaml.js +18 -0
  127. package/dist/types/agent/agent.d.ts +220 -0
  128. package/dist/types/agent/common.d.ts +0 -0
  129. package/dist/types/agent/execution-session.d.ts +36 -0
  130. package/dist/types/agent/index.d.ts +9 -0
  131. package/dist/types/agent/task-builder.d.ts +34 -0
  132. package/dist/types/agent/task-cache.d.ts +49 -0
  133. package/dist/types/agent/tasks.d.ts +70 -0
  134. package/dist/types/agent/ui-utils.d.ts +14 -0
  135. package/dist/types/agent/utils.d.ts +25 -0
  136. package/dist/types/ai-model/auto-glm/actions.d.ts +78 -0
  137. package/dist/types/ai-model/auto-glm/index.d.ts +6 -0
  138. package/dist/types/ai-model/auto-glm/parser.d.ts +18 -0
  139. package/dist/types/ai-model/auto-glm/planning.d.ts +12 -0
  140. package/dist/types/ai-model/auto-glm/prompt.d.ts +27 -0
  141. package/dist/types/ai-model/auto-glm/util.d.ts +13 -0
  142. package/dist/types/ai-model/connectivity.d.ts +20 -0
  143. package/dist/types/ai-model/conversation-history.d.ts +105 -0
  144. package/dist/types/ai-model/index.d.ts +16 -0
  145. package/dist/types/ai-model/inspect.d.ts +67 -0
  146. package/dist/types/ai-model/llm-planning.d.ts +19 -0
  147. package/dist/types/ai-model/prompt/common.d.ts +2 -0
  148. package/dist/types/ai-model/prompt/describe.d.ts +1 -0
  149. package/dist/types/ai-model/prompt/extraction.d.ts +7 -0
  150. package/dist/types/ai-model/prompt/llm-locator.d.ts +3 -0
  151. package/dist/types/ai-model/prompt/llm-planning.d.ts +10 -0
  152. package/dist/types/ai-model/prompt/llm-section-locator.d.ts +3 -0
  153. package/dist/types/ai-model/prompt/order-sensitive-judge.d.ts +2 -0
  154. package/dist/types/ai-model/prompt/playwright-generator.d.ts +26 -0
  155. package/dist/types/ai-model/prompt/ui-tars-planning.d.ts +2 -0
  156. package/dist/types/ai-model/prompt/util.d.ts +33 -0
  157. package/dist/types/ai-model/prompt/yaml-generator.d.ts +102 -0
  158. package/dist/types/ai-model/service-caller/codex-app-server.d.ts +42 -0
  159. package/dist/types/ai-model/service-caller/image-detail.d.ts +2 -0
  160. package/dist/types/ai-model/service-caller/index.d.ts +60 -0
  161. package/dist/types/ai-model/service-caller/request-timeout.d.ts +32 -0
  162. package/dist/types/ai-model/ui-tars-planning.d.ts +72 -0
  163. package/dist/types/common.d.ts +288 -0
  164. package/dist/types/device/device-options.d.ts +155 -0
  165. package/dist/types/device/index.d.ts +2565 -0
  166. package/dist/types/dump/html-utils.d.ts +75 -0
  167. package/dist/types/dump/index.d.ts +5 -0
  168. package/dist/types/dump/screenshot-restoration.d.ts +8 -0
  169. package/dist/types/dump/screenshot-store.d.ts +49 -0
  170. package/dist/types/index.d.ts +21 -0
  171. package/dist/types/report-cli.d.ts +36 -0
  172. package/dist/types/report-generator.d.ts +88 -0
  173. package/dist/types/report-markdown.d.ts +24 -0
  174. package/dist/types/report.d.ts +52 -0
  175. package/dist/types/screenshot-item.d.ts +67 -0
  176. package/dist/types/service/index.d.ts +24 -0
  177. package/dist/types/service/utils.d.ts +2 -0
  178. package/dist/types/skill/index.d.ts +25 -0
  179. package/dist/types/task-runner.d.ts +50 -0
  180. package/dist/types/task-timing.d.ts +8 -0
  181. package/dist/types/tree.d.ts +4 -0
  182. package/dist/types/types.d.ts +684 -0
  183. package/dist/types/utils.d.ts +45 -0
  184. package/dist/types/yaml/builder.d.ts +2 -0
  185. package/dist/types/yaml/index.d.ts +4 -0
  186. package/dist/types/yaml/player.d.ts +34 -0
  187. package/dist/types/yaml/utils.d.ts +9 -0
  188. package/dist/types/yaml.d.ts +215 -0
  189. package/package.json +130 -0
@@ -0,0 +1,115 @@
1
+ import { PLAYWRIGHT_EXAMPLE_CODE } from "@godscene/shared/constants";
2
+ import { callAI, callAIWithStringResponse } from "../index.mjs";
3
+ import { createEventCounts, createMessageContent, extractInputDescriptions, filterEventsByType, getScreenshotsForLLM, prepareEventSummary, processEventsForLLM, validateEvents } from "./yaml-generator.mjs";
4
+ const generatePlaywrightTest = async (events, options, modelConfig)=>{
5
+ validateEvents(events);
6
+ const summary = prepareEventSummary(events, {
7
+ testName: options.testName,
8
+ maxScreenshots: options.maxScreenshots || 3
9
+ });
10
+ const playwrightSummary = {
11
+ ...summary,
12
+ waitForNetworkIdle: false !== options.waitForNetworkIdle,
13
+ waitForNetworkIdleTimeout: options.waitForNetworkIdleTimeout || 2000,
14
+ viewportSize: options.viewportSize || {
15
+ width: 1280,
16
+ height: 800
17
+ }
18
+ };
19
+ const screenshots = getScreenshotsForLLM(events, options.maxScreenshots || 3);
20
+ const promptText = `Generate a Playwright test using @godscene/web/playwright that reproduces this recorded browser session. The test should be based on the following events and follow the structure of the example provided. Make the test descriptive with appropriate assertions and validations.
21
+
22
+ Event Summary:
23
+ ${JSON.stringify(playwrightSummary, null, 2)}
24
+
25
+ Generated code should:
26
+ 1. Import required dependencies
27
+ 2. Set up the test with proper configuration
28
+ 3. Include a beforeEach hook to navigate to the starting URL
29
+ 4. Implement a test that uses Midscene AI methods (aiTap, aiInput, aiAssert, etc.)
30
+ 5. Include appropriate assertions and validations
31
+ 6. Follow best practices for Playwright tests
32
+ 7. Be ready to execute without further modification
33
+
34
+ Important: Return ONLY the raw Playwright test code. Do NOT wrap the response in markdown code blocks (no \`\`\`typescript, \`\`\`javascript or \`\`\`). Start directly with the code content.`;
35
+ const messageContent = createMessageContent(promptText, screenshots, false !== options.includeScreenshots);
36
+ const systemPrompt = `You are an expert test automation engineer specializing in Playwright and Midscene.
37
+ Your task is to generate a complete, executable Playwright test using @godscene/web/playwright that reproduces a recorded browser session.
38
+
39
+ ${PLAYWRIGHT_EXAMPLE_CODE}`;
40
+ const prompt = [
41
+ {
42
+ role: 'system',
43
+ content: systemPrompt
44
+ },
45
+ {
46
+ role: 'user',
47
+ content: messageContent
48
+ }
49
+ ];
50
+ const response = await callAIWithStringResponse(prompt, modelConfig);
51
+ if (response?.content && 'string' == typeof response.content) return response.content;
52
+ throw new Error('Failed to generate Playwright test code');
53
+ };
54
+ const generatePlaywrightTestStream = async (events, options, modelConfig)=>{
55
+ validateEvents(events);
56
+ const summary = prepareEventSummary(events, {
57
+ testName: options.testName,
58
+ maxScreenshots: options.maxScreenshots || 3
59
+ });
60
+ const playwrightSummary = {
61
+ ...summary,
62
+ waitForNetworkIdle: false !== options.waitForNetworkIdle,
63
+ waitForNetworkIdleTimeout: options.waitForNetworkIdleTimeout || 2000,
64
+ viewportSize: options.viewportSize || {
65
+ width: 1280,
66
+ height: 800
67
+ }
68
+ };
69
+ const screenshots = getScreenshotsForLLM(events, options.maxScreenshots || 3);
70
+ const promptText = `Generate a Playwright test using @godscene/web/playwright that reproduces this recorded browser session. The test should be based on the following events and follow the structure of the example provided. Make the test descriptive with appropriate assertions and validations.
71
+
72
+ Event Summary:
73
+ ${JSON.stringify(playwrightSummary, null, 2)}
74
+
75
+ Generated code should:
76
+ 1. Import required dependencies
77
+ 2. Set up the test with proper configuration
78
+ 3. Include a beforeEach hook to navigate to the starting URL
79
+ 4. Implement a test that uses Midscene AI methods (aiTap, aiInput, aiAssert, etc.)
80
+ 5. Include appropriate assertions and validations
81
+ 6. Follow best practices for Playwright tests
82
+ 7. Be ready to execute without further modification
83
+ 8. can't wrap this test code in markdown code block
84
+
85
+ Important: Return ONLY the raw Playwright test code. Do NOT wrap the response in markdown code blocks (no \`\`\`typescript, \`\`\`javascript or \`\`\`). Start directly with the code content.`;
86
+ const messageContent = createMessageContent(promptText, screenshots, false !== options.includeScreenshots);
87
+ const systemPrompt = `You are an expert test automation engineer specializing in Playwright and Midscene.
88
+ Your task is to generate a complete, executable Playwright test using @godscene/web/playwright that reproduces a recorded browser session.
89
+
90
+ ${PLAYWRIGHT_EXAMPLE_CODE}`;
91
+ const prompt = [
92
+ {
93
+ role: 'system',
94
+ content: systemPrompt
95
+ },
96
+ {
97
+ role: 'user',
98
+ content: messageContent
99
+ }
100
+ ];
101
+ if (options.stream && options.onChunk) return await callAI(prompt, modelConfig, {
102
+ stream: true,
103
+ onChunk: options.onChunk
104
+ });
105
+ {
106
+ const response = await callAIWithStringResponse(prompt, modelConfig);
107
+ if (response?.content && 'string' == typeof response.content) return {
108
+ content: response.content,
109
+ usage: response.usage,
110
+ isStreamed: false
111
+ };
112
+ throw new Error('Failed to generate Playwright test code');
113
+ }
114
+ };
115
+ export { createEventCounts, createMessageContent, extractInputDescriptions, filterEventsByType, generatePlaywrightTest, generatePlaywrightTestStream, getScreenshotsForLLM, prepareEventSummary, processEventsForLLM, validateEvents };
@@ -0,0 +1,34 @@
1
+ import { getPreferredLanguage } from "@godscene/shared/env";
2
+ function getUiTarsPlanningPrompt() {
3
+ const preferredLanguage = getPreferredLanguage();
4
+ return `
5
+ 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.
6
+
7
+ ## Output Format
8
+ \`\`\`
9
+ Thought: ...
10
+ Action: ...
11
+ \`\`\`
12
+
13
+ ## Action Space
14
+
15
+ click(start_box='[x1, y1, x2, y2]')
16
+ left_double(start_box='[x1, y1, x2, y2]')
17
+ right_single(start_box='[x1, y1, x2, y2]')
18
+ drag(start_box='[x1, y1, x2, y2]', end_box='[x3, y3, x4, y4]')
19
+ hotkey(key='')
20
+ 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.
21
+ scroll(start_box='[x1, y1, x2, y2]', direction='down or up or right or left')
22
+ wait() #Sleep for 5s and take a screenshot to check for any changes.
23
+ finished(content='xxx') # Use escape characters \\', \\", and \\n in content part to ensure we can parse the content in normal python string format.
24
+
25
+
26
+ ## Note
27
+ - Use ${preferredLanguage} in \`Thought\` part.
28
+ - Write a small plan and finally summarize your next action (with its target element) in one sentence in \`Thought\` part.
29
+
30
+ ## User Instruction
31
+ `;
32
+ }
33
+ const getSummary = (prediction)=>prediction.replace(/Reflection:[\s\S]*?(?=Action_Summary:|Action:|$)/g, '').trim();
34
+ export { getSummary, getUiTarsPlanningPrompt };
@@ -0,0 +1,57 @@
1
+ function extractXMLTag(xmlString, tagName) {
2
+ const lowerXmlString = xmlString.toLowerCase();
3
+ const lowerTagName = tagName.toLowerCase();
4
+ const closeTag = `</${lowerTagName}>`;
5
+ const openTag = `<${lowerTagName}>`;
6
+ const lastCloseIndex = lowerXmlString.lastIndexOf(closeTag);
7
+ if (-1 === lastCloseIndex) {
8
+ const lastOpenIndex = lowerXmlString.lastIndexOf(openTag);
9
+ if (-1 === lastOpenIndex) return;
10
+ const contentStart = lastOpenIndex + openTag.length;
11
+ const remaining = xmlString.substring(contentStart);
12
+ const nextTagIndex = remaining.indexOf('<');
13
+ const content = -1 === nextTagIndex ? remaining : remaining.substring(0, nextTagIndex);
14
+ return content.trim();
15
+ }
16
+ const searchArea = lowerXmlString.substring(0, lastCloseIndex);
17
+ const lastOpenIndex = searchArea.lastIndexOf(openTag);
18
+ if (-1 === lastOpenIndex) return;
19
+ const contentStart = lastOpenIndex + openTag.length;
20
+ const contentEnd = lastCloseIndex;
21
+ const content = xmlString.substring(contentStart, contentEnd);
22
+ return content.trim();
23
+ }
24
+ function parseSubGoalsFromXML(xmlContent) {
25
+ const subGoals = [];
26
+ const regex = /<sub-goal\s+index="(\d+)"\s+status="(pending|finished)"(?:\s*\/>|>([\s\S]*?)<\/sub-goal>)/gi;
27
+ let match;
28
+ match = regex.exec(xmlContent);
29
+ while(null !== match){
30
+ const index = Number.parseInt(match[1], 10);
31
+ const status = match[2];
32
+ const description = match[3]?.trim() || '';
33
+ subGoals.push({
34
+ index,
35
+ status,
36
+ description
37
+ });
38
+ match = regex.exec(xmlContent);
39
+ }
40
+ return subGoals;
41
+ }
42
+ function parseMarkFinishedIndexes(xmlContent) {
43
+ const indexes = [];
44
+ const regex = /<sub-goal\s+index="(\d+)"\s+status="finished"\s*\/>/gi;
45
+ let match;
46
+ match = regex.exec(xmlContent);
47
+ while(null !== match){
48
+ indexes.push(Number.parseInt(match[1], 10));
49
+ match = regex.exec(xmlContent);
50
+ }
51
+ return indexes;
52
+ }
53
+ const distanceThreshold = 16;
54
+ function distance(point1, point2) {
55
+ return Math.sqrt((point1.x - point2.x) ** 2 + (point1.y - point2.y) ** 2);
56
+ }
57
+ export { distance, distanceThreshold, extractXMLTag, parseMarkFinishedIndexes, parseSubGoalsFromXML };
@@ -0,0 +1,201 @@
1
+ import { YAML_EXAMPLE_CODE } from "@godscene/shared/constants";
2
+ import { callAI, callAIWithStringResponse } from "../index.mjs";
3
+ const getScreenshotsForLLM = (events, maxScreenshots = 1)=>{
4
+ const eventsWithScreenshots = events.filter((event)=>event.screenshotBefore || event.screenshotAfter || event.screenshotWithBox);
5
+ const sortedEvents = [
6
+ ...eventsWithScreenshots
7
+ ].sort((a, b)=>{
8
+ if ('navigation' === a.type && 'navigation' !== b.type) return -1;
9
+ if ('navigation' !== a.type && 'navigation' === b.type) return 1;
10
+ if ('click' === a.type && 'click' !== b.type) return -1;
11
+ if ('click' !== a.type && 'click' === b.type) return 1;
12
+ return 0;
13
+ });
14
+ const screenshots = [];
15
+ for (const event of sortedEvents){
16
+ const screenshot = event.screenshotWithBox || event.screenshotAfter || event.screenshotBefore;
17
+ if (screenshot && !screenshots.includes(screenshot)) {
18
+ screenshots.push(screenshot);
19
+ if (screenshots.length >= maxScreenshots) break;
20
+ }
21
+ }
22
+ return screenshots;
23
+ };
24
+ const filterEventsByType = (events)=>({
25
+ navigationEvents: events.filter((event)=>'navigation' === event.type),
26
+ clickEvents: events.filter((event)=>'click' === event.type),
27
+ inputEvents: events.filter((event)=>'input' === event.type),
28
+ scrollEvents: events.filter((event)=>'scroll' === event.type)
29
+ });
30
+ const createEventCounts = (filteredEvents, totalEvents)=>({
31
+ navigation: filteredEvents.navigationEvents.length,
32
+ click: filteredEvents.clickEvents.length,
33
+ input: filteredEvents.inputEvents.length,
34
+ scroll: filteredEvents.scrollEvents.length,
35
+ total: totalEvents
36
+ });
37
+ const extractInputDescriptions = (inputEvents)=>inputEvents.map((event)=>({
38
+ description: event.elementDescription || '',
39
+ value: event.value || ''
40
+ })).filter((item)=>item.description && item.value);
41
+ const processEventsForLLM = (events)=>events.map((event)=>({
42
+ type: event.type,
43
+ timestamp: event.timestamp,
44
+ url: event.url,
45
+ title: event.title,
46
+ elementDescription: event.elementDescription,
47
+ value: event.value,
48
+ pageInfo: event.pageInfo,
49
+ elementRect: event.elementRect
50
+ }));
51
+ const prepareEventSummary = (events, options = {})=>{
52
+ const filteredEvents = filterEventsByType(events);
53
+ const eventCounts = createEventCounts(filteredEvents, events.length);
54
+ const startUrl = filteredEvents.navigationEvents.length > 0 ? filteredEvents.navigationEvents[0].url || '' : '';
55
+ const clickDescriptions = filteredEvents.clickEvents.map((event)=>event.elementDescription).filter((desc)=>Boolean(desc)).slice(0, 10);
56
+ const inputDescriptions = extractInputDescriptions(filteredEvents.inputEvents).slice(0, 10);
57
+ const urls = filteredEvents.navigationEvents.map((e)=>e.url).filter((url)=>Boolean(url)).slice(0, 5);
58
+ const processedEvents = processEventsForLLM(events);
59
+ return {
60
+ testName: options.testName || 'Automated test from recorded events',
61
+ startUrl,
62
+ eventCounts,
63
+ urls,
64
+ clickDescriptions,
65
+ inputDescriptions,
66
+ events: processedEvents
67
+ };
68
+ };
69
+ const createMessageContent = (promptText, screenshots = [], includeScreenshots = true)=>{
70
+ const messageContent = [
71
+ {
72
+ type: 'text',
73
+ text: promptText
74
+ }
75
+ ];
76
+ if (includeScreenshots && screenshots.length > 0) {
77
+ messageContent.unshift({
78
+ type: 'text',
79
+ text: 'Here are screenshots from the recording session to help you understand the context:'
80
+ });
81
+ screenshots.forEach((screenshot)=>{
82
+ messageContent.push({
83
+ type: 'image_url',
84
+ image_url: {
85
+ url: screenshot
86
+ }
87
+ });
88
+ });
89
+ }
90
+ return messageContent;
91
+ };
92
+ const getYamlLanguageInstruction = (language)=>{
93
+ const normalizedLanguage = language?.trim();
94
+ if (!normalizedLanguage) return '';
95
+ return `
96
+ Language requirement:
97
+ - Write all human-readable YAML content in ${normalizedLanguage}.
98
+ - Keep YAML keys, field names, and Midscene API names unchanged.`;
99
+ };
100
+ const createYamlPrompt = ({ yamlSummary, screenshots, language })=>{
101
+ const prompt = [
102
+ {
103
+ role: 'system',
104
+ content: `You are an expert in RPA智能助手 YAML test generation. Generate clean, accurate YAML following these rules: ${YAML_EXAMPLE_CODE}`
105
+ },
106
+ {
107
+ role: 'user',
108
+ content: `Generate YAML test for RPA智能助手 automation from recorded browser events.
109
+
110
+ Event Summary:
111
+ ${JSON.stringify(yamlSummary, null, 2)}
112
+
113
+ Convert events:
114
+ - navigation → target.url
115
+ - click → aiTap with element description
116
+ - input → aiInput with value and locate
117
+ - scroll → aiScroll with appropriate direction
118
+ - Add aiAssert for important state changes${getYamlLanguageInstruction(language)}
119
+
120
+ Important: Return ONLY the raw YAML content. Do NOT wrap the response in markdown code blocks (no \`\`\`yaml or \`\`\`). Start directly with the YAML content.`
121
+ }
122
+ ];
123
+ if (screenshots.length > 0) {
124
+ prompt.push({
125
+ role: 'user',
126
+ content: 'Here are screenshots from the recording session to help you understand the context:'
127
+ });
128
+ prompt.push({
129
+ role: 'user',
130
+ content: screenshots.map((screenshot)=>({
131
+ type: 'image_url',
132
+ image_url: {
133
+ url: screenshot
134
+ }
135
+ }))
136
+ });
137
+ }
138
+ return prompt;
139
+ };
140
+ const validateEvents = (events)=>{
141
+ if (!events.length) throw new Error('No events provided for test generation');
142
+ };
143
+ const generateYamlTest = async (events, options, modelConfig)=>{
144
+ try {
145
+ validateEvents(events);
146
+ const summary = prepareEventSummary(events, {
147
+ testName: options.testName,
148
+ maxScreenshots: options.maxScreenshots || 3
149
+ });
150
+ const yamlSummary = {
151
+ ...summary,
152
+ includeTimestamps: options.includeTimestamps || false
153
+ };
154
+ const screenshots = getScreenshotsForLLM(events, options.maxScreenshots || 3);
155
+ const prompt = createYamlPrompt({
156
+ yamlSummary,
157
+ screenshots,
158
+ language: options.language
159
+ });
160
+ const response = await callAIWithStringResponse(prompt, modelConfig);
161
+ if (response?.content && 'string' == typeof response.content) return response.content;
162
+ throw new Error('Failed to generate YAML test configuration');
163
+ } catch (error) {
164
+ throw new Error(`Failed to generate YAML test: ${error}`);
165
+ }
166
+ };
167
+ const generateYamlTestStream = async (events, options, modelConfig)=>{
168
+ try {
169
+ validateEvents(events);
170
+ const summary = prepareEventSummary(events, {
171
+ testName: options.testName,
172
+ maxScreenshots: options.maxScreenshots || 3
173
+ });
174
+ const yamlSummary = {
175
+ ...summary,
176
+ includeTimestamps: options.includeTimestamps || false
177
+ };
178
+ const screenshots = getScreenshotsForLLM(events, options.maxScreenshots || 3);
179
+ const prompt = createYamlPrompt({
180
+ yamlSummary,
181
+ screenshots,
182
+ language: options.language
183
+ });
184
+ if (options.stream && options.onChunk) return await callAI(prompt, modelConfig, {
185
+ stream: true,
186
+ onChunk: options.onChunk
187
+ });
188
+ {
189
+ const response = await callAIWithStringResponse(prompt, modelConfig);
190
+ if (response?.content && 'string' == typeof response.content) return {
191
+ content: response.content,
192
+ usage: response.usage,
193
+ isStreamed: false
194
+ };
195
+ throw new Error('Failed to generate YAML test configuration');
196
+ }
197
+ } catch (error) {
198
+ throw new Error(`Failed to generate YAML test: ${error}`);
199
+ }
200
+ };
201
+ export { createEventCounts, createMessageContent, extractInputDescriptions, filterEventsByType, generateYamlTest, generateYamlTestStream, getScreenshotsForLLM, prepareEventSummary, processEventsForLLM, validateEvents };