Skip to content

Commit b1173fc

Browse files
committed
update readme
1 parent 82c49d9 commit b1173fc

File tree

9 files changed

+131
-55
lines changed

9 files changed

+131
-55
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
## 📋 Project Overview
1212

13-
LMeterX is a professional large language model performance testing platform that supports comprehensive load testing for any LLM service compatible with the OpenAI API format. Through an intuitive Web interface, users can easily create and manage test tasks, monitor testing processes in real-time, and obtain detailed performance analysis reports, providing reliable data support for model deployment and performance optimization.
13+
LMeterX is a professional large language model performance testing platform that supports comprehensive load testing for any LLM service. Through an intuitive Web interface, users can easily create and manage test tasks, monitor testing processes in real-time, and obtain detailed performance analysis reports, providing reliable data support for model deployment and performance optimization.
1414

1515
<div align="center">
1616
<img src="docs/images/images.gif" alt="LMeterX Demo" width="800"/>
1717
</div>
1818

1919
## ✨ Core Features
2020

21-
- **Universal compatibility**: Applicable to any openai format API such as GPT, Claude, Llama, etc (language/multimodal/CoT)
21+
- **Universal compatibility**: Supports any language/multimodal/CoT model (GPT/Claude/Llama, etc.) service API stress testing
2222
- **Smart load testing**: Precise concurrency control & Real user simulation
2323
- **Professional metrics**: TTFT, TPS, RPS, success/error rate, etc
2424
- **Multi-scenario support**: Text conversations & Multimodal (image+text)
@@ -131,7 +131,6 @@ LMeterX/
131131
## 🗺️ Development Roadmap
132132

133133
### In Development
134-
- [ ] Support for custom API paths and performance metrics collection
135134
- [ ] Support for user-defined load test datasets
136135
- [ ] Support for client resource monitoring
137136

README_CN.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
## 📋 项目简介
1212

13-
LMeterX 是一个专业的大语言模型性能测试平台,支持对任何兼容 OpenAI API 格式的 LLM 服务进行全面的负载测试。通过直观的 Web 界面,用户可以轻松创建和管理测试任务,实时监控测试过程,并获得详细的性能分析报告,为模型部署和性能优化提供可靠的数据支撑。
13+
LMeterX 是一个专业的大语言模型性能测试平台,支持对 LLM 服务进行全面的负载测试。通过直观的 Web 界面,用户可以轻松创建和管理测试任务,实时监控测试过程,并获得详细的性能分析报告,为模型部署和性能优化提供可靠的数据支撑。
1414

1515
<div align="center">
1616
<img src="docs/images/images.gif" alt="LMeterX Demo" width="700"/>
1717
</div>
1818

1919
## ✨ 核心功能
2020

21-
- **通用兼容性**适用于任何openai格式的API, GPT/Claude/Llama等(语言/多模态/CoT)
21+
- **通用兼容性**支持任何语言/多模态/CoT模型(GPT/Claude/Llama等)服务API压测
2222
- **智能负载测试**:精确并发控制 & 真实用户模拟
2323
- **专业性能指标**:TTFT、TPS、RPS、成功/错误率等
2424
- **多场景支持**:文本对话 & 多模态(图像+文本)
@@ -131,7 +131,6 @@ LMeterX 采用现代化的技术栈,确保系统的可靠性和可维护性:
131131
## 🗺️ 发展路线图
132132

133133
### 开发中
134-
- [ ] 支持自定义 API 路径和性能指标收集
135134
- [ ] 支持用户自定义压测数据集
136135
- [ ] 支持客户端资源监控
137136

backend/service/task_service.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -898,9 +898,9 @@ async def test_api_endpoint_svc(request: Request, body: TaskCreateReq):
898898
# Prepare certificate configuration
899899
client_cert = _prepare_client_cert(body)
900900

901-
# Test with httpx client
901+
# Test with httpx client - increased timeout for slow APIs
902902
async with httpx.AsyncClient(
903-
timeout=60.0, verify=False, cert=client_cert
903+
timeout=180.0, verify=False, cert=client_cert
904904
) as client:
905905
if body.stream_mode:
906906
# Handle streaming response
@@ -919,7 +919,7 @@ async def test_api_endpoint_svc(request: Request, body: TaskCreateReq):
919919
logger.error("Request timeout when testing API endpoint")
920920
return {
921921
"status": "error",
922-
"error": "Request timeout (60 seconds)",
922+
"error": "Request timeout (180 seconds)",
923923
"response": None,
924924
}
925925
except httpx.ConnectError as e:

