Merge branch 'ChatBotFeature'

This commit is contained in:
DhumalePrasad04
2025-02-23 10:46:35 +05:30
2 changed files with 203 additions and 260 deletions

View File

@ -6,46 +6,43 @@ 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:1b', model: 'llama3.2:1b',
prompt: prompt prompt: prompt
), ),
); );
return response.response.toString(); return response.response.toString();
} }
// Explain latest API request & response // Explain latest API request & response
Future<String> explainLatestApi({required dynamic requestModel, required dynamic responseModel}) async { Future<String> explainLatestApi({required dynamic requestModel, required dynamic responseModel}) async {
if (requestModel == null || responseModel == null) { if (requestModel == null || responseModel == null) {
return "No recent API requests found"; return "No recent API requests found";
} }
// Extract request details // Extract request details
final method = requestModel.httpRequestModel?.method final method = requestModel.httpRequestModel?.method
.toString() .toString()
.split('.') .split('.')
.last .last
.toUpperCase() .toUpperCase()
?? "GET"; ?? "GET";
final endpoint = requestModel.httpRequestModel?.url ?? "Unknown endpoint"; final endpoint = requestModel.httpRequestModel?.url ?? "Unknown endpoint";
final headers = requestModel.httpRequestModel?.enabledHeadersMap ?? {}; final headers = requestModel.httpRequestModel?.enabledHeadersMap ?? {};
final parameters = requestModel.httpRequestModel?.enabledParamsMap ?? {}; final parameters = requestModel.httpRequestModel?.enabledParamsMap ?? {};
final body = requestModel.httpRequestModel?.body; final body = requestModel.httpRequestModel?.body;
// Process response // Process response
final rawResponse = responseModel.body; final rawResponse = responseModel.body;
final responseBody = rawResponse is String final responseBody = rawResponse is String;
? jsonDecode(rawResponse) final statusCode = responseModel.statusCode ?? 0;
: rawResponse as Map<String, dynamic>?;
final statusCode = responseModel.statusCode ?? 0;
final prompt = '''
Analyze this API interaction following these examples:
final prompt = '''
Analyze this API interaction
Current API Request: Current API Request:
- Endpoint: $endpoint - Endpoint: $endpoint
- Method: $method - Method: $method
@ -55,11 +52,9 @@ Current API Request:
Current Response: Current Response:
- Status Code: $statusCode - Status Code: $statusCode
- Response Body: ${responseBody != null ? jsonEncode(responseBody) : rawResponse} - Response Body: ${jsonEncode(responseBody)}
Required Analysis Format: Required Analysis Format:
1. Start with overall status assessment 1. Start with overall status assessment
2. List validation/security issues 2. List validation/security issues
3. Highlight request/response mismatches 3. Highlight request/response mismatches
@ -71,139 +66,123 @@ API Request: [request details]
Response: [response details] Response: [response details]
Analysis: [structured analysis]'''; Analysis: [structured analysis]''';
return generateResponse(prompt);
}
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.";
}
Future<String> debugApi({required dynamic requestModel, required dynamic responseModel}) async { final requestJson = jsonEncode(requestModel.toJson());
if (requestModel == null || responseModel == null) { final responseJson = jsonEncode(responseModel.toJson());
return "There are no recent API Requests to debug."; final statusCode = responseModel.statusCode;
}
final requestJson = jsonEncode(requestModel.toJson()); final prompt = '''
final responseJson = jsonEncode(responseModel.toJson()); Provide detailed debugging steps for this failed API request:
final statusCode = responseModel.statusCode;
**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.
''';
final prompt = ''' return generateResponse(prompt);
Provide detailed debugging steps for this failed API request: }
**Status Code:** $statusCode Future<String> generateTestCases({required dynamic requestModel, required dynamic responseModel}) async {
**Request Details:**
$requestJson
**Response Details:** final method = requestModel.httpRequestModel?.method
$responseJson .toString()
.split('.')
Provide a step-by-step debugging guide including: .last
1. Common causes for this status code .toUpperCase()
2. Specific issues in the request ?? "GET";
3. Potential fixes final endpoint = requestModel.httpRequestModel?.url ?? "Unknown endpoint";
4. Recommended next steps final headers = requestModel.httpRequestModel?.enabledHeadersMap ?? {};
final parameters = requestModel.httpRequestModel?.enabledParamsMap ?? {};
Format the response with clear headings and bullet points. final body = requestModel.httpRequestModel?.body;
'''; final responsebody=responseModel.body;
final exampleParams = await generateExampleParams(
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, requestModel: requestModel,
responseModel: responseModel, responseModel: responseModel,
); );
final prompt = ''' final prompt = '''
Generate test cases for the following API:
**API Request:** **API Request:**
- **Endpoint:** `$endpoint` - **Endpoint:** `$endpoint`
- **Method:** `$method` - **Method:** `$method`
- **Headers:** ${headers.isNotEmpty ? jsonEncode(headers) : "None"} - **Headers:** ${headers.isNotEmpty ? jsonEncode(headers) : "None"}
- **Parameters:** ${parameters.isNotEmpty ? jsonEncode(parameters) : "None"} - **Parameters:** ${parameters.isNotEmpty ? jsonEncode(parameters) : "None"}
-**body:** ${body ?? "None"}
**Test Case Requirements:** here is an example test case for the given:$exampleParams
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:** **Instructions:**
@Test - Generate example parameter values for the request.
void testValidRequest() { -Generate the url of as i provided in the api reuest
final response = sendRequest("$endpoint", method: "$method", params: $exampleParams); -generate same to same type of test case url for test purpose
assert(response.status == 200);
}
\`\`\`
Generate test cases covering all scenarios.
'''; ''';
return generateResponse(prompt); return generateResponse(prompt);
} }
/// Generate example parameter values based on parameter names /// Generate example parameter values based on parameter names
Future<Map<String, dynamic>> generateExampleParams({ Future<Map<String, dynamic>> generateExampleParams({required dynamic requestModel, required dynamic responseModel,}) async {
required dynamic requestModel, final ollamaService = OllamaService();
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 method = requestModel.httpRequestModel?.method
final Map<String, dynamic>? apiResponse = .toString()
(rawResponse is String) ? jsonDecode(rawResponse) : rawResponse is Map<String, dynamic> ? rawResponse : null; .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;
// Construct LLM prompt to analyze and extract meaningful test cases
final String prompt = ''' 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. Analyze the following API request and generate structured example parameters.
**API Request:** **API Request:**
- **Endpoint:** `$apiEndpoint` - **Endpoint:** `$endpoint`
- **Method:** `$apiMethod` - **Method:** `$method`
- **Headers:** ${apiHeaders.isNotEmpty ? jsonEncode(apiHeaders) : "None"} - **Headers:** ${headers.isNotEmpty ? jsonEncode(headers) : "None"}
- **Parameters:** ${apiParams.isNotEmpty ? jsonEncode(apiParams) : "None"} - **Parameters:** ${parameters.isNotEmpty ? jsonEncode(parameters) : "None"}
- **Body:** ${apiBody ?? "None"} - **Body:** ${body ?? "None"}
**Response:**
- **Status Code:** ${responseModel?.statusCode ?? "Unknown"}
- **Response Body:** ${apiResponse != null ? jsonEncode(apiResponse) : rawResponse}
### **Required Output Format** **Instructions:**
1. **Standard Example Values**: Assign the most appropriate example values for each parameter. - Generate example parameter values for the request.
2. **Edge Cases**: Provide at least 2 edge cases per parameter. -Generate the url of as i provided in the api reuest
3. **Invalid Cases**: Generate invalid inputs for error handling. generate same to same type of test case url for test purpose
4. **Output must be in valid JSON format.**
'''; ''';
// Force LLM to return structured JSON output // Force LLM to return structured JSON output
final String response = await ollamaService.generateResponse(prompt); 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."};
}
try {
return jsonDecode(response) as Map<String, dynamic>;
} catch (e) {
return {"error": "Failed to parse response from LLM."};
} }
} }
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart'; import 'package:apidash/providers/providers.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
class ChatbotWidget extends ConsumerStatefulWidget { class ChatbotWidget extends ConsumerStatefulWidget {
const ChatbotWidget({Key? key}) : super(key: key); const ChatbotWidget({Key? key}) : super(key: key);
@ -27,26 +29,16 @@ setState(() {
_isLoading = true; _isLoading = true;
}); });
try { try {
String response; String response;
if (message == "Explain API") { if (message == "Explain API") {
response = await ollamaService.explainLatestApi( response = await ollamaService.explainLatestApi(
requestModel: requestModel, requestModel: requestModel,
responseModel: responseModel, responseModel: responseModel,
); );
} } else {
else if (message == "Debug API") { response = await ollamaService.generateResponse(message);
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(() { setState(() {
_messages.add({'role': 'bot', 'message': response}); _messages.add({'role': 'bot', 'message': response});
@ -62,99 +54,71 @@ try {
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final requestModel = ref.watch(selectedRequestModelProvider); return Container(
final statusCode = requestModel?.httpResponseModel?.statusCode; height: 400,
final showDebugButton = statusCode != null && statusCode >= 400; padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
return Container( color: Theme.of(context).colorScheme.surface,
height: 400, borderRadius: BorderRadius.circular(12),
padding: const EdgeInsets.all(16), boxShadow: const [
decoration: BoxDecoration( BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 4)),
color: Theme.of(context).colorScheme.surface, ],
borderRadius: BorderRadius.circular(12), ),
boxShadow: const [ child: Column(
BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 4)),
],
),
child: Column(
children: [
Row(
children: [ children: [
ElevatedButton.icon( Row(
onPressed: () => _sendMessage("Explain API"), children: [
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( ElevatedButton.icon(
onPressed: () => _sendMessage("Generate Test Case"), onPressed: () => _sendMessage("Explain API"),
icon: const Icon(Icons.developer_mode), icon: const Icon(Icons.info_outline),
label: const Text("Test Case"), label: const Text("Explain API"),
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(
const Spacer(), icon: const Icon(Icons.send),
], onPressed: () => _sendMessage(_controller.text),
),
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 {
@ -163,21 +127,21 @@ 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),
), ),
); );
} }
} }