@mseep/ai-tech-app-agent 1.0.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/.env +24 -0
- package/.env.example +24 -0
- package/Jenkinsfile +210 -0
- package/MCP-SERVER-GUIDE.md +405 -0
- package/README.MD +450 -0
- package/dist/config/app.config.d.ts +65 -0
- package/dist/config/app.config.d.ts.map +1 -0
- package/dist/config/app.config.js +94 -0
- package/dist/config/app.config.js.map +1 -0
- package/dist/config/llm.config.d.ts +63 -0
- package/dist/config/llm.config.d.ts.map +1 -0
- package/dist/config/llm.config.js +158 -0
- package/dist/config/llm.config.js.map +1 -0
- package/dist/config/mcp.config.d.ts +175 -0
- package/dist/config/mcp.config.d.ts.map +1 -0
- package/dist/config/mcp.config.js +215 -0
- package/dist/config/mcp.config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +175 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/llamaClient.d.ts +14 -0
- package/dist/llm/llamaClient.d.ts.map +1 -0
- package/dist/llm/llamaClient.js +136 -0
- package/dist/llm/llamaClient.js.map +1 -0
- package/dist/mcp/mcpClient.d.ts +132 -0
- package/dist/mcp/mcpClient.d.ts.map +1 -0
- package/dist/mcp/mcpClient.js +784 -0
- package/dist/mcp/mcpClient.js.map +1 -0
- package/dist/models/testSpec.d.ts +78 -0
- package/dist/models/testSpec.d.ts.map +1 -0
- package/dist/models/testSpec.js +3 -0
- package/dist/models/testSpec.js.map +1 -0
- package/dist/orchestrator/aiTestRunner.d.ts +18 -0
- package/dist/orchestrator/aiTestRunner.d.ts.map +1 -0
- package/dist/orchestrator/aiTestRunner.js +247 -0
- package/dist/orchestrator/aiTestRunner.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +49 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/promptBuilder.d.ts +62 -0
- package/dist/utils/promptBuilder.d.ts.map +1 -0
- package/dist/utils/promptBuilder.js +333 -0
- package/dist/utils/promptBuilder.js.map +1 -0
- package/knowledge/app-knowledge.txt +100 -0
- package/logs/combined.log +486 -0
- package/logs/error.log +50 -0
- package/package.json +62 -0
- package/reports/screenshots/screenshot_1764535110518.png +0 -0
- package/reports/test-report.json +106 -0
- package/scripts/check-mcp-server.sh +100 -0
- package/scripts/extract-pom-knowledge.js +222 -0
- package/scripts/pre-test-setup.js +262 -0
- package/scripts/start-mcp-server.sh +76 -0
- package/src/config/app.config.ts +175 -0
- package/src/config/llm.config.ts +220 -0
- package/src/config/mcp.config.ts +291 -0
- package/src/index.ts +161 -0
- package/src/llm/llamaClient.ts +159 -0
- package/src/mcp/mcpClient.ts +878 -0
- package/src/models/testSpec.ts +85 -0
- package/src/orchestrator/aiTestRunner.ts +286 -0
- package/src/utils/logger.ts +59 -0
- package/src/utils/promptBuilder.ts +384 -0
- package/tests/nlp-specs/login-flow.yaml +31 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from 'axios';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import logger from '../utils/logger';
|
|
4
|
+
import { ActionPlan } from '../models/testSpec';
|
|
5
|
+
|
|
6
|
+
dotenv.config();
|
|
7
|
+
|
|
8
|
+
export class LlamaClient {
|
|
9
|
+
private client: AxiosInstance;
|
|
10
|
+
private model: string;
|
|
11
|
+
private temperature: number;
|
|
12
|
+
private maxTokens: number;
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
const baseURL = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';
|
|
16
|
+
this.model = process.env.LLM_MODEL || 'llama3.2:3b';
|
|
17
|
+
this.temperature = parseFloat(process.env.LLM_TEMPERATURE || '0.1');
|
|
18
|
+
this.maxTokens = parseInt(process.env.LLM_MAX_TOKENS || '2000');
|
|
19
|
+
|
|
20
|
+
this.client = axios.create({
|
|
21
|
+
baseURL,
|
|
22
|
+
timeout: 60000,
|
|
23
|
+
headers: {
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
logger.info(`LlamaClient initialized with model: ${this.model}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async generateActionPlan(
|
|
32
|
+
stepText: string,
|
|
33
|
+
uiContext: string,
|
|
34
|
+
appKnowledge?: string
|
|
35
|
+
): Promise<ActionPlan> {
|
|
36
|
+
const prompt = this.buildPrompt(stepText, uiContext, appKnowledge);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
logger.debug(`Sending prompt to LLM for step: "${stepText}"`);
|
|
40
|
+
|
|
41
|
+
const response = await this.client.post('/api/generate', {
|
|
42
|
+
model: this.model,
|
|
43
|
+
prompt,
|
|
44
|
+
stream: false,
|
|
45
|
+
options: {
|
|
46
|
+
temperature: this.temperature,
|
|
47
|
+
num_predict: this.maxTokens,
|
|
48
|
+
},
|
|
49
|
+
format: 'json', // Request JSON output
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const rawResponse = response.data.response;
|
|
53
|
+
logger.debug(`LLM raw response: ${rawResponse}`);
|
|
54
|
+
|
|
55
|
+
// Parse the JSON response
|
|
56
|
+
const actionPlan = this.parseActionPlan(rawResponse);
|
|
57
|
+
logger.info(`Generated ${actionPlan.actions.length} actions for step`);
|
|
58
|
+
|
|
59
|
+
return actionPlan;
|
|
60
|
+
} catch (error: any) {
|
|
61
|
+
logger.error(`Error generating action plan: ${error.message}`);
|
|
62
|
+
throw new Error(`LLM generation failed: ${error.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private buildPrompt(
|
|
67
|
+
stepText: string,
|
|
68
|
+
uiContext: string,
|
|
69
|
+
appKnowledge?: string
|
|
70
|
+
): string {
|
|
71
|
+
return `You are an expert mobile test automation agent. Your task is to generate precise Appium actions to accomplish a test step.
|
|
72
|
+
|
|
73
|
+
**App Context:**
|
|
74
|
+
${appKnowledge || 'A mobile application with standard Android/iOS UI patterns.'}
|
|
75
|
+
|
|
76
|
+
**Current UI State:**
|
|
77
|
+
${uiContext}
|
|
78
|
+
|
|
79
|
+
**Test Step to Execute:**
|
|
80
|
+
"${stepText}"
|
|
81
|
+
|
|
82
|
+
**Instructions:**
|
|
83
|
+
1. Analyze the current UI state carefully
|
|
84
|
+
2. Identify the most reliable element selectors (prefer text, accessibility-id, or resource-id over XPath)
|
|
85
|
+
3. Generate a sequence of actions to accomplish the test step
|
|
86
|
+
4. Minimize screenshot-based coordinate selection - use it only as last resort
|
|
87
|
+
5. Ensure actions are reliable across different device sizes
|
|
88
|
+
|
|
89
|
+
**Output Format (JSON only, no markdown):**
|
|
90
|
+
{
|
|
91
|
+
"actions": [
|
|
92
|
+
{
|
|
93
|
+
"type": "tap|type|scroll|swipe|wait|assert",
|
|
94
|
+
"selector": {
|
|
95
|
+
"strategy": "text|id|accessibility-id|xpath|class",
|
|
96
|
+
"value": "selector_value",
|
|
97
|
+
"index": 0
|
|
98
|
+
},
|
|
99
|
+
"value": "text_to_type (only for type action)",
|
|
100
|
+
"direction": "up|down|left|right (only for scroll/swipe)",
|
|
101
|
+
"assertionType": "exists|visible|text|enabled (only for assert)",
|
|
102
|
+
"expectedValue": "expected_value (only for assert)"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"reasoning": "Brief explanation of the action sequence"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
Generate the JSON action plan now:`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private parseActionPlan(rawResponse: string): ActionPlan {
|
|
112
|
+
try {
|
|
113
|
+
// Remove any markdown code blocks if present
|
|
114
|
+
let cleanedResponse = rawResponse
|
|
115
|
+
.replace(/```json\n?/g, '')
|
|
116
|
+
.replace(/```\n?/g, '')
|
|
117
|
+
.trim();
|
|
118
|
+
|
|
119
|
+
const parsed = JSON.parse(cleanedResponse);
|
|
120
|
+
|
|
121
|
+
// Validate the structure
|
|
122
|
+
if (!parsed.actions || !Array.isArray(parsed.actions)) {
|
|
123
|
+
throw new Error('Invalid action plan structure: missing actions array');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return parsed as ActionPlan;
|
|
127
|
+
} catch (error: any) {
|
|
128
|
+
logger.error(`Failed to parse action plan: ${error.message}`);
|
|
129
|
+
logger.error(`Raw response: ${rawResponse}`);
|
|
130
|
+
|
|
131
|
+
// Return a fallback empty action plan
|
|
132
|
+
return {
|
|
133
|
+
actions: [],
|
|
134
|
+
reasoning: `Parsing failed: ${error.message}`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async healthCheck(): Promise<boolean> {
|
|
140
|
+
try {
|
|
141
|
+
const response = await this.client.get('/api/tags');
|
|
142
|
+
const models = response.data.models || [];
|
|
143
|
+
const modelExists = models.some((m: any) => m.name === this.model);
|
|
144
|
+
|
|
145
|
+
if (!modelExists) {
|
|
146
|
+
logger.warn(`Model ${this.model} not found in Ollama. Available models: ${models.map((m: any) => m.name).join(', ')}`);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
logger.info('LLM health check passed');
|
|
151
|
+
return true;
|
|
152
|
+
} catch (error: any) {
|
|
153
|
+
logger.error(`LLM health check failed: ${error.message}`);
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default LlamaClient;
|