frontend/src/api/apiClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api';
1212
// Create an axios instance - Use a consistent BASE_URL
1313
const apiClient: AxiosInstance = axios.create({
1414
baseURL: BASE_URL,
15-
timeout: 30000,
15+
timeout: 150000, // Increase to 150 seconds (longer than backend 120s timeout)
1616
headers: {
1717
'Content-Type': 'application/json',
1818
},

frontend/src/components/CreateJobForm.tsx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,32 @@ const CreateJobFormContent: React.FC<CreateJobFormProps> = ({
382382

383383
setTestResult(result);
384384
setTestModalVisible(true);
385-
} catch (error) {
386-
message.error('Test failed, please check your configuration');
385+
} catch (error: any) {
386+
// Try to extract error message from backend response with priority order
387+
let errorMessage = 'Test failed, please check your configuration';
388+
389+
// Priority 1: Backend API error field (most specific)
390+
if (error?.response?.data?.error) {
391+
errorMessage = error.response.data.error;
392+
}
393+
// Priority 2: Backend API message field
394+
else if (error?.response?.data?.message) {
395+
errorMessage = error.response.data.message;
396+
}
397+
// Priority 3: Network timeout or connection errors
398+
else if (
399+
error?.code === 'ECONNABORTED' &&
400+
error?.message?.includes('timeout')
401+
) {
402+
errorMessage =
403+
'Network timeout, please check your connection and try again';
404+
}
405+
// Priority 4: Other axios errors
406+
else if (error?.message) {
407+
errorMessage = error.message;
408+
}
409+
410+
message.error(errorMessage);
387411
} finally {
388412
setTesting(false);
389413
}

frontend/src/pages/Results.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,25 @@ const TaskResults: React.FC = () => {
108108
);
109109
const failResult = results.find(item => item.metric_type === 'failure');
110110

111+
// Calculate total failed requests from multiple sources
112+
const calculateFailedRequests = () => {
113+
// Get failure requests from 'failure' metric type
114+
const failureMetricRequests = failResult?.request_count || 0;
115+
116+
// Get failure count from chat_completions or custom_api metric types
117+
const chatCompletionsResult = results.find(
118+
item => item.metric_type === 'chat_completions'
119+
);
120+
const customApiResult = results.find(
121+
item => item.metric_type === 'custom_api'
122+
);
123+
124+
const chatCompletionsFailures = chatCompletionsResult?.failure_count || 0;
125+
const customApiFailures = customApiResult?.failure_count || 0;
126+
127+
return failureMetricRequests + chatCompletionsFailures + customApiFailures;
128+
};
129+
111130
// Check if we have any valid test results
112131
const hasValidResults =
113132
CompletionResult || firstTokenResult || outputCompletionResult || TpsResult;
@@ -458,7 +477,7 @@ const TaskResults: React.FC = () => {
458477
CompletionResult?.request_count ||
459478
firstTokenResult?.request_count ||
460479
0;
461-
const failedRequestCount = failResult?.request_count || 0;
480+
const failedRequestCount = calculateFailedRequests();
462481
const actualTotalRequests = baseRequestCount + failedRequestCount;
463482
const actualSuccessRate =
464483
actualTotalRequests > 0
@@ -514,10 +533,12 @@ const TaskResults: React.FC = () => {
514533
</span>
515534
}
516535
value={
517-
(firstTokenResult?.avg_response_time || 0) / 1000 ||
518-
'-'
536+
firstTokenResult?.avg_response_time
537+
? (
538+
firstTokenResult.avg_response_time / 1000
539+
).toFixed(2)
540+
: '-'
519541
}
520-
precision={2}
521542
/>
522543
</Col>
523544
</Row>

frontend/src/utils/request.ts

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import axios from 'axios';
1010
// Create axios instance
1111
const request = axios.create({
1212
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
13-
timeout: 30000, // Increase to 30 seconds
13+
timeout: 150000, // Increase to 150 seconds (longer than backend 120s timeout)
1414
headers: {
1515
'Content-Type': 'application/json',
1616
'Cache-Control': 'no-cache, no-store, must-revalidate',
@@ -42,27 +42,21 @@ request.interceptors.response.use(
4242
return response.data;
4343
},
4444
error => {
45-
// Handle timeout error
46-
if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
47-
message.error('Request timeout, please try again later');
48-
} else if (error.response) {
49-
// Handle HTTP error status codes
50-
switch (error.response.status) {
51-
case 404:
52-
message.error('Requested resource not found');
53-
break;
54-
case 500:
55-
message.error('Internal server error');
56-
break;
57-
default:
58-
message.error(`Request failed: ${error.message}`);
59-
}
60-
} else {
61-
message.error(`Request error: ${error.message}`);
62-
}
45+
// Log error details for debugging
46+
console.error('Request error:', error);
47+
48+
// Don't show generic error messages here - let specific components handle their own errors
49+
// This allows components to extract and display backend error messages
6350

64-
// Show detailed error in console
65-
// Request error occurred
51+
// Only handle truly generic network errors that don't have response data
52+
if (
53+
!error.response &&
54+
error.code === 'ECONNABORTED' &&
55+
error.message.includes('timeout')
56+
) {
57+
// Only show generic timeout message if there's no backend response
58+
message.error('Network timeout, please check your connection');
59+
}
6660

6761
return Promise.reject(error);
6862
}

init_db.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ CREATE TABLE `tasks` (
1313
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
1414
`status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'idle',
1515
`target_host` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
16+
`api_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '/v1/chat/completions' COMMENT 'API path',
17+
`request_payload` longtext COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Custom request payload for non-chat completions APIs',
18+
`field_mapping` json DEFAULT NULL COMMENT 'Field mapping configuration for custom APIs',
1619
`model` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
1720
`system_prompt` longtext COLLATE utf8mb4_unicode_ci,
1821
`user_prompt` longtext COLLATE utf8mb4_unicode_ci,
@@ -30,9 +33,6 @@ CREATE TABLE `tasks` (
3033
`error_message` text COLLATE utf8mb4_unicode_ci,
3134
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
3235
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
33-
`api_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '/v1/chat/completions' COMMENT 'API path',
34-
`request_payload` longtext COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Custom request payload for non-chat completions APIs',
35-
`field_mapping` json DEFAULT NULL COMMENT 'Field mapping configuration for custom APIs',
3636
PRIMARY KEY (`id`),
3737
KEY `idx_status_created` (`status`,`created_at`),
3838
KEY `idx_name` (`name`),

0 commit comments

Comments
 (0)