@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.
- package/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/es/agent/agent.mjs +767 -0
- package/dist/es/agent/common.mjs +0 -0
- package/dist/es/agent/execution-session.mjs +39 -0
- package/dist/es/agent/index.mjs +6 -0
- package/dist/es/agent/task-builder.mjs +343 -0
- package/dist/es/agent/task-cache.mjs +212 -0
- package/dist/es/agent/tasks.mjs +428 -0
- package/dist/es/agent/ui-utils.mjs +101 -0
- package/dist/es/agent/utils.mjs +167 -0
- package/dist/es/ai-model/auto-glm/actions.mjs +237 -0
- package/dist/es/ai-model/auto-glm/index.mjs +6 -0
- package/dist/es/ai-model/auto-glm/parser.mjs +237 -0
- package/dist/es/ai-model/auto-glm/planning.mjs +69 -0
- package/dist/es/ai-model/auto-glm/prompt.mjs +220 -0
- package/dist/es/ai-model/auto-glm/util.mjs +7 -0
- package/dist/es/ai-model/connectivity.mjs +136 -0
- package/dist/es/ai-model/conversation-history.mjs +193 -0
- package/dist/es/ai-model/index.mjs +12 -0
- package/dist/es/ai-model/inspect.mjs +395 -0
- package/dist/es/ai-model/llm-planning.mjs +231 -0
- package/dist/es/ai-model/prompt/common.mjs +5 -0
- package/dist/es/ai-model/prompt/describe.mjs +64 -0
- package/dist/es/ai-model/prompt/extraction.mjs +129 -0
- package/dist/es/ai-model/prompt/llm-locator.mjs +49 -0
- package/dist/es/ai-model/prompt/llm-planning.mjs +584 -0
- package/dist/es/ai-model/prompt/llm-section-locator.mjs +42 -0
- package/dist/es/ai-model/prompt/order-sensitive-judge.mjs +33 -0
- package/dist/es/ai-model/prompt/playwright-generator.mjs +115 -0
- package/dist/es/ai-model/prompt/ui-tars-planning.mjs +34 -0
- package/dist/es/ai-model/prompt/util.mjs +57 -0
- package/dist/es/ai-model/prompt/yaml-generator.mjs +201 -0
- package/dist/es/ai-model/service-caller/codex-app-server.mjs +573 -0
- package/dist/es/ai-model/service-caller/image-detail.mjs +4 -0
- package/dist/es/ai-model/service-caller/index.mjs +648 -0
- package/dist/es/ai-model/service-caller/request-timeout.mjs +47 -0
- package/dist/es/ai-model/ui-tars-planning.mjs +247 -0
- package/dist/es/common.mjs +382 -0
- package/dist/es/device/device-options.mjs +0 -0
- package/dist/es/device/index.mjs +340 -0
- package/dist/es/dump/html-utils.mjs +290 -0
- package/dist/es/dump/index.mjs +3 -0
- package/dist/es/dump/screenshot-restoration.mjs +30 -0
- package/dist/es/dump/screenshot-store.mjs +125 -0
- package/dist/es/index.mjs +17 -0
- package/dist/es/report-cli.mjs +149 -0
- package/dist/es/report-generator.mjs +203 -0
- package/dist/es/report-markdown.mjs +216 -0
- package/dist/es/report.mjs +287 -0
- package/dist/es/screenshot-item.mjs +120 -0
- package/dist/es/service/index.mjs +272 -0
- package/dist/es/service/utils.mjs +13 -0
- package/dist/es/skill/index.mjs +35 -0
- package/dist/es/task-runner.mjs +261 -0
- package/dist/es/task-timing.mjs +10 -0
- package/dist/es/tree.mjs +11 -0
- package/dist/es/types.mjs +202 -0
- package/dist/es/utils.mjs +232 -0
- package/dist/es/yaml/builder.mjs +11 -0
- package/dist/es/yaml/index.mjs +4 -0
- package/dist/es/yaml/player.mjs +425 -0
- package/dist/es/yaml/utils.mjs +100 -0
- package/dist/es/yaml.mjs +0 -0
- package/dist/lib/agent/agent.js +815 -0
- package/dist/lib/agent/common.js +5 -0
- package/dist/lib/agent/execution-session.js +73 -0
- package/dist/lib/agent/index.js +76 -0
- package/dist/lib/agent/task-builder.js +380 -0
- package/dist/lib/agent/task-cache.js +264 -0
- package/dist/lib/agent/tasks.js +471 -0
- package/dist/lib/agent/ui-utils.js +153 -0
- package/dist/lib/agent/utils.js +238 -0
- package/dist/lib/ai-model/auto-glm/actions.js +271 -0
- package/dist/lib/ai-model/auto-glm/index.js +64 -0
- package/dist/lib/ai-model/auto-glm/parser.js +280 -0
- package/dist/lib/ai-model/auto-glm/planning.js +103 -0
- package/dist/lib/ai-model/auto-glm/prompt.js +257 -0
- package/dist/lib/ai-model/auto-glm/util.js +44 -0
- package/dist/lib/ai-model/connectivity.js +180 -0
- package/dist/lib/ai-model/conversation-history.js +227 -0
- package/dist/lib/ai-model/index.js +127 -0
- package/dist/lib/ai-model/inspect.js +441 -0
- package/dist/lib/ai-model/llm-planning.js +268 -0
- package/dist/lib/ai-model/prompt/common.js +39 -0
- package/dist/lib/ai-model/prompt/describe.js +98 -0
- package/dist/lib/ai-model/prompt/extraction.js +169 -0
- package/dist/lib/ai-model/prompt/llm-locator.js +86 -0
- package/dist/lib/ai-model/prompt/llm-planning.js +621 -0
- package/dist/lib/ai-model/prompt/llm-section-locator.js +79 -0
- package/dist/lib/ai-model/prompt/order-sensitive-judge.js +70 -0
- package/dist/lib/ai-model/prompt/playwright-generator.js +176 -0
- package/dist/lib/ai-model/prompt/ui-tars-planning.js +71 -0
- package/dist/lib/ai-model/prompt/util.js +103 -0
- package/dist/lib/ai-model/prompt/yaml-generator.js +262 -0
- package/dist/lib/ai-model/service-caller/codex-app-server.js +622 -0
- package/dist/lib/ai-model/service-caller/image-detail.js +38 -0
- package/dist/lib/ai-model/service-caller/index.js +716 -0
- package/dist/lib/ai-model/service-caller/request-timeout.js +93 -0
- package/dist/lib/ai-model/ui-tars-planning.js +281 -0
- package/dist/lib/common.js +491 -0
- package/dist/lib/device/device-options.js +18 -0
- package/dist/lib/device/index.js +467 -0
- package/dist/lib/dump/html-utils.js +366 -0
- package/dist/lib/dump/index.js +58 -0
- package/dist/lib/dump/screenshot-restoration.js +64 -0
- package/dist/lib/dump/screenshot-store.js +165 -0
- package/dist/lib/index.js +184 -0
- package/dist/lib/report-cli.js +189 -0
- package/dist/lib/report-generator.js +244 -0
- package/dist/lib/report-markdown.js +253 -0
- package/dist/lib/report.js +333 -0
- package/dist/lib/screenshot-item.js +154 -0
- package/dist/lib/service/index.js +306 -0
- package/dist/lib/service/utils.js +47 -0
- package/dist/lib/skill/index.js +69 -0
- package/dist/lib/task-runner.js +298 -0
- package/dist/lib/task-timing.js +44 -0
- package/dist/lib/tree.js +51 -0
- package/dist/lib/types.js +298 -0
- package/dist/lib/utils.js +314 -0
- package/dist/lib/yaml/builder.js +55 -0
- package/dist/lib/yaml/index.js +79 -0
- package/dist/lib/yaml/player.js +459 -0
- package/dist/lib/yaml/utils.js +153 -0
- package/dist/lib/yaml.js +18 -0
- package/dist/types/agent/agent.d.ts +220 -0
- package/dist/types/agent/common.d.ts +0 -0
- package/dist/types/agent/execution-session.d.ts +36 -0
- package/dist/types/agent/index.d.ts +9 -0
- package/dist/types/agent/task-builder.d.ts +34 -0
- package/dist/types/agent/task-cache.d.ts +49 -0
- package/dist/types/agent/tasks.d.ts +70 -0
- package/dist/types/agent/ui-utils.d.ts +14 -0
- package/dist/types/agent/utils.d.ts +25 -0
- package/dist/types/ai-model/auto-glm/actions.d.ts +78 -0
- package/dist/types/ai-model/auto-glm/index.d.ts +6 -0
- package/dist/types/ai-model/auto-glm/parser.d.ts +18 -0
- package/dist/types/ai-model/auto-glm/planning.d.ts +12 -0
- package/dist/types/ai-model/auto-glm/prompt.d.ts +27 -0
- package/dist/types/ai-model/auto-glm/util.d.ts +13 -0
- package/dist/types/ai-model/connectivity.d.ts +20 -0
- package/dist/types/ai-model/conversation-history.d.ts +105 -0
- package/dist/types/ai-model/index.d.ts +16 -0
- package/dist/types/ai-model/inspect.d.ts +67 -0
- package/dist/types/ai-model/llm-planning.d.ts +19 -0
- package/dist/types/ai-model/prompt/common.d.ts +2 -0
- package/dist/types/ai-model/prompt/describe.d.ts +1 -0
- package/dist/types/ai-model/prompt/extraction.d.ts +7 -0
- package/dist/types/ai-model/prompt/llm-locator.d.ts +3 -0
- package/dist/types/ai-model/prompt/llm-planning.d.ts +10 -0
- package/dist/types/ai-model/prompt/llm-section-locator.d.ts +3 -0
- package/dist/types/ai-model/prompt/order-sensitive-judge.d.ts +2 -0
- package/dist/types/ai-model/prompt/playwright-generator.d.ts +26 -0
- package/dist/types/ai-model/prompt/ui-tars-planning.d.ts +2 -0
- package/dist/types/ai-model/prompt/util.d.ts +33 -0
- package/dist/types/ai-model/prompt/yaml-generator.d.ts +102 -0
- package/dist/types/ai-model/service-caller/codex-app-server.d.ts +42 -0
- package/dist/types/ai-model/service-caller/image-detail.d.ts +2 -0
- package/dist/types/ai-model/service-caller/index.d.ts +60 -0
- package/dist/types/ai-model/service-caller/request-timeout.d.ts +32 -0
- package/dist/types/ai-model/ui-tars-planning.d.ts +72 -0
- package/dist/types/common.d.ts +288 -0
- package/dist/types/device/device-options.d.ts +155 -0
- package/dist/types/device/index.d.ts +2565 -0
- package/dist/types/dump/html-utils.d.ts +75 -0
- package/dist/types/dump/index.d.ts +5 -0
- package/dist/types/dump/screenshot-restoration.d.ts +8 -0
- package/dist/types/dump/screenshot-store.d.ts +49 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/report-cli.d.ts +36 -0
- package/dist/types/report-generator.d.ts +88 -0
- package/dist/types/report-markdown.d.ts +24 -0
- package/dist/types/report.d.ts +52 -0
- package/dist/types/screenshot-item.d.ts +67 -0
- package/dist/types/service/index.d.ts +24 -0
- package/dist/types/service/utils.d.ts +2 -0
- package/dist/types/skill/index.d.ts +25 -0
- package/dist/types/task-runner.d.ts +50 -0
- package/dist/types/task-timing.d.ts +8 -0
- package/dist/types/tree.d.ts +4 -0
- package/dist/types/types.d.ts +684 -0
- package/dist/types/utils.d.ts +45 -0
- package/dist/types/yaml/builder.d.ts +2 -0
- package/dist/types/yaml/index.d.ts +4 -0
- package/dist/types/yaml/player.d.ts +34 -0
- package/dist/types/yaml/utils.d.ts +9 -0
- package/dist/types/yaml.d.ts +215 -0
- 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 };
|