ff-automationv2 1.0.0 → 2.0.1-beta.0
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/bitbucket-pipelines.yml +20 -0
- package/eslint.config.ts +29 -29
- package/package.json +51 -51
- package/src/ai/llmcalls/decodeApiKey.ts +14 -14
- package/src/ai/llmcalls/llmAction.ts +89 -89
- package/src/ai/llmcalls/parseLlmOputput.ts +69 -69
- package/src/ai/llmprompts/systemPrompts/actionExtractorPrompt.ts +70 -70
- package/src/ai/llmprompts/systemPrompts/errorDescriptionPrompt.ts +23 -23
- package/src/ai/llmprompts/systemPrompts/fireflinkElementIndexExtactors.ts +198 -198
- package/src/ai/llmprompts/systemPrompts/userStoryToListPrompt.ts +24 -24
- package/src/ai/llmprompts/systemPrompts/visionPrompt.ts +28 -28
- package/src/automation/actions/executor.ts +74 -74
- package/src/automation/actions/interaction/enterInput.ts +27 -27
- package/src/automation/actions/interface/interactionActionInterface.ts +26 -26
- package/src/automation/actions/interface/navigationActionInterface.ts +21 -21
- package/src/automation/actions/interface/waitActionInterface.ts +5 -5
- package/src/automation/actions/navigation/getTitle.ts +8 -8
- package/src/automation/actions/navigation/goBack.ts +8 -8
- package/src/automation/actions/navigation/navigate.ts +9 -9
- package/src/automation/actions/navigation/refresh.ts +8 -8
- package/src/automation/actions/wait/wait.ts +9 -9
- package/src/automation/browserSession/initiateBrowserSession.ts +81 -81
- package/src/core/constants/supportedActions.ts +7 -7
- package/src/core/interfaces/StableDomInterface.ts +5 -5
- package/src/core/interfaces/actionInterface.ts +13 -13
- package/src/core/interfaces/automationRunnerInterface.ts +2 -2
- package/src/core/interfaces/browserCapabilitiesInterface.ts +4 -4
- package/src/core/interfaces/browserConfigurationInterface.ts +2 -2
- package/src/core/interfaces/domAnalysisInterface.ts +34 -34
- package/src/core/interfaces/executionDetails.ts +29 -29
- package/src/core/interfaces/fireflinkScriptPayloadInterface.ts +39 -39
- package/src/core/interfaces/llmConfigurationInterface.ts +2 -2
- package/src/core/interfaces/llmResponseInterface.ts +38 -38
- package/src/core/interfaces/promptInterface.ts +21 -21
- package/src/core/interfaces/scriptGenrationDataInterface.ts +16 -16
- package/src/core/interfaces/toolsInterface.ts +5 -5
- package/src/core/main/actionHandlerFactory.ts +86 -86
- package/src/core/main/executionContext.ts +18 -18
- package/src/core/main/runAutomationScript.ts +177 -177
- package/src/core/main/stepProcessor.ts +28 -28
- package/src/core/types/llmResponseType.ts +10 -10
- package/src/core/types/visionllmInputType.ts +4 -4
- package/src/domAnalysis/getRelaventElements.ts +24 -24
- package/src/domAnalysis/relativeElementsFromDom.ts +94 -94
- package/src/domAnalysis/searchBest.ts +159 -159
- package/src/domAnalysis/simplifyAndFlatten.ts +118 -118
- package/src/fireflinkData/fireflinkLocators/elementsFromHTML.ts +656 -656
- package/src/fireflinkData/fireflinkLocators/getListOfLocators.ts +31 -31
- package/src/fireflinkData/fireflinkLocators/typeList.ts +36 -36
- package/src/fireflinkData/fireflinkScript/scriptGenrationData.ts +30 -30
- package/src/index.ts +5 -5
- package/src/llmConfig/llmConfiguration.ts +26 -26
- package/src/service/fireflinkApi.service.ts +46 -46
- package/src/service/scriptRunner.service.ts +83 -83
- package/src/utils/DomExtraction/jsForAttributeInjection.ts +254 -254
- package/src/utils/javascript/jsFindElement.ts +161 -161
- package/src/utils/javascript/jsForShadowRoot.ts +216 -216
- package/src/utils/javascript/jsForToaster.ts +60 -60
- package/src/utils/logger/logData.ts +36 -36
- package/tsconfig.json +26 -26
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
image: node:20
|
|
2
|
+
|
|
3
|
+
pipelines:
|
|
4
|
+
tags:
|
|
5
|
+
"v*":
|
|
6
|
+
- step:
|
|
7
|
+
name: Publish to NPM
|
|
8
|
+
caches:
|
|
9
|
+
- node
|
|
10
|
+
script:
|
|
11
|
+
- echo "Installing dependencies"
|
|
12
|
+
- npm ci
|
|
13
|
+
|
|
14
|
+
- echo "Building project"
|
|
15
|
+
- npm run build
|
|
16
|
+
|
|
17
|
+
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
|
|
18
|
+
|
|
19
|
+
- echo "Publishing to npm"
|
|
20
|
+
- npm publish --access public
|
package/eslint.config.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import js from "@eslint/js";
|
|
2
|
-
import globals from "globals";
|
|
3
|
-
import tseslint from "typescript-eslint";
|
|
4
|
-
import { defineConfig } from "eslint/config";
|
|
5
|
-
|
|
6
|
-
export default defineConfig([
|
|
7
|
-
{
|
|
8
|
-
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
|
9
|
-
plugins: { js },
|
|
10
|
-
extends: ["js/recommended"],
|
|
11
|
-
languageOptions: { globals: globals.browser },
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
...tseslint.configs.recommended,
|
|
15
|
-
|
|
16
|
-
{
|
|
17
|
-
rules: {
|
|
18
|
-
"no-console": ["error", { allow: ["warn", "error"] }],
|
|
19
|
-
"@typescript-eslint/no-explicit-any": "off",
|
|
20
|
-
"@typescript-eslint/no-unused-vars": [
|
|
21
|
-
"error",
|
|
22
|
-
{
|
|
23
|
-
argsIgnorePattern: "^_",
|
|
24
|
-
varsIgnorePattern: "^_",
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
]);
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import tseslint from "typescript-eslint";
|
|
4
|
+
import { defineConfig } from "eslint/config";
|
|
5
|
+
|
|
6
|
+
export default defineConfig([
|
|
7
|
+
{
|
|
8
|
+
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
|
9
|
+
plugins: { js },
|
|
10
|
+
extends: ["js/recommended"],
|
|
11
|
+
languageOptions: { globals: globals.browser },
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
...tseslint.configs.recommended,
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
rules: {
|
|
18
|
+
"no-console": ["error", { allow: ["warn", "error"] }],
|
|
19
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
20
|
+
"@typescript-eslint/no-unused-vars": [
|
|
21
|
+
"error",
|
|
22
|
+
{
|
|
23
|
+
argsIgnorePattern: "^_",
|
|
24
|
+
varsIgnorePattern: "^_",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
]);
|
package/package.json
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ff-automationv2",
|
|
3
|
-
"version": "
|
|
4
|
-
"private": false,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"description": "This lib is used to automate the manual testcase",
|
|
7
|
-
"main": "./dist/index.js",
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"import": "./dist/index.js",
|
|
12
|
-
"types": "./dist/index.d.ts"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"builds": "npm run lint && tsc",
|
|
17
|
-
"build": "tsc",
|
|
18
|
-
"prepare": "npm run build",
|
|
19
|
-
"lint": "eslint src --max-warnings=0",
|
|
20
|
-
"test": "npm run build && node dist/tests/test12.js",
|
|
21
|
-
"llm": "npm run build && node dist/tests/testllm.js",
|
|
22
|
-
"dev": "npm run build && node dist/index.js",
|
|
23
|
-
"start": "node dist/index.js",
|
|
24
|
-
"file": "node dist/domAnalysis/getXpath.js"
|
|
25
|
-
},
|
|
26
|
-
"keywords": [],
|
|
27
|
-
"author": "Diwahar R",
|
|
28
|
-
"license": "ISC",
|
|
29
|
-
"devDependencies": {
|
|
30
|
-
"@eslint/js": "10.0.1",
|
|
31
|
-
"@types/jsdom": "27.0.0",
|
|
32
|
-
"@types/node": "25.2.2",
|
|
33
|
-
"eslint": "10.0.0",
|
|
34
|
-
"globals": "17.3.0",
|
|
35
|
-
"jiti": "2.6.1",
|
|
36
|
-
"nodemon": "3.1.11",
|
|
37
|
-
"ts-node": "10.9.2",
|
|
38
|
-
"typescript": "5.9.3",
|
|
39
|
-
"typescript-eslint": "8.56.0"
|
|
40
|
-
},
|
|
41
|
-
"dependencies": {
|
|
42
|
-
"axios": "1.13.5",
|
|
43
|
-
"cheerio": "1.2.0",
|
|
44
|
-
"fs-extra": "11.3.3",
|
|
45
|
-
"fuzzball": "2.2.3",
|
|
46
|
-
"jsdom": "27.4.0",
|
|
47
|
-
"openai": "6.18.0",
|
|
48
|
-
"uuid": "13.0.0",
|
|
49
|
-
"webdriverio": "9.23.3"
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "ff-automationv2",
|
|
3
|
+
"version": "2.0.1-beta.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "This lib is used to automate the manual testcase",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"builds": "npm run lint && tsc",
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"prepare": "npm run build",
|
|
19
|
+
"lint": "eslint src --max-warnings=0",
|
|
20
|
+
"test": "npm run build && node dist/tests/test12.js",
|
|
21
|
+
"llm": "npm run build && node dist/tests/testllm.js",
|
|
22
|
+
"dev": "npm run build && node dist/index.js",
|
|
23
|
+
"start": "node dist/index.js",
|
|
24
|
+
"file": "node dist/domAnalysis/getXpath.js"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [],
|
|
27
|
+
"author": "Diwahar R",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@eslint/js": "10.0.1",
|
|
31
|
+
"@types/jsdom": "27.0.0",
|
|
32
|
+
"@types/node": "25.2.2",
|
|
33
|
+
"eslint": "10.0.0",
|
|
34
|
+
"globals": "17.3.0",
|
|
35
|
+
"jiti": "2.6.1",
|
|
36
|
+
"nodemon": "3.1.11",
|
|
37
|
+
"ts-node": "10.9.2",
|
|
38
|
+
"typescript": "5.9.3",
|
|
39
|
+
"typescript-eslint": "8.56.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"axios": "1.13.5",
|
|
43
|
+
"cheerio": "1.2.0",
|
|
44
|
+
"fs-extra": "11.3.3",
|
|
45
|
+
"fuzzball": "2.2.3",
|
|
46
|
+
"jsdom": "27.4.0",
|
|
47
|
+
"openai": "6.18.0",
|
|
48
|
+
"uuid": "13.0.0",
|
|
49
|
+
"webdriverio": "9.23.3"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
export function decodeApiKey(apiKey: string): string {
|
|
3
|
-
if (!apiKey) {
|
|
4
|
-
throw new Error("API key is required");
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
try {
|
|
8
|
-
return Buffer.from(apiKey, "base64").toString("utf-8");
|
|
9
|
-
} catch (error) {
|
|
10
|
-
throw new Error("Failed to decode API key", { cause: error });
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
|
|
2
|
+
export function decodeApiKey(apiKey: string): string {
|
|
3
|
+
if (!apiKey) {
|
|
4
|
+
throw new Error("API key is required");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
return Buffer.from(apiKey, "base64").toString("utf-8");
|
|
9
|
+
} catch (error) {
|
|
10
|
+
throw new Error("Failed to decode API key", { cause: error });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import { prompts } from "../llmprompts/promptRegistry.js";
|
|
2
|
-
import OpenAI from "openai";
|
|
3
|
-
import { decodeApiKey } from "./decodeApiKey.js";
|
|
4
|
-
import { FireFlinkLLMResponse } from "../../core/types/llmResponseType.js"
|
|
5
|
-
import { PromptArgsMap } from "../../core/interfaces/promptInterface.js"
|
|
6
|
-
import { userInputFormatters } from "../../ai/llmprompts/userPrompts/userPrompt.js"
|
|
7
|
-
import { visionPromptMessage } from "../../core/types/visionllmInputType.js"
|
|
8
|
-
import { PromptType } from "../../core/types/promptType.js";
|
|
9
|
-
export class llmAction {
|
|
10
|
-
private client: OpenAI;
|
|
11
|
-
private model: string;
|
|
12
|
-
private visionClient: OpenAI
|
|
13
|
-
constructor(
|
|
14
|
-
apiKey: string,
|
|
15
|
-
baseURL: string,
|
|
16
|
-
model: string,
|
|
17
|
-
visionApiKey: string
|
|
18
|
-
) {
|
|
19
|
-
this.model = model;
|
|
20
|
-
this.client = new OpenAI({
|
|
21
|
-
apiKey: decodeApiKey(apiKey),
|
|
22
|
-
baseURL: baseURL,
|
|
23
|
-
});
|
|
24
|
-
this.visionClient = new OpenAI({
|
|
25
|
-
apiKey: decodeApiKey(visionApiKey),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
private async getLLMResponseText(system: string, user: string): Promise<FireFlinkLLMResponse> {
|
|
31
|
-
const response = await this.client.chat.completions.create({
|
|
32
|
-
model: this.model,
|
|
33
|
-
messages: [
|
|
34
|
-
{
|
|
35
|
-
role: "system",
|
|
36
|
-
content: system
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
role: "user",
|
|
40
|
-
content: user
|
|
41
|
-
}
|
|
42
|
-
],
|
|
43
|
-
response_format: { type: "json_object" }
|
|
44
|
-
});
|
|
45
|
-
try {
|
|
46
|
-
|
|
47
|
-
return response;
|
|
48
|
-
} catch {
|
|
49
|
-
throw new Error(`Invalid JSON returned from LLM `);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async getLLMResponse<K extends keyof PromptArgsMap>(
|
|
53
|
-
type: K,
|
|
54
|
-
args: PromptArgsMap[K],
|
|
55
|
-
userInput: Record<string, any>
|
|
56
|
-
): Promise<FireFlinkLLMResponse> {
|
|
57
|
-
const promptBuilder: (args: PromptArgsMap[K]) => Promise<string> =
|
|
58
|
-
prompts[type];
|
|
59
|
-
const systemPrompt = await promptBuilder(args);
|
|
60
|
-
const userPrompt: string | visionPromptMessage = userInputFormatters[type](userInput);
|
|
61
|
-
if (type === PromptType.VISION_PROMPT && Array.isArray(userPrompt)) {
|
|
62
|
-
return this.getLLMResponseWithVision(systemPrompt, userPrompt);
|
|
63
|
-
}
|
|
64
|
-
if (typeof userPrompt !== "string") {
|
|
65
|
-
throw new Error("Invalid non-vision user prompt format");
|
|
66
|
-
}
|
|
67
|
-
return this.getLLMResponseText(systemPrompt, userPrompt);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private async getLLMResponseWithVision(system: any, userPrompt: visionPromptMessage): Promise<string> {
|
|
71
|
-
const response = await this.visionClient.chat.completions.create({
|
|
72
|
-
model: "gpt-4.1",
|
|
73
|
-
messages: [
|
|
74
|
-
{
|
|
75
|
-
role: "system",
|
|
76
|
-
content: system
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
role: "user",
|
|
80
|
-
content: userPrompt
|
|
81
|
-
}
|
|
82
|
-
],
|
|
83
|
-
response_format: { type: "json_object" }
|
|
84
|
-
});
|
|
85
|
-
return response.choices[0].message.content || "";
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
1
|
+
import { prompts } from "../llmprompts/promptRegistry.js";
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
import { decodeApiKey } from "./decodeApiKey.js";
|
|
4
|
+
import { FireFlinkLLMResponse } from "../../core/types/llmResponseType.js"
|
|
5
|
+
import { PromptArgsMap } from "../../core/interfaces/promptInterface.js"
|
|
6
|
+
import { userInputFormatters } from "../../ai/llmprompts/userPrompts/userPrompt.js"
|
|
7
|
+
import { visionPromptMessage } from "../../core/types/visionllmInputType.js"
|
|
8
|
+
import { PromptType } from "../../core/types/promptType.js";
|
|
9
|
+
export class llmAction {
|
|
10
|
+
private client: OpenAI;
|
|
11
|
+
private model: string;
|
|
12
|
+
private visionClient: OpenAI
|
|
13
|
+
constructor(
|
|
14
|
+
apiKey: string,
|
|
15
|
+
baseURL: string,
|
|
16
|
+
model: string,
|
|
17
|
+
visionApiKey: string
|
|
18
|
+
) {
|
|
19
|
+
this.model = model;
|
|
20
|
+
this.client = new OpenAI({
|
|
21
|
+
apiKey: decodeApiKey(apiKey),
|
|
22
|
+
baseURL: baseURL,
|
|
23
|
+
});
|
|
24
|
+
this.visionClient = new OpenAI({
|
|
25
|
+
apiKey: decodeApiKey(visionApiKey),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private async getLLMResponseText(system: string, user: string): Promise<FireFlinkLLMResponse> {
|
|
31
|
+
const response = await this.client.chat.completions.create({
|
|
32
|
+
model: this.model,
|
|
33
|
+
messages: [
|
|
34
|
+
{
|
|
35
|
+
role: "system",
|
|
36
|
+
content: system
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
role: "user",
|
|
40
|
+
content: user
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
response_format: { type: "json_object" }
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
|
|
47
|
+
return response;
|
|
48
|
+
} catch {
|
|
49
|
+
throw new Error(`Invalid JSON returned from LLM `);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async getLLMResponse<K extends keyof PromptArgsMap>(
|
|
53
|
+
type: K,
|
|
54
|
+
args: PromptArgsMap[K],
|
|
55
|
+
userInput: Record<string, any>
|
|
56
|
+
): Promise<FireFlinkLLMResponse> {
|
|
57
|
+
const promptBuilder: (args: PromptArgsMap[K]) => Promise<string> =
|
|
58
|
+
prompts[type];
|
|
59
|
+
const systemPrompt = await promptBuilder(args);
|
|
60
|
+
const userPrompt: string | visionPromptMessage = userInputFormatters[type](userInput);
|
|
61
|
+
if (type === PromptType.VISION_PROMPT && Array.isArray(userPrompt)) {
|
|
62
|
+
return this.getLLMResponseWithVision(systemPrompt, userPrompt);
|
|
63
|
+
}
|
|
64
|
+
if (typeof userPrompt !== "string") {
|
|
65
|
+
throw new Error("Invalid non-vision user prompt format");
|
|
66
|
+
}
|
|
67
|
+
return this.getLLMResponseText(systemPrompt, userPrompt);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private async getLLMResponseWithVision(system: any, userPrompt: visionPromptMessage): Promise<string> {
|
|
71
|
+
const response = await this.visionClient.chat.completions.create({
|
|
72
|
+
model: "gpt-4.1",
|
|
73
|
+
messages: [
|
|
74
|
+
{
|
|
75
|
+
role: "system",
|
|
76
|
+
content: system
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
role: "user",
|
|
80
|
+
content: userPrompt
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
response_format: { type: "json_object" }
|
|
84
|
+
});
|
|
85
|
+
return response.choices[0].message.content || "";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
}
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
import { FireFlinkLLMResponse } from "../../core/types/llmResponseType.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export class LLMResultParser {
|
|
5
|
-
private inputTokens: number = 0;
|
|
6
|
-
private outputTokens: number = 0;
|
|
7
|
-
private totalTokens: number = 0;
|
|
8
|
-
|
|
9
|
-
private async extractJsonObject(response: string) {
|
|
10
|
-
const match = response.match(/\{[\s\S]*\}/);
|
|
11
|
-
if (!match) return null;
|
|
12
|
-
return JSON.parse(match[0]);
|
|
13
|
-
}
|
|
14
|
-
public async fetchResult(llmOutput: any): Promise<{
|
|
15
|
-
response: FireFlinkLLMResponse
|
|
16
|
-
}> {
|
|
17
|
-
try {
|
|
18
|
-
const content = llmOutput?.choices?.[0]?.message?.content;
|
|
19
|
-
|
|
20
|
-
if (!content) {
|
|
21
|
-
throw new Error("No content in LLM output");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let parsedContent: FireFlinkLLMResponse;
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
// Try direct parse first
|
|
28
|
-
parsedContent = JSON.parse(content);
|
|
29
|
-
} catch {
|
|
30
|
-
// If LLM added explanation text, extract JSON block
|
|
31
|
-
const extracted = this.extractJsonObject(content);
|
|
32
|
-
|
|
33
|
-
if (!extracted) {
|
|
34
|
-
throw new Error("Could not extract valid JSON from LLM response");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
parsedContent = extracted as FireFlinkLLMResponse;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const usage = llmOutput?.usage ?? {};
|
|
41
|
-
|
|
42
|
-
const inputTokens = Number(usage.prompt_tokens ?? 0);
|
|
43
|
-
const outputTokens = Number(usage.completion_tokens ?? 0);
|
|
44
|
-
const totalTokens = Number(usage.total_tokens ?? 0);
|
|
45
|
-
|
|
46
|
-
this.inputTokens += inputTokens;
|
|
47
|
-
this.outputTokens += outputTokens;
|
|
48
|
-
this.totalTokens += totalTokens;
|
|
49
|
-
|
|
50
|
-
return { response: parsedContent };
|
|
51
|
-
|
|
52
|
-
} catch (error) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
"Failed to parse LLM output",
|
|
55
|
-
{ cause: error }
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
public getTokenUsage() {
|
|
63
|
-
return {
|
|
64
|
-
inputTokens: this.inputTokens,
|
|
65
|
-
outputTokens: this.outputTokens,
|
|
66
|
-
totalTokens: this.totalTokens,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
1
|
+
import { FireFlinkLLMResponse } from "../../core/types/llmResponseType.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export class LLMResultParser {
|
|
5
|
+
private inputTokens: number = 0;
|
|
6
|
+
private outputTokens: number = 0;
|
|
7
|
+
private totalTokens: number = 0;
|
|
8
|
+
|
|
9
|
+
private async extractJsonObject(response: string) {
|
|
10
|
+
const match = response.match(/\{[\s\S]*\}/);
|
|
11
|
+
if (!match) return null;
|
|
12
|
+
return JSON.parse(match[0]);
|
|
13
|
+
}
|
|
14
|
+
public async fetchResult(llmOutput: any): Promise<{
|
|
15
|
+
response: FireFlinkLLMResponse
|
|
16
|
+
}> {
|
|
17
|
+
try {
|
|
18
|
+
const content = llmOutput?.choices?.[0]?.message?.content;
|
|
19
|
+
|
|
20
|
+
if (!content) {
|
|
21
|
+
throw new Error("No content in LLM output");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let parsedContent: FireFlinkLLMResponse;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Try direct parse first
|
|
28
|
+
parsedContent = JSON.parse(content);
|
|
29
|
+
} catch {
|
|
30
|
+
// If LLM added explanation text, extract JSON block
|
|
31
|
+
const extracted = this.extractJsonObject(content);
|
|
32
|
+
|
|
33
|
+
if (!extracted) {
|
|
34
|
+
throw new Error("Could not extract valid JSON from LLM response");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
parsedContent = extracted as FireFlinkLLMResponse;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const usage = llmOutput?.usage ?? {};
|
|
41
|
+
|
|
42
|
+
const inputTokens = Number(usage.prompt_tokens ?? 0);
|
|
43
|
+
const outputTokens = Number(usage.completion_tokens ?? 0);
|
|
44
|
+
const totalTokens = Number(usage.total_tokens ?? 0);
|
|
45
|
+
|
|
46
|
+
this.inputTokens += inputTokens;
|
|
47
|
+
this.outputTokens += outputTokens;
|
|
48
|
+
this.totalTokens += totalTokens;
|
|
49
|
+
|
|
50
|
+
return { response: parsedContent };
|
|
51
|
+
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
"Failed to parse LLM output",
|
|
55
|
+
{ cause: error }
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
public getTokenUsage() {
|
|
63
|
+
return {
|
|
64
|
+
inputTokens: this.inputTokens,
|
|
65
|
+
outputTokens: this.outputTokens,
|
|
66
|
+
totalTokens: this.totalTokens,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|