diff --git a/lib/services/ollama_service.dart b/lib/services/ollama_service.dart index f10508b3..fccade4d 100644 --- a/lib/services/ollama_service.dart +++ b/lib/services/ollama_service.dart @@ -6,46 +6,43 @@ final OllamaClient _client; OllamaService() : _client = OllamaClient(baseUrl: 'http://127.0.0.1:11434/api'); -// Generate response -Future generateResponse(String prompt) async { -final response = await _client.generateCompletion( -request: GenerateCompletionRequest( -model: 'llama3.2:1b', -prompt: prompt -), -); -return response.response.toString(); -} + // Generate response + Future generateResponse(String prompt) async { + final response = await _client.generateCompletion( + request: GenerateCompletionRequest( + model: 'llama3.2:1b', + prompt: prompt + ), + ); + return response.response.toString(); + } -// Explain latest API request & response -Future explainLatestApi({required dynamic requestModel, required dynamic responseModel}) async { -if (requestModel == null || responseModel == null) { -return "No recent API requests found"; -} + // Explain latest API request & response + Future 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; + // 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?; -final statusCode = responseModel.statusCode ?? 0; + // Process response + final rawResponse = responseModel.body; + final responseBody = rawResponse is String; + final statusCode = responseModel.statusCode ?? 0; + final prompt = ''' +Analyze this API interaction following these examples: - - final prompt = ''' - Analyze this API interaction Current API Request: - Endpoint: $endpoint - Method: $method @@ -55,11 +52,9 @@ Current API Request: Current Response: - Status Code: $statusCode -- Response Body: ${responseBody != null ? jsonEncode(responseBody) : rawResponse} - +- Response Body: ${jsonEncode(responseBody)} Required Analysis Format: - 1. Start with overall status assessment 2. List validation/security issues 3. Highlight request/response mismatches @@ -71,139 +66,123 @@ API Request: [request details] Response: [response details] Analysis: [structured analysis]'''; + return generateResponse(prompt); + } -return generateResponse(prompt); -} + Future debugApi({required dynamic requestModel, required dynamic responseModel}) async { + if (requestModel == null || responseModel == null) { + return "There are no recent API Requests to debug."; + } -Future 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 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. + '''; -final prompt = ''' -Provide detailed debugging steps for this failed API request: + return generateResponse(prompt); + } -**Status Code:** $statusCode -**Request Details:** -$requestJson + Future generateTestCases({required dynamic requestModel, required dynamic responseModel}) async { -**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 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?; - final statusCode = responseModel.statusCode ?? 0; - final exampleParams = await generateExampleParams( + 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; + final responsebody=responseModel.body; + final exampleParams = await generateExampleParams( requestModel: requestModel, responseModel: responseModel, ); final prompt = ''' -Generate test cases for the following API: - **API Request:** - **Endpoint:** `$endpoint` - **Method:** `$method` - **Headers:** ${headers.isNotEmpty ? jsonEncode(headers) : "None"} - **Parameters:** ${parameters.isNotEmpty ? jsonEncode(parameters) : "None"} +-**body:** ${body ?? "None"} -**Test Case Requirements:** -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 +here is an example test case for the given:$exampleParams -**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. +**Instructions:** +- Generate example parameter values for the request. +-Generate the url of as i provided in the api reuest +-generate same to same type of test case url for test purpose '''; return generateResponse(prompt); } /// Generate example parameter values based on parameter names - Future> 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 apiHeaders = requestModel.httpRequestModel?.enabledHeadersMap ?? {}; - final Map apiParams = requestModel.httpRequestModel?.enabledParamsMap ?? {}; - final String? apiBody = requestModel.httpRequestModel?.body; + Future> generateExampleParams({required dynamic requestModel, required dynamic responseModel,}) async { + final ollamaService = OllamaService(); - final dynamic rawResponse = responseModel?.body; - final Map? apiResponse = - (rawResponse is String) ? jsonDecode(rawResponse) : rawResponse is Map ? rawResponse : null; + 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; - // Construct LLM prompt to analyze and extract meaningful test cases - final String prompt = ''' + + final dynamic rawResponse = responseModel?.body; + final Map? apiResponse = + (rawResponse is String) ? jsonDecode(rawResponse) : rawResponse is Map ? 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"} +- **Endpoint:** `$endpoint` +- **Method:** `$method` +- **Headers:** ${headers.isNotEmpty ? jsonEncode(headers) : "None"} +- **Parameters:** ${parameters.isNotEmpty ? jsonEncode(parameters) : "None"} +- **Body:** ${body ?? "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.** +**Instructions:** +- Generate example parameter values for the request. +-Generate the url of as i provided in the api reuest +generate same to same type of test case url for test purpose + '''; - // Force LLM to return structured JSON output - final String response = await ollamaService.generateResponse(prompt); + // Force LLM to return structured JSON output + final String response = await ollamaService.generateResponse(prompt); + + try { + return jsonDecode(response) as Map; + } catch (e) { + return {"error": "Failed to parse response from LLM."}; + } - try { - return jsonDecode(response) as Map; - } catch (e) { - return {"error": "Failed to parse response from LLM."}; } - } - -} \ No newline at end of file diff --git a/lib/widgets/chatbot_widget.dart b/lib/widgets/chatbot_widget.dart index ae41fc27..6f53fb1f 100644 --- a/lib/widgets/chatbot_widget.dart +++ b/lib/widgets/chatbot_widget.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; + class ChatbotWidget extends ConsumerStatefulWidget { const ChatbotWidget({Key? key}) : super(key: key); @@ -27,26 +29,16 @@ setState(() { _isLoading = true; }); -try { - String response; - if (message == "Explain API") { - response = await ollamaService.explainLatestApi( - requestModel: requestModel, - responseModel: responseModel, - ); - } - 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,); + try { + String response; + if (message == "Explain API") { + response = await ollamaService.explainLatestApi( + requestModel: requestModel, + responseModel: responseModel, + ); + } else { + response = await ollamaService.generateResponse(message); } - else { - response = await ollamaService.generateResponse(message); - } setState(() { _messages.add({'role': 'bot', 'message': response}); @@ -62,99 +54,71 @@ try { } -@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( + @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: [ - 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), + Row( + children: [ ElevatedButton.icon( - onPressed: () => _sendMessage("Generate Test Case"), - icon: const Icon(Icons.developer_mode), - label: const Text("Test Case"), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blueAccent, + 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, ), ), - - 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)), + IconButton( + icon: const Icon(Icons.send), + onPressed: () => _sendMessage(_controller.text), ), - onSubmitted: _sendMessage, - ), - ), - IconButton( - icon: const Icon(Icons.send), - onPressed: () => _sendMessage(_controller.text), + ], ), ], ), - ], - ), -); - - - -} + ); + } } class ChatBubble extends StatelessWidget { @@ -163,21 +127,21 @@ final bool isUser; const ChatBubble({super.key, required this.message, this.isUser = false}); -@override -Widget build(BuildContext context) { -return Align( -alignment: isUser ? Alignment.centerRight : Alignment.centerLeft, -child: Container( -margin: const EdgeInsets.symmetric(vertical: 4), -padding: const EdgeInsets.all(12), -decoration: BoxDecoration( -color: isUser -? Theme.of(context).colorScheme.primaryContainer -: Theme.of(context).colorScheme.secondaryContainer, -borderRadius: BorderRadius.circular(8), -), -child: Text(message), -), -); -} + @override + Widget build(BuildContext context) { + return Align( + alignment: isUser ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 4), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: isUser + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.secondaryContainer, + borderRadius: BorderRadius.circular(8), + ), + child: Text(message), + ), + ); + } } \ No newline at end of file