Feat: Test Case and GenerateExample added

This commit is contained in:
DhumalePrasad04
2025-02-23 01:15:52 +05:30
parent e472ea77a8
commit 9aa209512c
2 changed files with 354 additions and 143 deletions

View File

@ -2,41 +2,211 @@ import 'dart:convert';
import 'package:ollama_dart/ollama_dart.dart'; import 'package:ollama_dart/ollama_dart.dart';
class OllamaService { class OllamaService {
final OllamaClient _client; final OllamaClient _client;
OllamaService() : _client = OllamaClient(baseUrl: 'http://127.0.0.1:11434/api'); OllamaService() : _client = OllamaClient(baseUrl: 'http://127.0.0.1:11434/api');
// Generate response // Generate response
Future<String> generateResponse(String prompt) async { Future<String> generateResponse(String prompt) async {
final response = await _client.generateCompletion( final response = await _client.generateCompletion(
request: GenerateCompletionRequest( request: GenerateCompletionRequest(
model: 'llama3.2:3b', model: 'llama3.2:1b',
prompt: prompt prompt: prompt
), ),
);
return response.response.toString();
}
// Explain latest API request & response
Future<String> explainLatestApi({required dynamic requestModel, required dynamic responseModel}) async {
if (requestModel == null || responseModel == null) {
return "No recent API requests found";
}
// Extract request details
final method = requestModel.httpRequestModel?.method
.toString()
.split('.')
.last
.toUpperCase()
?? "GET";
final endpoint = requestModel.httpRequestModel?.url ?? "Unknown endpoint";
final headers = requestModel.httpRequestModel?.enabledHeadersMap ?? {};
final parameters = requestModel.httpRequestModel?.enabledParamsMap ?? {};
final body = requestModel.httpRequestModel?.body;
// Process response
final rawResponse = responseModel.body;
final responseBody = rawResponse is String
? jsonDecode(rawResponse)
: rawResponse as Map<String, dynamic>?;
final statusCode = responseModel.statusCode ?? 0;
final prompt = '''
Analyze this API interaction
Current API Request:
- Endpoint: $endpoint
- Method: $method
- Headers: ${headers.isNotEmpty ? jsonEncode(headers) : "None"}
- Parameters: ${parameters.isNotEmpty ? jsonEncode(parameters) : "None"}
- Body: ${body ?? "None"}
Current Response:
- Status Code: $statusCode
- Response Body: ${responseBody != null ? jsonEncode(responseBody) : rawResponse}
Required Analysis Format:
1. Start with overall status assessment
2. List validation/security issues
3. Highlight request/response mismatches
4. Suggest concrete improvements
5. Use plain text formatting with clear section headers
Response Structure:
API Request: [request details]
Response: [response details]
Analysis: [structured analysis]''';
return generateResponse(prompt);
}
Future<String> debugApi({required dynamic requestModel, required dynamic responseModel}) async {
if (requestModel == null || responseModel == null) {
return "There are no recent API Requests to debug.";
}
final requestJson = jsonEncode(requestModel.toJson());
final responseJson = jsonEncode(responseModel.toJson());
final statusCode = responseModel.statusCode;
final prompt = '''
Provide detailed debugging steps for this failed API request:
**Status Code:** $statusCode
**Request Details:**
$requestJson
**Response Details:**
$responseJson
Provide a step-by-step debugging guide including:
1. Common causes for this status code
2. Specific issues in the request
3. Potential fixes
4. Recommended next steps
Format the response with clear headings and bullet points.
''';
return generateResponse(prompt);
}
Future<String> generateTestCases({required dynamic requestModel, required dynamic responseModel}) async {
final endpoint = requestModel.httpRequestModel?.url ?? "Unknown endpoint";
final headers = requestModel.httpRequestModel?.enabledHeadersMap ?? {};
final parameters = requestModel.httpRequestModel?.enabledParamsMap ?? {};
final body = requestModel.httpRequestModel?.body;
final method = requestModel.httpRequestModel?.method.toString().split('.').last.toUpperCase() ?? "GET";
// Process response
// final rawResponse = responseModel.body;
// final responseBody = rawResponse is String
// ? jsonDecode(rawResponse)
// : rawResponse as Map<String, dynamic>?;
final statusCode = responseModel.statusCode ?? 0;
final exampleParams = await generateExampleParams(
requestModel: requestModel,
responseModel: responseModel,
); );
return response.response.toString();
}
// Explain latest API request & response
Future<String> explainLatestApi({required dynamic requestModel, required dynamic responseModel}) async {
if (requestModel == null || responseModel == null) {
return "There are no recent API Requests.";
}
final requestJson = jsonEncode(requestModel.toJson());
final responseJson = jsonEncode(responseModel.toJson());
final prompt = ''' final prompt = '''
Explain the API request and response in a simple way: Generate test cases for the following API:
**Request Details:** **API Request:**
$requestJson - **Endpoint:** `$endpoint`
- **Method:** `$method`
- **Headers:** ${headers.isNotEmpty ? jsonEncode(headers) : "None"}
- **Parameters:** ${parameters.isNotEmpty ? jsonEncode(parameters) : "None"}
**Response Details:** **Test Case Requirements:**
$responseJson 1. Normal case (valid input, expected success)
2. Edge case (unexpected or boundary values)
3. Missing required parameters
4. Invalid authentication (if applicable)
5. Error handling for different status codes
**Example Test Case Format:**
@Test
void testValidRequest() {
final response = sendRequest("$endpoint", method: "$method", params: $exampleParams);
assert(response.status == 200);
}
\`\`\`
Generate test cases covering all scenarios.
''';
Please provide a brief and clear explanation with key insights.
''';
return generateResponse(prompt); return generateResponse(prompt);
} }
/// Generate example parameter values based on parameter names
Future<Map<String, dynamic>> generateExampleParams({
required dynamic requestModel,
required dynamic responseModel,
}) async {
final ollamaService = OllamaService();
final String apiEndpoint = requestModel.httpRequestModel?.url ?? "Unknown Endpoint";
final String apiMethod = requestModel.httpRequestModel?.method.name ?? "GET";
final Map<String, String> apiHeaders = requestModel.httpRequestModel?.enabledHeadersMap ?? {};
final Map<String, String> apiParams = requestModel.httpRequestModel?.enabledParamsMap ?? {};
final String? apiBody = requestModel.httpRequestModel?.body;
final dynamic rawResponse = responseModel?.body;
final Map<String, dynamic>? apiResponse =
(rawResponse is String) ? jsonDecode(rawResponse) : rawResponse is Map<String, dynamic> ? rawResponse : null;
// Construct LLM prompt to analyze and extract meaningful test cases
final String prompt = '''
Analyze the following API request and generate structured example parameters.
**API Request:**
- **Endpoint:** `$apiEndpoint`
- **Method:** `$apiMethod`
- **Headers:** ${apiHeaders.isNotEmpty ? jsonEncode(apiHeaders) : "None"}
- **Parameters:** ${apiParams.isNotEmpty ? jsonEncode(apiParams) : "None"}
- **Body:** ${apiBody ?? "None"}
**Response:**
- **Status Code:** ${responseModel?.statusCode ?? "Unknown"}
- **Response Body:** ${apiResponse != null ? jsonEncode(apiResponse) : rawResponse}
### **Required Output Format**
1. **Standard Example Values**: Assign the most appropriate example values for each parameter.
2. **Edge Cases**: Provide at least 2 edge cases per parameter.
3. **Invalid Cases**: Generate invalid inputs for error handling.
4. **Output must be in valid JSON format.**
''';
// Force LLM to return structured JSON output
final String response = await ollamaService.generateResponse(prompt);
try {
return jsonDecode(response) as Map<String, dynamic>;
} catch (e) {
return {"error": "Failed to parse response from LLM."};
}
}
} }

View File

@ -3,140 +3,181 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart'; import 'package:apidash/providers/providers.dart';
class ChatbotWidget extends ConsumerStatefulWidget { class ChatbotWidget extends ConsumerStatefulWidget {
const ChatbotWidget({Key? key}) : super(key: key); const ChatbotWidget({Key? key}) : super(key: key);
@override @override
_ChatbotWidgetState createState() => _ChatbotWidgetState(); _ChatbotWidgetState createState() => _ChatbotWidgetState();
} }
class _ChatbotWidgetState extends ConsumerState<ChatbotWidget> { class _ChatbotWidgetState extends ConsumerState<ChatbotWidget> {
final TextEditingController _controller = TextEditingController(); final TextEditingController _controller = TextEditingController();
final List<Map<String, dynamic>> _messages = []; final List<Map<String, dynamic>> _messages = [];
bool _isLoading = false; bool _isLoading = false;
void _sendMessage(String message) async { void _sendMessage(String message) async {
if (message.trim().isEmpty) return; if (message.trim().isEmpty) return;
final ollamaService = ref.read(ollamaServiceProvider); final ollamaService = ref.read(ollamaServiceProvider);
final requestModel = ref.read(selectedRequestModelProvider); final requestModel = ref.read(selectedRequestModelProvider);
final responseModel = requestModel?.httpResponseModel; final responseModel = requestModel?.httpResponseModel;
setState(() {
_messages.add({'role': 'user', 'message': message});
_controller.clear();
_isLoading = true;
});
try { setState(() {
String response; _messages.add({'role': 'user', 'message': message});
if (message == "Explain API") { _controller.clear();
response = await ollamaService.explainLatestApi( _isLoading = true;
requestModel: requestModel, });
responseModel: responseModel,
);
} else {
response = await ollamaService.generateResponse(message);
}
setState(() { try {
_messages.add({'role': 'bot', 'message': response}); String response;
}); if (message == "Explain API") {
} catch (error) { response = await ollamaService.explainLatestApi(
setState(() { requestModel: requestModel,
_messages.add({'role': 'bot', 'message': "Error: ${error.toString()}"}); responseModel: responseModel,
});
} finally {
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return Container(
height: 400,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: const [
BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 4)),
],
),
child: Column(
children: [
Row(
children: [
ElevatedButton.icon(
onPressed: () => _sendMessage("Explain API"),
icon: const Icon(Icons.info_outline),
label: const Text("Explain API"),
),
const Spacer(),
],
),
Expanded(
child: ListView.builder(
reverse: true,
itemCount: _messages.length,
itemBuilder: (context, index) {
final message = _messages.reversed.toList()[index];
return ChatBubble(
message: message['message'],
isUser: message['role'] == 'user',
);
},
),
),
if (_isLoading)
const Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
),
Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Ask something...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8)),
),
onSubmitted: _sendMessage,
),
),
IconButton(
icon: const Icon(Icons.send),
onPressed: () => _sendMessage(_controller.text),
),
],
),
],
),
); );
} }
else if (message == "Debug API") {
response = await ollamaService.debugApi(
requestModel: requestModel,
responseModel: responseModel,
);
} else if (message == "Generate Test Case") {
response = await ollamaService.generateTestCases(requestModel: requestModel,
responseModel: responseModel,);
}
else {
response = await ollamaService.generateResponse(message);
}
setState(() {
_messages.add({'role': 'bot', 'message': response});
});
} catch (error) {
setState(() {
_messages.add({'role': 'bot', 'message': "Error: ${error.toString()}"});
});
} finally {
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
final requestModel = ref.watch(selectedRequestModelProvider);
final statusCode = requestModel?.httpResponseModel?.statusCode;
final showDebugButton = statusCode != null && statusCode >= 400;
return Container(
height: 400,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: const [
BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 4)),
],
),
child: Column(
children: [
Row(
children: [
ElevatedButton.icon(
onPressed: () => _sendMessage("Explain API"),
icon: const Icon(Icons.info_outline),
label: const Text("Explain API"),
),
if (showDebugButton) ...[
const SizedBox(width: 8),
ElevatedButton.icon(
onPressed: () => _sendMessage("Debug API"),
icon: const Icon(Icons.bug_report),
label: const Text("Debug"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.redAccent,
),
),
],
const SizedBox(width: 8),
ElevatedButton.icon(
onPressed: () => _sendMessage("Generate Test Case"),
icon: const Icon(Icons.developer_mode),
label: const Text("Test Case"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueAccent,
),
),
const Spacer(),
],
),
Expanded(
child: ListView.builder(
reverse: true,
itemCount: _messages.length,
itemBuilder: (context, index) {
final message = _messages.reversed.toList()[index];
return ChatBubble(
message: message['message'],
isUser: message['role'] == 'user',
);
},
),
),
if (_isLoading)
const Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
),
Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Ask something...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8)),
),
onSubmitted: _sendMessage,
),
),
IconButton(
icon: const Icon(Icons.send),
onPressed: () => _sendMessage(_controller.text),
),
],
),
],
),
);
}
} }
class ChatBubble extends StatelessWidget { class ChatBubble extends StatelessWidget {
final String message; final String message;
final bool isUser; final bool isUser;
const ChatBubble({super.key, required this.message, this.isUser = false}); const ChatBubble({super.key, required this.message, this.isUser = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Align( return Align(
alignment: isUser ? Alignment.centerRight : Alignment.centerLeft, alignment: isUser ? Alignment.centerRight : Alignment.centerLeft,
child: Container( child: Container(
margin: const EdgeInsets.symmetric(vertical: 4), margin: const EdgeInsets.symmetric(vertical: 4),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isUser color: isUser
? Theme.of(context).colorScheme.primaryContainer ? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.secondaryContainer, : Theme.of(context).colorScheme.secondaryContainer,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Text(message), child: Text(message),
), ),
); );
} }
} }