@empiricalrun/test-gen 0.52.1 → 0.52.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/CHANGELOG.md +29 -0
- package/dist/agent/browsing/run.d.ts +3 -1
- package/dist/agent/browsing/run.d.ts.map +1 -1
- package/dist/agent/browsing/run.js +11 -6
- package/dist/agent/chat/index.d.ts +3 -2
- package/dist/agent/chat/index.d.ts.map +1 -1
- package/dist/agent/chat/index.js +15 -18
- package/dist/agent/chat/prompt.js +2 -2
- package/dist/agent/cua/computer.d.ts +4 -1
- package/dist/agent/cua/computer.d.ts.map +1 -1
- package/dist/agent/cua/computer.js +12 -2
- package/dist/agent/cua/index.d.ts +1 -3
- package/dist/agent/cua/index.d.ts.map +1 -1
- package/dist/agent/cua/index.js +75 -20
- package/dist/agent/cua/model.d.ts.map +1 -1
- package/dist/agent/cua/model.js +5 -2
- package/dist/bin/index.js +17 -4
- package/dist/bin/utils/index.d.ts +2 -1
- package/dist/bin/utils/index.d.ts.map +1 -1
- package/dist/file/client.d.ts +5 -4
- package/dist/file/client.d.ts.map +1 -1
- package/dist/file/client.js +10 -17
- package/dist/file/server.d.ts +6 -2
- package/dist/file/server.d.ts.map +1 -1
- package/dist/file/server.js +15 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/tools/codegen-agent.d.ts +1 -1
- package/dist/tools/codegen-agent.d.ts.map +1 -1
- package/dist/tools/diagnosis-fetcher.d.ts +1 -1
- package/dist/tools/diagnosis-fetcher.d.ts.map +1 -1
- package/dist/tools/grep.d.ts +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/test-gen-browser.d.ts +1 -1
- package/dist/tools/test-gen-browser.d.ts.map +1 -1
- package/dist/tools/test-gen-browser.js +23 -21
- package/dist/tools/test-run-fetcher/index.d.ts +1 -1
- package/dist/tools/test-run-fetcher/index.d.ts.map +1 -1
- package/dist/tools/test-run.d.ts +1 -1
- package/dist/tools/test-run.d.ts.map +1 -1
- package/dist/tools/test-run.js +1 -2
- package/package.json +2 -2
- package/dist/tools/types.d.ts +0 -38
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js +0 -12
- package/dist/tools/zod-schema.d.ts +0 -19
- package/dist/tools/zod-schema.d.ts.map +0 -1
- package/dist/tools/zod-schema.js +0 -95
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @empiricalrun/test-gen
|
|
2
2
|
|
|
3
|
+
## 0.52.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6a19298: feat: changed gemini-2.5pro from exp to preview
|
|
8
|
+
- cbe5823: fix: removed headed default from runTest tool schema and added function in role of toolRes Gemini
|
|
9
|
+
- Updated dependencies [f4f4c5d]
|
|
10
|
+
- Updated dependencies [6a19298]
|
|
11
|
+
- Updated dependencies [cbe5823]
|
|
12
|
+
- @empiricalrun/llm@0.13.1
|
|
13
|
+
|
|
14
|
+
## 0.52.2
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- c490603: feat: input initial prompt with markdown file
|
|
19
|
+
- 68640d2: feat: handover from test-gen tool to chat agent with a summary message
|
|
20
|
+
- ae91e37: fix: cap cua iterations, add tracing and improve logging
|
|
21
|
+
- 0704b28: feat: zod schema for str_replace_editor for gemini to use this tool
|
|
22
|
+
- 02a2439: feat: summarize actions done by cua and rename fileservice
|
|
23
|
+
- 01fa143: feat: custom tool grep added for gemini
|
|
24
|
+
- Updated dependencies [c490603]
|
|
25
|
+
- Updated dependencies [486264f]
|
|
26
|
+
- Updated dependencies [ae91e37]
|
|
27
|
+
- Updated dependencies [0704b28]
|
|
28
|
+
- Updated dependencies [3ed20a3]
|
|
29
|
+
- Updated dependencies [01fa143]
|
|
30
|
+
- @empiricalrun/llm@0.13.0
|
|
31
|
+
|
|
3
32
|
## 0.52.1
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
|
@@ -4,10 +4,12 @@ type GenerateTestsType = {
|
|
|
4
4
|
pwProjectsFilter: string[];
|
|
5
5
|
testGenToken: string;
|
|
6
6
|
repoDir: string;
|
|
7
|
+
editFileWithGeneratedCode: boolean;
|
|
7
8
|
};
|
|
8
|
-
export declare function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, pwProjectsFilter, testGenToken, repoDir, }: GenerateTestsType): Promise<{
|
|
9
|
+
export declare function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, pwProjectsFilter, testGenToken, repoDir, editFileWithGeneratedCode, }: GenerateTestsType): Promise<{
|
|
9
10
|
isError: boolean;
|
|
10
11
|
error: string;
|
|
12
|
+
actionsSummary?: string;
|
|
11
13
|
}>;
|
|
12
14
|
export {};
|
|
13
15
|
//# sourceMappingURL=run.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAiBA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAiBA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,yBAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC,CAqFD"}
|
|
@@ -10,7 +10,7 @@ const web_1 = require("../../bin/utils/platform/web");
|
|
|
10
10
|
const server_1 = require("../../file/server");
|
|
11
11
|
const exec_1 = require("../../utils/exec");
|
|
12
12
|
const utils_1 = require("./utils");
|
|
13
|
-
async function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, pwProjectsFilter, testGenToken, repoDir, }) {
|
|
13
|
+
async function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, pwProjectsFilter, testGenToken, repoDir, editFileWithGeneratedCode, }) {
|
|
14
14
|
if (!fs_extra_1.default.existsSync(testFilePath)) {
|
|
15
15
|
throw new Error(`File for master agent to run not found: ${testFilePath}`);
|
|
16
16
|
}
|
|
@@ -18,9 +18,13 @@ async function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, p
|
|
|
18
18
|
const port = await (0, detect_port_1.default)(3030);
|
|
19
19
|
// start a file service to handle file updates from agent
|
|
20
20
|
// - also update the file path with updates when agent is done spitting out code
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const fileServer = new server_1.FileServiceServer({
|
|
22
|
+
port,
|
|
23
|
+
repoDir,
|
|
24
|
+
updateFile: editFileWithGeneratedCode,
|
|
25
|
+
});
|
|
26
|
+
await fileServer.startFileService();
|
|
27
|
+
fileServer.setFilePath(filePathToUpdate);
|
|
24
28
|
// read playwright config from ./playwright.config.ts of source repo
|
|
25
29
|
const playwrightConfig = await (0, utils_1.readPlaywrightConfig)(repoDir);
|
|
26
30
|
// detect the playwright project name for the given test file and playwright config
|
|
@@ -42,7 +46,7 @@ async function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, p
|
|
|
42
46
|
}
|
|
43
47
|
await (0, exec_1.cmd)(command.split(" "), {
|
|
44
48
|
env: {
|
|
45
|
-
|
|
49
|
+
IPC_FILE_SERVICE_PORT: port.toString(),
|
|
46
50
|
PW_TEST_HTML_REPORT_OPEN: "never",
|
|
47
51
|
// pass the test gen token so that the agent has the same configuration as cli
|
|
48
52
|
TEST_GEN_TOKEN: testGenToken,
|
|
@@ -75,10 +79,11 @@ async function generateTestsUsingMasterAgent({ testFilePath, filePathToUpdate, p
|
|
|
75
79
|
}
|
|
76
80
|
// remove the test only from the file
|
|
77
81
|
await (0, web_1.removeTestOnly)(testFilePath);
|
|
78
|
-
await
|
|
82
|
+
await fileServer.stop();
|
|
79
83
|
return {
|
|
80
84
|
isError,
|
|
81
85
|
error,
|
|
86
|
+
actionsSummary: fileServer.getActionsSummary(),
|
|
82
87
|
};
|
|
83
88
|
}
|
|
84
89
|
exports.generateTestsUsingMasterAgent = generateTestsUsingMasterAgent;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export declare function chatAgent({ selectedModel, useDiskForChatState, }: {
|
|
2
|
-
selectedModel?: "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-
|
|
1
|
+
export declare function chatAgent({ selectedModel, useDiskForChatState, initialPromptContent, }: {
|
|
2
|
+
selectedModel?: "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-preview-03-25";
|
|
3
3
|
useDiskForChatState?: boolean;
|
|
4
|
+
initialPromptContent?: string;
|
|
4
5
|
}): Promise<string>;
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAgEA,wBAAsB,SAAS,CAAC,EAC9B,aAA4C,EAC5C,mBAA2B,EAC3B,oBAAoB,GACrB,EAAE;IACD,aAAa,CAAC,EACV,4BAA4B,GAC5B,4BAA4B,GAC5B,8BAA8B,CAAC;IACnC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,mBAsFA"}
|
package/dist/agent/chat/index.js
CHANGED
|
@@ -10,8 +10,8 @@ const grep_1 = require("../../tools/grep");
|
|
|
10
10
|
const test_gen_browser_1 = require("../../tools/test-gen-browser");
|
|
11
11
|
const test_run_1 = require("../../tools/test-run");
|
|
12
12
|
const test_run_fetcher_1 = require("../../tools/test-run-fetcher");
|
|
13
|
-
const zod_schema_1 = require("../../tools/zod-schema");
|
|
14
13
|
const prompt_1 = require("./prompt");
|
|
14
|
+
// TODO: Add strReplaceEditor for non-Claude models
|
|
15
15
|
const tools = [
|
|
16
16
|
test_run_1.runTestTool,
|
|
17
17
|
test_gen_browser_1.generateTestWithBrowserAgent,
|
|
@@ -21,7 +21,7 @@ const tools = [
|
|
|
21
21
|
];
|
|
22
22
|
const toolExecutors = {
|
|
23
23
|
...Object.fromEntries(tools.map((tool) => [tool.schema.name, tool.execute])),
|
|
24
|
-
str_replace_editor: (input) => (0, chat_1.
|
|
24
|
+
str_replace_editor: (input) => (0, chat_1.strReplaceEditorExecutor)(input, web_1.validateTypescript),
|
|
25
25
|
};
|
|
26
26
|
function createChatModel(useDiskForChatState, selectedModel) {
|
|
27
27
|
if (selectedModel.startsWith("claude")) {
|
|
@@ -43,9 +43,16 @@ function concludeAgent(usageSummary) {
|
|
|
43
43
|
console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + usageSummary)}`);
|
|
44
44
|
(0, chat_1.cleanupBackupFiles)(process.cwd());
|
|
45
45
|
}
|
|
46
|
-
async function chatAgent({ selectedModel = "claude-3-7-sonnet-20250219", useDiskForChatState = false, }) {
|
|
46
|
+
async function chatAgent({ selectedModel = "claude-3-7-sonnet-20250219", useDiskForChatState = false, initialPromptContent, }) {
|
|
47
47
|
let chatModel = createChatModel(useDiskForChatState, selectedModel);
|
|
48
48
|
let userPrompt = undefined;
|
|
49
|
+
if (initialPromptContent && chatModel.messages.length === 0) {
|
|
50
|
+
chatModel.pushUserMessage(initialPromptContent);
|
|
51
|
+
chatModel.askUserForInput = false;
|
|
52
|
+
}
|
|
53
|
+
else if (initialPromptContent && chatModel.messages.length > 0) {
|
|
54
|
+
console.warn(`Ignoring initial prompt because we have existing messages.`);
|
|
55
|
+
}
|
|
49
56
|
const handleSigInt = () => {
|
|
50
57
|
concludeAgent(chatModel.getUsageSummary());
|
|
51
58
|
process.exit(0);
|
|
@@ -81,35 +88,25 @@ async function chatAgent({ selectedModel = "claude-3-7-sonnet-20250219", useDisk
|
|
|
81
88
|
}
|
|
82
89
|
const toolUse = chatModel.getPendingToolCall();
|
|
83
90
|
if (toolUse) {
|
|
84
|
-
|
|
91
|
+
console.log(`Executing tool ${toolUse.name} with args: ${JSON.stringify(toolUse.input)}`);
|
|
85
92
|
const toolExecutor = toolExecutors[toolUse.name];
|
|
86
93
|
if (!toolExecutor) {
|
|
87
94
|
throw new Error(`Tool ${toolUse.name} not found`);
|
|
88
95
|
}
|
|
89
96
|
const toolResult = await toolExecutor(toolUse.input);
|
|
90
97
|
if (toolResult.isError) {
|
|
91
|
-
|
|
98
|
+
ora(`Tool ${toolUse.name} failed: ${toolResult.result}`).fail();
|
|
92
99
|
}
|
|
93
100
|
else {
|
|
94
|
-
|
|
101
|
+
ora(`Tool ${toolUse.name} completed`).succeed();
|
|
95
102
|
}
|
|
96
|
-
chatModel.
|
|
97
|
-
role: "user",
|
|
98
|
-
content: [
|
|
99
|
-
{
|
|
100
|
-
type: "tool_result",
|
|
101
|
-
tool_use_id: toolUse.id,
|
|
102
|
-
content: toolResult.result,
|
|
103
|
-
is_error: toolResult.isError,
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
});
|
|
103
|
+
chatModel.pushToolResultMessage(toolUse, toolResult);
|
|
107
104
|
continue;
|
|
108
105
|
}
|
|
109
106
|
const spinner = ora(`${getModelName(selectedModel)} is working...`).start();
|
|
110
107
|
const response = await chatModel.getLLMResponse({
|
|
111
108
|
systemPrompt,
|
|
112
|
-
tools: tools.map((tool) => (0,
|
|
109
|
+
tools: tools.map((tool) => (0, chat_1.zodToOpenAITool)(tool.schema)),
|
|
113
110
|
selectedModel,
|
|
114
111
|
});
|
|
115
112
|
spinner.stop();
|
|
@@ -41,8 +41,8 @@ Or if the user asks you to modify a test, you could use the generateTestWithBrow
|
|
|
41
41
|
that a UI selector needs to be updated, using the browser agent is a good idea.
|
|
42
42
|
|
|
43
43
|
Before using generateTestWithBrowserAgent, you need to prepare the test code for the browser agent.
|
|
44
|
-
You can do this by using the
|
|
45
|
-
comment
|
|
44
|
+
You can do this by using the strReplaceEditor or the text editor tool to add a TODO comment to the test
|
|
45
|
+
code. This comment explains to the browser agent what it needs to do.
|
|
46
46
|
|
|
47
47
|
For example, if the expected modification is to click on a login button, you could add the following comment.
|
|
48
48
|
|
|
@@ -2,6 +2,9 @@ import { ResponseComputerToolCall } from "openai/resources/responses/responses.m
|
|
|
2
2
|
import type { Page } from "playwright";
|
|
3
3
|
type ComputerAction = ResponseComputerToolCall.Click | ResponseComputerToolCall.DoubleClick | ResponseComputerToolCall.Drag | ResponseComputerToolCall.Keypress | ResponseComputerToolCall.Move | ResponseComputerToolCall.Screenshot | ResponseComputerToolCall.Scroll | ResponseComputerToolCall.Type | ResponseComputerToolCall.Wait;
|
|
4
4
|
export declare function getScreenshot(page: Page): Promise<string>;
|
|
5
|
-
export declare function handleModelAction(page: Page, action: ComputerAction): Promise<
|
|
5
|
+
export declare function handleModelAction(page: Page, action: ComputerAction): Promise<{
|
|
6
|
+
actionSummary: string;
|
|
7
|
+
actionCode: string;
|
|
8
|
+
}>;
|
|
6
9
|
export {};
|
|
7
10
|
//# sourceMappingURL=computer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computer.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/computer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,KAAK,cAAc,GACf,wBAAwB,CAAC,KAAK,GAC9B,wBAAwB,CAAC,WAAW,GACpC,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,QAAQ,GACjC,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,UAAU,GACnC,wBAAwB,CAAC,MAAM,GAC/B,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,IAAI,CAAC;AAElC,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,mBAG7C;AAgCD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"computer.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/computer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,KAAK,cAAc,GACf,wBAAwB,CAAC,KAAK,GAC9B,wBAAwB,CAAC,WAAW,GACpC,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,QAAQ,GACjC,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,UAAU,GACnC,wBAAwB,CAAC,MAAM,GAC/B,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,IAAI,CAAC;AAElC,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,mBAG7C;AAgCD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;IACT,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CAqID"}
|
|
@@ -38,11 +38,13 @@ const CUA_KEY_TO_PLAYWRIGHT_KEY = {
|
|
|
38
38
|
async function handleModelAction(page, action) {
|
|
39
39
|
const actionType = action.type;
|
|
40
40
|
let actionCode = "";
|
|
41
|
+
let actionSummary = "";
|
|
41
42
|
try {
|
|
42
43
|
switch (actionType) {
|
|
43
44
|
case "click": {
|
|
44
45
|
const { x, y, button = "left" } = action;
|
|
45
46
|
console.log(`Action: click at (${x}, ${y}) with button '${button}'`);
|
|
47
|
+
actionSummary = `Click at (${x}, ${y}) with button '${button}'`;
|
|
46
48
|
let pwButton = undefined;
|
|
47
49
|
if (button === "left" || button === "right") {
|
|
48
50
|
pwButton = button;
|
|
@@ -72,18 +74,21 @@ async function handleModelAction(page, action) {
|
|
|
72
74
|
case "double_click": {
|
|
73
75
|
const { x, y } = action;
|
|
74
76
|
console.log(`Action: doubleclick at (${x}, ${y})`);
|
|
77
|
+
actionSummary = `Double click at (${x}, ${y})`;
|
|
75
78
|
await page.mouse.dblclick(x, y, { button: "left" });
|
|
76
79
|
break;
|
|
77
80
|
}
|
|
78
81
|
case "move": {
|
|
79
82
|
const { x, y } = action;
|
|
80
|
-
console.log(`Action: move to (${x}, ${y})`);
|
|
83
|
+
console.log(`Action: mouse move to (${x}, ${y})`);
|
|
84
|
+
actionSummary = `Mouse move to (${x}, ${y})`;
|
|
81
85
|
await page.mouse.move(x, y);
|
|
82
86
|
break;
|
|
83
87
|
}
|
|
84
88
|
case "drag": {
|
|
85
89
|
const { path } = action;
|
|
86
90
|
console.log(`Action: drag along path ${path}`);
|
|
91
|
+
actionSummary = `Drag along path ${path}`;
|
|
87
92
|
if (!path || path.length === 0) {
|
|
88
93
|
break;
|
|
89
94
|
}
|
|
@@ -98,6 +103,7 @@ async function handleModelAction(page, action) {
|
|
|
98
103
|
case "scroll": {
|
|
99
104
|
const { x, y, scroll_x, scroll_y } = action;
|
|
100
105
|
console.log(`Action: scroll at (${x}, ${y}) with offsets (scroll_x=${scroll_x}, scroll_y=${scroll_y})`);
|
|
106
|
+
actionSummary = `Scroll at (${x}, ${y}) with offsets (scroll_x=${scroll_x}, scroll_y=${scroll_y})`;
|
|
101
107
|
await page.mouse.move(x, y);
|
|
102
108
|
await page.evaluate(`window.scrollBy(${scroll_x}, ${scroll_y})`);
|
|
103
109
|
break;
|
|
@@ -109,6 +115,7 @@ async function handleModelAction(page, action) {
|
|
|
109
115
|
});
|
|
110
116
|
const mappedKey = mappedKeys.join("+"); // ["CTRL", "A"] becomes ControlOrMeta+A
|
|
111
117
|
console.log(`Action: keypress for keys ${keys} -> '${mappedKey}'`);
|
|
118
|
+
actionSummary = `Keypress for keys ${keys} (mapped to '${mappedKey}' for Playwright)`;
|
|
112
119
|
try {
|
|
113
120
|
await page.keyboard.press(mappedKey);
|
|
114
121
|
actionCode = `await page.keyboard.press('${mappedKey}');\n`;
|
|
@@ -121,6 +128,7 @@ async function handleModelAction(page, action) {
|
|
|
121
128
|
case "type": {
|
|
122
129
|
const { text } = action;
|
|
123
130
|
console.log(`Action: type text '${text}'`);
|
|
131
|
+
actionSummary = `Type text '${text}'`;
|
|
124
132
|
await page.keyboard.type(text);
|
|
125
133
|
const locator = await page.evaluate(() => {
|
|
126
134
|
const element = document.activeElement;
|
|
@@ -131,12 +139,14 @@ async function handleModelAction(page, action) {
|
|
|
131
139
|
}
|
|
132
140
|
case "wait": {
|
|
133
141
|
console.log(`Action: wait`);
|
|
142
|
+
actionSummary = `Wait for 2 seconds`;
|
|
134
143
|
await page.waitForTimeout(2000);
|
|
135
144
|
break;
|
|
136
145
|
}
|
|
137
146
|
case "screenshot": {
|
|
138
147
|
// Nothing to do as screenshot is taken at each turn
|
|
139
148
|
console.log(`Action: screenshot`);
|
|
149
|
+
actionSummary = `Screenshot`;
|
|
140
150
|
break;
|
|
141
151
|
}
|
|
142
152
|
default:
|
|
@@ -146,6 +156,6 @@ async function handleModelAction(page, action) {
|
|
|
146
156
|
catch (e) {
|
|
147
157
|
console.error("Error handling action", action, ":", e);
|
|
148
158
|
}
|
|
149
|
-
return actionCode;
|
|
159
|
+
return { actionSummary, actionCode };
|
|
150
160
|
}
|
|
151
161
|
exports.handleModelAction = handleModelAction;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { Page } from "playwright";
|
|
2
2
|
export declare function startPlaywrightCodegen(page: Page): Promise<void>;
|
|
3
|
-
/**
|
|
4
|
-
* Run the loop that executes computer actions until no 'computer_call' is found.
|
|
5
|
-
*/
|
|
6
3
|
export declare function createTestUsingComputerUseAgent({ page, task, }: {
|
|
7
4
|
page: Page;
|
|
8
5
|
task: string;
|
|
9
6
|
}): Promise<{
|
|
10
7
|
code: string;
|
|
11
8
|
importPaths: string[];
|
|
9
|
+
actionsSummary: string;
|
|
12
10
|
}>;
|
|
13
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAOlC,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,IAAI,iBAoBtD;AAED,wBAAsB,+BAA+B,CAAC,EACpD,IAAI,EACJ,IAAI,GACL,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CA2JD"}
|
package/dist/agent/cua/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.createTestUsingComputerUseAgent = exports.startPlaywrightCodegen = void 0;
|
|
7
|
+
const llm_1 = require("@empiricalrun/llm");
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
const logger_1 = require("../../bin/logger");
|
|
4
10
|
const utils_1 = require("../browsing/utils");
|
|
5
11
|
const computer_1 = require("./computer");
|
|
6
12
|
const model_1 = require("./model");
|
|
@@ -26,16 +32,25 @@ async function startPlaywrightCodegen(page) {
|
|
|
26
32
|
await page.pause();
|
|
27
33
|
}
|
|
28
34
|
exports.startPlaywrightCodegen = startPlaywrightCodegen;
|
|
29
|
-
/**
|
|
30
|
-
* Run the loop that executes computer actions until no 'computer_call' is found.
|
|
31
|
-
*/
|
|
32
35
|
async function createTestUsingComputerUseAgent({ page, task, }) {
|
|
33
|
-
let generatedCode = "";
|
|
34
36
|
await (0, utils_1.injectPwLocatorGenerator)(page);
|
|
35
37
|
const screenshotBytes = await (0, computer_1.getScreenshot)(page);
|
|
36
38
|
const viewport = page.viewportSize();
|
|
37
39
|
let screenWidth = viewport?.width || 1280;
|
|
38
40
|
let screenHeight = viewport?.height || 720;
|
|
41
|
+
const logger = new logger_1.CustomLogger({ useReporter: false });
|
|
42
|
+
const trace = llm_1.langfuseInstance?.trace({
|
|
43
|
+
name: "computer-use-agent",
|
|
44
|
+
id: crypto_1.default.randomUUID(),
|
|
45
|
+
input: { task },
|
|
46
|
+
});
|
|
47
|
+
if (trace) {
|
|
48
|
+
const traceUrl = trace.getTraceUrl();
|
|
49
|
+
logger.log(`Starting computer use agent: ${traceUrl}`);
|
|
50
|
+
}
|
|
51
|
+
const span = trace?.span({
|
|
52
|
+
name: "initial-model-call",
|
|
53
|
+
});
|
|
39
54
|
let response = await (0, model_1.callComputerUseModel)({
|
|
40
55
|
input: [
|
|
41
56
|
{
|
|
@@ -56,32 +71,63 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
|
|
|
56
71
|
screenWidth,
|
|
57
72
|
screenHeight,
|
|
58
73
|
});
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
span?.end({ output: response });
|
|
75
|
+
let isTaskDone = false;
|
|
76
|
+
let maxIterations = 15;
|
|
77
|
+
let generatedCode = "";
|
|
78
|
+
let actionsSummary = [];
|
|
79
|
+
let iterationIndex = 0;
|
|
80
|
+
while (!isTaskDone && iterationIndex < maxIterations) {
|
|
81
|
+
actionsSummary.push(`\n# Agent iteration ${iterationIndex}`);
|
|
82
|
+
iterationIndex++;
|
|
83
|
+
const iterationSpan = trace?.span({
|
|
84
|
+
name: `iteration-${iterationIndex}`,
|
|
85
|
+
input: { response },
|
|
86
|
+
});
|
|
61
87
|
const computerCalls = response.output.filter((item) => item.type === "computer_call");
|
|
62
88
|
if (computerCalls.length === 0) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
89
|
+
const assistantOutput = response.output.find((item) => item.type === "message");
|
|
90
|
+
if (assistantOutput) {
|
|
91
|
+
const content = assistantOutput.content.find((item) => item.type === "output_text");
|
|
92
|
+
if (content && "text" in content) {
|
|
93
|
+
// TODO: This ignores `ResponseOutputRefusal` type (refusal from assistant)
|
|
94
|
+
actionsSummary.push(`Agent summary: ${content.text}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
isTaskDone = true;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const reasoning = response.output.find(() => (item) => item.type === "reasoning");
|
|
101
|
+
if (reasoning) {
|
|
102
|
+
const reasoningItem = reasoning;
|
|
103
|
+
const summaryText = reasoningItem.summary?.find((item) => item.type === "summary_text")?.text;
|
|
104
|
+
if (summaryText) {
|
|
105
|
+
actionsSummary.push(`Action reasoning: ${summaryText}`);
|
|
106
|
+
}
|
|
72
107
|
}
|
|
73
108
|
// We expect at most one computer call per response.
|
|
74
109
|
const computerCall = computerCalls[0];
|
|
75
110
|
const lastCallId = computerCall.call_id;
|
|
76
111
|
const action = computerCall.action;
|
|
77
112
|
const pendingSafetyChecks = computerCall.pending_safety_checks;
|
|
78
|
-
// Execute the action
|
|
79
|
-
const actionCode = await (0, computer_1.handleModelAction)(page, action);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
113
|
+
// Execute the action and take a screenshot
|
|
114
|
+
const { actionSummary, actionCode } = await (0, computer_1.handleModelAction)(page, action);
|
|
115
|
+
actionsSummary.push(`Action executed: ${actionSummary}`);
|
|
116
|
+
if (actionCode) {
|
|
117
|
+
actionsSummary.push(`Generated code: ${actionCode}`);
|
|
118
|
+
generatedCode += actionCode;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
actionsSummary.push(`No code generated: Will rely on Playwright's ability to auto-wait or auto-scroll`);
|
|
122
|
+
}
|
|
123
|
+
// Allow time for changes to take effect.
|
|
124
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
83
125
|
const screenshotBytes = await (0, computer_1.getScreenshot)(page);
|
|
84
126
|
// Send the screenshot back as a computer_call_output
|
|
127
|
+
const computerCallSpan = iterationSpan?.span({
|
|
128
|
+
name: "computer-call-output",
|
|
129
|
+
input: { lastCallId, acknowledged_safety_checks: pendingSafetyChecks },
|
|
130
|
+
});
|
|
85
131
|
response = await (0, model_1.callComputerUseModel)({
|
|
86
132
|
previousResponseId: response.id,
|
|
87
133
|
input: [
|
|
@@ -98,8 +144,17 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
|
|
|
98
144
|
screenWidth,
|
|
99
145
|
screenHeight,
|
|
100
146
|
});
|
|
147
|
+
computerCallSpan?.end({ output: response });
|
|
148
|
+
iterationSpan?.end({ output: response });
|
|
149
|
+
}
|
|
150
|
+
if (!isTaskDone) {
|
|
151
|
+
actionsSummary.push(`Max iteration limit hit: Task not done after ${maxIterations} iterations`);
|
|
101
152
|
}
|
|
153
|
+
trace?.update({
|
|
154
|
+
output: { code: generatedCode, actionsSummary: actionsSummary.join("\n") },
|
|
155
|
+
});
|
|
102
156
|
return {
|
|
157
|
+
actionsSummary: actionsSummary.join("\n"),
|
|
103
158
|
code: generatedCode,
|
|
104
159
|
// TODO: Does not support skills, so import paths are empty
|
|
105
160
|
importPaths: [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,aAAa,EACd,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,aAAa,EACd,MAAM,0CAA0C,CAAC;AAWlD,wBAAsB,oBAAoB,CAAC,EACzC,KAAK,EACL,kBAAkB,EAClB,WAAW,EACX,YAAY,GACb,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqBpB"}
|
package/dist/agent/cua/model.js
CHANGED
|
@@ -9,11 +9,14 @@ const INSTRUCTIONS = `You will be asked to execute some actions in a browser con
|
|
|
9
9
|
Don't ask the user for confirmations - just execute the actions.
|
|
10
10
|
|
|
11
11
|
For example, if the user message says "Click on Submit button", then
|
|
12
|
-
you click on the submit button -- even if it looks like a scary action
|
|
12
|
+
you click on the submit button -- even if it looks like a scary action.
|
|
13
|
+
|
|
14
|
+
If you have been asked to retrieve text or verify something on the UI, then communicate
|
|
15
|
+
that in your responses so that the user can see your thinking process in its entirety.`;
|
|
13
16
|
async function callComputerUseModel({ input, previousResponseId, screenWidth, screenHeight, }) {
|
|
14
17
|
const openai = new openai_1.default();
|
|
15
18
|
return await openai.responses.create({
|
|
16
|
-
model: "computer-use-preview",
|
|
19
|
+
model: "computer-use-preview-2025-03-11",
|
|
17
20
|
previous_response_id: previousResponseId,
|
|
18
21
|
tools: [
|
|
19
22
|
{
|
package/dist/bin/index.js
CHANGED
|
@@ -35,20 +35,31 @@ function setupProcessListeners(cleanup) {
|
|
|
35
35
|
events.forEach((event) => process.removeListener(event, cleanup));
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
async function runChatAgent(modelInput, useDiskForChatState) {
|
|
38
|
+
async function runChatAgent(modelInput, useDiskForChatState, initialPromptPath) {
|
|
39
39
|
const MODEL_MAPPING = {
|
|
40
40
|
"claude-3-7": "claude-3-7-sonnet-20250219",
|
|
41
41
|
"3-7": "claude-3-7-sonnet-20250219",
|
|
42
42
|
"claude-3-5": "claude-3-5-sonnet-20241022",
|
|
43
43
|
"3-5": "claude-3-5-sonnet-20241022",
|
|
44
|
-
"gemini-2.5-pro-
|
|
44
|
+
"gemini-2.5-pro-preview-03-25": "gemini-2.5-pro-preview-03-25",
|
|
45
45
|
};
|
|
46
46
|
if (modelInput && !MODEL_MAPPING[modelInput]) {
|
|
47
47
|
throw new Error(`Invalid chat model: ${modelInput}`);
|
|
48
48
|
}
|
|
49
|
+
let initialPromptContent = undefined;
|
|
50
|
+
if (initialPromptPath) {
|
|
51
|
+
try {
|
|
52
|
+
const fs = await import("fs");
|
|
53
|
+
initialPromptContent = fs.readFileSync(initialPromptPath, "utf-8");
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new Error(`Failed to read initial prompt file at ${initialPromptPath}: ${error.message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
49
59
|
return await (0, chat_1.chatAgent)({
|
|
50
60
|
selectedModel: modelInput ? MODEL_MAPPING[modelInput] : undefined,
|
|
51
61
|
useDiskForChatState,
|
|
62
|
+
initialPromptContent,
|
|
52
63
|
});
|
|
53
64
|
}
|
|
54
65
|
async function runAgentsWorkflow(testGenConfig, testGenToken) {
|
|
@@ -168,6 +179,7 @@ async function runAgentsWorkflow(testGenConfig, testGenToken) {
|
|
|
168
179
|
pwProjectsFilter: testGenConfig.environment?.playwrightProjects,
|
|
169
180
|
testGenToken,
|
|
170
181
|
repoDir: process.cwd(),
|
|
182
|
+
editFileWithGeneratedCode: true,
|
|
171
183
|
});
|
|
172
184
|
if (isError) {
|
|
173
185
|
throw new Error(error);
|
|
@@ -187,7 +199,8 @@ async function runAgentsWorkflow(testGenConfig, testGenToken) {
|
|
|
187
199
|
.option("--suites <suites>", "Comma separated list of describe blocks")
|
|
188
200
|
.option("--use-chat", "Use chat agent (and not the workflow)")
|
|
189
201
|
.option("--use-disk-for-chat-state", "Save and load chat state from disk")
|
|
190
|
-
.option("--chat-model <model>", "Chat model to use (claude-3-7-sonnet-20250219 or claude-3-5-sonnet-20241022 or gemini-2.5-pro-
|
|
202
|
+
.option("--chat-model <model>", "Chat model to use (claude-3-7-sonnet-20250219 or claude-3-5-sonnet-20241022 or gemini-2.5-pro-preview-03-25)")
|
|
203
|
+
.option("--initial-prompt <path>", "Path to an initial prompt file (e.g. prompt.md)")
|
|
191
204
|
.parse(process.argv);
|
|
192
205
|
const options = program.opts();
|
|
193
206
|
const completedOptions = await (0, utils_2.validateAndCompleteCliOptions)(options);
|
|
@@ -211,7 +224,7 @@ async function runAgentsWorkflow(testGenConfig, testGenToken) {
|
|
|
211
224
|
// Download the build if repo has a download script
|
|
212
225
|
await (0, test_build_1.downloadBuild)(testGenConfig.build || {});
|
|
213
226
|
if (completedOptions.useChat) {
|
|
214
|
-
await runChatAgent(completedOptions.chatModel, completedOptions.useDiskForChatState);
|
|
227
|
+
await runChatAgent(completedOptions.chatModel, completedOptions.useDiskForChatState, completedOptions.initialPrompt);
|
|
215
228
|
return;
|
|
216
229
|
}
|
|
217
230
|
let agentUsed;
|
|
@@ -6,7 +6,8 @@ export interface CliOptions {
|
|
|
6
6
|
suites?: string;
|
|
7
7
|
useChat?: boolean;
|
|
8
8
|
useDiskForChatState?: boolean;
|
|
9
|
-
|
|
9
|
+
initialPrompt?: string;
|
|
10
|
+
chatModel?: "claude-3-7" | "3-7" | "claude-3-5" | "3-5" | "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-preview-03-25";
|
|
10
11
|
}
|
|
11
12
|
export declare function validateAndCompleteCliOptions(options: CliOptions): Promise<CliOptions>;
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EACN,YAAY,GACZ,KAAK,GACL,YAAY,GACZ,KAAK,GACL,4BAA4B,GAC5B,4BAA4B,GAC5B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EACN,YAAY,GACZ,KAAK,GACL,YAAY,GACZ,KAAK,GACL,4BAA4B,GAC5B,4BAA4B,GAC5B,8BAA8B,CAAC;CACpC;AAQD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CAyDrB"}
|
package/dist/file/client.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
declare class
|
|
1
|
+
declare class FileServiceClient {
|
|
2
2
|
baseUrl: string;
|
|
3
3
|
port: number | undefined;
|
|
4
4
|
constructor();
|
|
5
5
|
static isAvailable(): boolean;
|
|
6
|
-
updateTest({ generatedCode, task, importPaths, }: {
|
|
6
|
+
updateTest({ generatedCode, task, importPaths, actionsSummary, }: {
|
|
7
7
|
generatedCode: string;
|
|
8
8
|
task: string;
|
|
9
9
|
importPaths: string[];
|
|
10
|
-
|
|
10
|
+
actionsSummary?: string;
|
|
11
|
+
}): Promise<any>;
|
|
11
12
|
post(path: string, body: any): Promise<any>;
|
|
12
13
|
}
|
|
13
|
-
export default
|
|
14
|
+
export default FileServiceClient;
|
|
14
15
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/file/client.ts"],"names":[],"mappings":"AAAA,cAAM,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/file/client.ts"],"names":[],"mappings":"AAAA,cAAM,iBAAiB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;;IAUzB,MAAM,CAAC,WAAW;IAIZ,UAAU,CAAC,EACf,aAAa,EACb,IAAI,EACJ,WAAW,EACX,cAAc,GACf,EAAE;QACD,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB;IASK,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG;CAgBnC;AAED,eAAe,iBAAiB,CAAC"}
|
package/dist/file/client.js
CHANGED
|
@@ -1,32 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
class
|
|
3
|
+
class FileServiceClient {
|
|
4
4
|
baseUrl;
|
|
5
5
|
port;
|
|
6
6
|
constructor() {
|
|
7
|
-
const port = Number(process.env.
|
|
7
|
+
const port = Number(process.env.IPC_FILE_SERVICE_PORT);
|
|
8
8
|
if (port && !isNaN(port)) {
|
|
9
9
|
this.port = port;
|
|
10
10
|
}
|
|
11
11
|
this.baseUrl = `http://localhost:${port}`;
|
|
12
12
|
}
|
|
13
13
|
static isAvailable() {
|
|
14
|
-
return !!Number(process.env.
|
|
14
|
+
return !!Number(process.env.IPC_FILE_SERVICE_PORT);
|
|
15
15
|
}
|
|
16
|
-
async updateTest({ generatedCode, task, importPaths, }) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
body: JSON.stringify({ generatedCode, task, importPaths }),
|
|
16
|
+
async updateTest({ generatedCode, task, importPaths, actionsSummary, }) {
|
|
17
|
+
return this.post("/test", {
|
|
18
|
+
generatedCode,
|
|
19
|
+
task,
|
|
20
|
+
importPaths,
|
|
21
|
+
actionsSummary,
|
|
23
22
|
});
|
|
24
|
-
if (!resp.ok) {
|
|
25
|
-
throw new Error(resp.statusText);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
console.log("Generated and updated test successfully");
|
|
29
|
-
}
|
|
30
23
|
}
|
|
31
24
|
async post(path, body) {
|
|
32
25
|
const resp = await fetch(`${this.baseUrl}${path}`, {
|
|
@@ -45,4 +38,4 @@ class TestFileService {
|
|
|
45
38
|
}
|
|
46
39
|
}
|
|
47
40
|
}
|
|
48
|
-
exports.default =
|
|
41
|
+
exports.default = FileServiceClient;
|
package/dist/file/server.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
export declare class
|
|
1
|
+
export declare class FileServiceServer {
|
|
2
2
|
private port;
|
|
3
3
|
private filePath;
|
|
4
4
|
private repoDir;
|
|
5
5
|
private server;
|
|
6
|
-
|
|
6
|
+
private actionsSummary;
|
|
7
|
+
private updateFile;
|
|
8
|
+
constructor({ port, repoDir, updateFile, }: {
|
|
7
9
|
port: number;
|
|
8
10
|
repoDir: string;
|
|
11
|
+
updateFile: boolean;
|
|
9
12
|
});
|
|
13
|
+
getActionsSummary(): string | undefined;
|
|
10
14
|
setFilePath(filePath: string): void;
|
|
11
15
|
startFileService(): Promise<number>;
|
|
12
16
|
stop(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/file/server.ts"],"names":[],"mappings":"AAWA,qBAAa,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/file/server.ts"],"names":[],"mappings":"AAWA,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,UAAU,CAAkB;gBAExB,EACV,IAAI,EACJ,OAAO,EACP,UAAU,GACX,EAAE;QACD,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,OAAO,CAAC;KACrB;IAMD,iBAAiB;IAIjB,WAAW,CAAC,QAAQ,EAAE,MAAM;IAItB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IA+CnC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAgB5B;AAED,wBAAsB,gBAAgB,kBAAK"}
|
package/dist/file/server.js
CHANGED
|
@@ -3,20 +3,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.startFileService = exports.
|
|
6
|
+
exports.startFileService = exports.FileServiceServer = void 0;
|
|
7
7
|
const express_1 = __importDefault(require("express"));
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const web_1 = require("../bin/utils/platform/web");
|
|
11
11
|
const ipc_1 = require("../human-in-the-loop/ipc");
|
|
12
|
-
class
|
|
12
|
+
class FileServiceServer {
|
|
13
13
|
port = 0;
|
|
14
14
|
filePath = "";
|
|
15
15
|
repoDir = "";
|
|
16
16
|
server;
|
|
17
|
-
|
|
17
|
+
actionsSummary;
|
|
18
|
+
updateFile = false;
|
|
19
|
+
constructor({ port, repoDir, updateFile, }) {
|
|
18
20
|
this.port = port;
|
|
19
21
|
this.repoDir = repoDir;
|
|
22
|
+
this.updateFile = updateFile;
|
|
23
|
+
}
|
|
24
|
+
getActionsSummary() {
|
|
25
|
+
return this.actionsSummary;
|
|
20
26
|
}
|
|
21
27
|
setFilePath(filePath) {
|
|
22
28
|
this.filePath = filePath;
|
|
@@ -26,7 +32,11 @@ class FileService {
|
|
|
26
32
|
app.use(express_1.default.json());
|
|
27
33
|
(0, ipc_1.humanLoopRoute)(app);
|
|
28
34
|
app.post("/test", async (req, res) => {
|
|
29
|
-
const { generatedCode, importPaths } = req.body;
|
|
35
|
+
const { generatedCode, importPaths, actionsSummary } = req.body;
|
|
36
|
+
this.actionsSummary = actionsSummary;
|
|
37
|
+
if (!this.updateFile) {
|
|
38
|
+
return res.send({ success: true });
|
|
39
|
+
}
|
|
30
40
|
try {
|
|
31
41
|
const testFilePath = path_1.default.resolve(this.repoDir, this.filePath);
|
|
32
42
|
if (testFilePath) {
|
|
@@ -64,6 +74,6 @@ class FileService {
|
|
|
64
74
|
});
|
|
65
75
|
}
|
|
66
76
|
}
|
|
67
|
-
exports.
|
|
77
|
+
exports.FileServiceServer = FileServiceServer;
|
|
68
78
|
async function startFileService() { }
|
|
69
79
|
exports.startFileService = startFileService;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAQlC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAQlC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAqBpC,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,SAAS,iBAyD3E"}
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ async function createTest(task, page, scope) {
|
|
|
40
40
|
projectRepoName: testGenConfig.options?.metadata.projectRepoName,
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
|
-
const
|
|
43
|
+
const fileServiceClient = new client_1.default();
|
|
44
44
|
const useComputerUseAgent = testGenConfig.options?.useComputerUseAgent;
|
|
45
45
|
let agentResult;
|
|
46
46
|
if (useComputerUseAgent) {
|
|
@@ -61,11 +61,12 @@ async function createTest(task, page, scope) {
|
|
|
61
61
|
scopeVars: scope,
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
|
-
const { code, importPaths } = agentResult;
|
|
65
|
-
await
|
|
64
|
+
const { code, importPaths, actionsSummary } = agentResult;
|
|
65
|
+
await fileServiceClient.updateTest({
|
|
66
66
|
task,
|
|
67
67
|
generatedCode: code,
|
|
68
68
|
importPaths,
|
|
69
|
+
actionsSummary,
|
|
69
70
|
});
|
|
70
71
|
// skip the rest of the test once generation is over
|
|
71
72
|
await (0, pw_test_1.skipTest)();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen-agent.d.ts","sourceRoot":"","sources":["../../src/tools/codegen-agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codegen-agent.d.ts","sourceRoot":"","sources":["../../src/tools/codegen-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAmBnD,eAAO,MAAM,WAAW,EAAE,IAyBzB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnosis-fetcher.d.ts","sourceRoot":"","sources":["../../src/tools/diagnosis-fetcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"diagnosis-fetcher.d.ts","sourceRoot":"","sources":["../../src/tools/diagnosis-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAanD,eAAO,MAAM,aAAa,EAAE,IAgF3B,CAAC"}
|
package/dist/tools/grep.d.ts
CHANGED
package/dist/tools/grep.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/tools/grep.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/tools/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,wBAAwB,CAAC;AAqB/D,eAAO,MAAM,QAAQ,EAAE,IA+CtB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AA0DnD,eAAO,MAAM,4BAA4B,EAAE,IA0E1C,CAAC"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.generateTestWithBrowserAgent = void 0;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
4
8
|
const zod_1 = require("zod");
|
|
5
9
|
const run_1 = require("../agent/browsing/run");
|
|
6
10
|
const utils_1 = require("../agent/browsing/utils");
|
|
7
11
|
const web_1 = require("../bin/utils/platform/web");
|
|
8
12
|
const scenarios_1 = require("../bin/utils/scenarios");
|
|
9
|
-
const git_1 = require("../utils/git");
|
|
10
13
|
const BrowserAgentSchema = zod_1.z.object({
|
|
11
14
|
testName: zod_1.z.string().describe("The name of the test to create or modify"),
|
|
12
15
|
testSuites: zod_1.z
|
|
@@ -27,10 +30,10 @@ locator/selector for an element on the page.
|
|
|
27
30
|
|
|
28
31
|
IMPORTANT: Before you invoke this tool, you need to ensure that the test code is correctly prepared for this
|
|
29
32
|
agent. Preparation involves adding a TODO comment that describes the change that needs to be made. A good
|
|
30
|
-
comment calls out the element and browser interactions
|
|
33
|
+
comment calls out the element and browser interactions it must take. The TODO comment also has (agent) next to it, to
|
|
31
34
|
clearly label that the change is for the agent to make.
|
|
32
35
|
|
|
33
|
-
For example
|
|
36
|
+
For example, this is a good TODO comment:
|
|
34
37
|
|
|
35
38
|
\`\`\`
|
|
36
39
|
test("Example test code", async ({ page }) => {
|
|
@@ -39,16 +42,12 @@ test("Example test code", async ({ page }) => {
|
|
|
39
42
|
});
|
|
40
43
|
\`\`\`
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
For the above file, the browser environment will execute the steps before the TODO comment and hand-over the control
|
|
46
|
+
to the browser agent. The agent will do the actions described in the TODO comment and then resume control back to the
|
|
47
|
+
test code.
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
await page.goto("https://example.com");
|
|
49
|
-
await page.getByRole("button", { name: "Login" }).click();
|
|
50
|
-
});
|
|
51
|
-
\`\`\`
|
|
49
|
+
The browser agent will return a summary of actions that it took, and the generated Playwright code for them. You can
|
|
50
|
+
then use the text editor tool to replace the TODO comment with the generated Playwright code.
|
|
52
51
|
`;
|
|
53
52
|
exports.generateTestWithBrowserAgent = {
|
|
54
53
|
schema: {
|
|
@@ -72,6 +71,7 @@ exports.generateTestWithBrowserAgent = {
|
|
|
72
71
|
result: `Test block not found for test name: "${testName}" in file: "${fileName}" with describe blocks: "${testSuites.join(", ")}"`,
|
|
73
72
|
};
|
|
74
73
|
}
|
|
74
|
+
const fileBackup = await promises_1.default.readFile(fileName, "utf-8");
|
|
75
75
|
try {
|
|
76
76
|
await (0, utils_1.replaceTodoWithCreateTest)({
|
|
77
77
|
testCaseName: testName,
|
|
@@ -85,7 +85,7 @@ exports.generateTestWithBrowserAgent = {
|
|
|
85
85
|
result: `Error running tool: ${error}`,
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
-
const
|
|
88
|
+
const toolResult = await (0, run_1.generateTestsUsingMasterAgent)({
|
|
89
89
|
testFilePath: fileName,
|
|
90
90
|
filePathToUpdate: fileName,
|
|
91
91
|
pwProjectsFilter: [project],
|
|
@@ -97,25 +97,27 @@ exports.generateTestWithBrowserAgent = {
|
|
|
97
97
|
useComputerUseAgent: true,
|
|
98
98
|
}),
|
|
99
99
|
repoDir: process.cwd(),
|
|
100
|
+
editFileWithGeneratedCode: false,
|
|
100
101
|
});
|
|
102
|
+
// Undo the TODO -> createTest change
|
|
103
|
+
await promises_1.default.writeFile(fileName, fileBackup, "utf-8");
|
|
104
|
+
const { isError, error, actionsSummary } = toolResult;
|
|
101
105
|
if (!isError) {
|
|
102
|
-
const gitPatch = (0, git_1.getGitDiff)(fileName);
|
|
103
106
|
return {
|
|
104
107
|
isError,
|
|
105
|
-
result: `
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
result: `Browser agent has finished running. Here is the summary of actions it took
|
|
109
|
+
and the generated Playwright code:
|
|
110
|
+
|
|
111
|
+
${actionsSummary}
|
|
109
112
|
`,
|
|
110
113
|
};
|
|
111
114
|
}
|
|
112
115
|
else {
|
|
113
116
|
return {
|
|
114
117
|
isError,
|
|
115
|
-
result: `
|
|
116
|
-
|
|
118
|
+
result: `Browser agent failed to run successfully. Here is the error:
|
|
119
|
+
|
|
117
120
|
${error}
|
|
118
|
-
\`\`\`
|
|
119
121
|
`,
|
|
120
122
|
};
|
|
121
123
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/test-run-fetcher/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/test-run-fetcher/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAanD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOnE;AAED,eAAO,MAAM,WAAW,EAAE,IA4HzB,CAAC"}
|
package/dist/tools/test-run.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run.d.ts","sourceRoot":"","sources":["../../src/tools/test-run.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"test-run.d.ts","sourceRoot":"","sources":["../../src/tools/test-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAuBnD,eAAO,MAAM,WAAW,EAAE,IA8BzB,CAAC"}
|
package/dist/tools/test-run.js
CHANGED
|
@@ -15,8 +15,7 @@ const RunTestSchema = zod_1.z.object({
|
|
|
15
15
|
headed: zod_1.z
|
|
16
16
|
.boolean()
|
|
17
17
|
.describe("Whether to run the test in headed mode (default is false, which is headless)")
|
|
18
|
-
.optional()
|
|
19
|
-
.default(false),
|
|
18
|
+
.optional(),
|
|
20
19
|
});
|
|
21
20
|
exports.runTestTool = {
|
|
22
21
|
schema: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/test-gen",
|
|
3
|
-
"version": "0.52.
|
|
3
|
+
"version": "0.52.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"tsx": "^4.16.2",
|
|
76
76
|
"typescript": "^5.3.3",
|
|
77
77
|
"zod": "^3.23.8",
|
|
78
|
-
"@empiricalrun/llm": "^0.
|
|
78
|
+
"@empiricalrun/llm": "^0.13.1",
|
|
79
79
|
"@empiricalrun/r2-uploader": "^0.3.8",
|
|
80
80
|
"@empiricalrun/test-run": "^0.7.6"
|
|
81
81
|
},
|
package/dist/tools/types.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
/**
|
|
3
|
-
* Base schema for all tools. Each tool should extend this with their specific parameters.
|
|
4
|
-
*/
|
|
5
|
-
export declare const BaseToolSchema: z.ZodObject<{
|
|
6
|
-
name: z.ZodString;
|
|
7
|
-
description: z.ZodString;
|
|
8
|
-
parameters: z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>;
|
|
9
|
-
}, "strip", z.ZodTypeAny, {
|
|
10
|
-
name: string;
|
|
11
|
-
description: string;
|
|
12
|
-
parameters: {} & {
|
|
13
|
-
[k: string]: unknown;
|
|
14
|
-
};
|
|
15
|
-
}, {
|
|
16
|
-
name: string;
|
|
17
|
-
description: string;
|
|
18
|
-
parameters: {} & {
|
|
19
|
-
[k: string]: unknown;
|
|
20
|
-
};
|
|
21
|
-
}>;
|
|
22
|
-
export type ToolSchema = z.infer<typeof BaseToolSchema>;
|
|
23
|
-
/**
|
|
24
|
-
* Interface for creating a tool with its schema and execute function
|
|
25
|
-
*/
|
|
26
|
-
export interface Tool {
|
|
27
|
-
schema: {
|
|
28
|
-
name: string;
|
|
29
|
-
description: string;
|
|
30
|
-
parameters: z.ZodType;
|
|
31
|
-
};
|
|
32
|
-
execute: (input: any) => Promise<ToolResult>;
|
|
33
|
-
}
|
|
34
|
-
export interface ToolResult {
|
|
35
|
-
isError: boolean;
|
|
36
|
-
result: string;
|
|
37
|
-
}
|
|
38
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;EAIzB,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC;KACvB,CAAC;IACF,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB"}
|
package/dist/tools/types.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BaseToolSchema = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
/**
|
|
6
|
-
* Base schema for all tools. Each tool should extend this with their specific parameters.
|
|
7
|
-
*/
|
|
8
|
-
exports.BaseToolSchema = zod_1.z.object({
|
|
9
|
-
name: zod_1.z.string(),
|
|
10
|
-
description: zod_1.z.string(),
|
|
11
|
-
parameters: zod_1.z.object({}).passthrough(),
|
|
12
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type OpenAI from "openai";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
/**
|
|
4
|
-
* Convert a tool schema to OpenAI tool format
|
|
5
|
-
*/
|
|
6
|
-
export declare function zodToOpenAITool(schema: {
|
|
7
|
-
name: string;
|
|
8
|
-
description: string;
|
|
9
|
-
parameters: z.ZodType;
|
|
10
|
-
}): OpenAI.Chat.Completions.ChatCompletionTool;
|
|
11
|
-
/**
|
|
12
|
-
* Convert Zod schema to JSON Schema
|
|
13
|
-
*/
|
|
14
|
-
export declare function zodToJsonSchema(schema: z.ZodType): any;
|
|
15
|
-
/**
|
|
16
|
-
* Convert specific Zod type to JSON Schema
|
|
17
|
-
*/
|
|
18
|
-
export declare function zodTypeToJsonSchema(zodType: z.ZodType): any;
|
|
19
|
-
//# sourceMappingURL=zod-schema.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"zod-schema.d.ts","sourceRoot":"","sources":["../../src/tools/zod-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC;CACvB,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAS7C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,GAAG,CAuBtD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,GAAG,GAAG,CAoD3D"}
|
package/dist/tools/zod-schema.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.zodTypeToJsonSchema = exports.zodToJsonSchema = exports.zodToOpenAITool = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
/**
|
|
6
|
-
* Convert a tool schema to OpenAI tool format
|
|
7
|
-
*/
|
|
8
|
-
function zodToOpenAITool(schema) {
|
|
9
|
-
return {
|
|
10
|
-
type: "function",
|
|
11
|
-
function: {
|
|
12
|
-
name: schema.name,
|
|
13
|
-
description: schema.description,
|
|
14
|
-
parameters: zodToJsonSchema(schema.parameters),
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
exports.zodToOpenAITool = zodToOpenAITool;
|
|
19
|
-
/**
|
|
20
|
-
* Convert Zod schema to JSON Schema
|
|
21
|
-
*/
|
|
22
|
-
function zodToJsonSchema(schema) {
|
|
23
|
-
if (schema instanceof zod_1.z.ZodObject) {
|
|
24
|
-
const shape = schema._def.shape();
|
|
25
|
-
const properties = {};
|
|
26
|
-
const required = [];
|
|
27
|
-
Object.entries(shape).forEach(([key, value]) => {
|
|
28
|
-
properties[key] = zodTypeToJsonSchema(value);
|
|
29
|
-
// Check if this field is required
|
|
30
|
-
if (!(value instanceof zod_1.z.ZodOptional)) {
|
|
31
|
-
required.push(key);
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
return {
|
|
35
|
-
type: "object",
|
|
36
|
-
properties,
|
|
37
|
-
...(required.length > 0 ? { required } : {}),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
return { type: "string" }; // Fallback
|
|
41
|
-
}
|
|
42
|
-
exports.zodToJsonSchema = zodToJsonSchema;
|
|
43
|
-
/**
|
|
44
|
-
* Convert specific Zod type to JSON Schema
|
|
45
|
-
*/
|
|
46
|
-
function zodTypeToJsonSchema(zodType) {
|
|
47
|
-
// Handle string types
|
|
48
|
-
if (zodType instanceof zod_1.z.ZodString) {
|
|
49
|
-
const schema = { type: "string" };
|
|
50
|
-
if (zodType.description)
|
|
51
|
-
schema.description = zodType.description;
|
|
52
|
-
return schema;
|
|
53
|
-
}
|
|
54
|
-
// Handle number types
|
|
55
|
-
if (zodType instanceof zod_1.z.ZodNumber) {
|
|
56
|
-
const schema = { type: "number" };
|
|
57
|
-
if (zodType.description)
|
|
58
|
-
schema.description = zodType.description;
|
|
59
|
-
return schema;
|
|
60
|
-
}
|
|
61
|
-
// Handle boolean
|
|
62
|
-
if (zodType instanceof zod_1.z.ZodBoolean) {
|
|
63
|
-
const schema = { type: "boolean" };
|
|
64
|
-
if (zodType.description)
|
|
65
|
-
schema.description = zodType.description;
|
|
66
|
-
return schema;
|
|
67
|
-
}
|
|
68
|
-
// Handle arrays
|
|
69
|
-
if (zodType instanceof zod_1.z.ZodArray) {
|
|
70
|
-
return {
|
|
71
|
-
type: "array",
|
|
72
|
-
items: zodTypeToJsonSchema(zodType._def.type),
|
|
73
|
-
...(zodType.description ? { description: zodType.description } : {}),
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
// Handle objects
|
|
77
|
-
if (zodType instanceof zod_1.z.ZodObject) {
|
|
78
|
-
return zodToJsonSchema(zodType);
|
|
79
|
-
}
|
|
80
|
-
// Handle enums
|
|
81
|
-
if (zodType instanceof zod_1.z.ZodEnum) {
|
|
82
|
-
return {
|
|
83
|
-
type: "string",
|
|
84
|
-
enum: zodType._def.values,
|
|
85
|
-
...(zodType.description ? { description: zodType.description } : {}),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
// Handle optional types
|
|
89
|
-
if (zodType instanceof zod_1.z.ZodOptional) {
|
|
90
|
-
return zodTypeToJsonSchema(zodType._def.innerType);
|
|
91
|
-
}
|
|
92
|
-
// Default fallback
|
|
93
|
-
return { type: "string" };
|
|
94
|
-
}
|
|
95
|
-
exports.zodTypeToJsonSchema = zodTypeToJsonSchema;
|