From d0a1d9e122a57223e363f157b6bc3b86e859c91f Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Tue, 18 Feb 2025 22:43:06 +0530 Subject: [PATCH 01/11] Update CONTRIBUTING.md --- CONTRIBUTING.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ea4e8f49..adc1d89a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,12 +11,18 @@ You can contribute to the project in any or all of the following ways: - Add documentation - Add a new feature, resolve an existing issue or add a new test to the project. (Goto [Code Contribution Guidelines](#code-contribution-guidelines)). +## Resources for New Contributors + +- API Dash Code Walkthrough - [Video](https://www.youtube.com/live/rIlwCTKNz-A?si=iMxTxzkpY_ySo4Ow&t=339) +- Getting Started with Flutter - [Video](https://www.youtube.com/watch?v=8K2gV1P6ZHI) +- API Dash Developer Guide - [Read](https://github.com/foss42/apidash/blob/main/doc/dev_guide/README.md) + +## Code Contribution Guidelines + ### I have not contributed to any open source project before. Will I get any guidance? In case you are new to the open source ecosystem, we would be more than happy to guide you through the entire process. Just join our [Discord server](https://bit.ly/heyfoss) and drop a message in the **#foss** channel. -## Code Contribution Guidelines - ### Some things to keep in mind before opening a PR > PRs with precise changes (like adding a new test, resolving a bug/issue, adding a new feature) are always preferred over a single PR with a ton of file changes as they are easier to review and merge. From 0d893c0f7a0e9a32296a19674fe1e8d32b8b2f3b Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Thu, 20 Feb 2025 10:16:27 +0530 Subject: [PATCH 02/11] Update CONTRIBUTING.md --- CONTRIBUTING.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index adc1d89a..ef1c9e36 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,6 +19,17 @@ You can contribute to the project in any or all of the following ways: ## Code Contribution Guidelines +### Why we do not assign issues anyone? + +- By not assigning issues upfront, anyone can feel welcome to contribute without feeling like the issue is already "taken." +- This also prevents discouraging new contributors who might feel locked out if issues are pre-assigned. +- Contributors are encouraged to pick issues that align with their skills and interests. To take initiative rather than waiting for permission or being "assigned" work. +- Sometimes contributors express interest but never follow through. If issues are assigned prematurely, others might avoid working on them, delaying progress. +- Leaving issues unassigned ensures that work can proceed without bottlenecks if someone goes inactive. +- Open issues encourage community discussion and brainstorming. Prematurely assigning an issue can stifle input from others who might have better ideas or solutions. +- As open-source work is often voluntary, and contributors' availability can change. Keeping issues unassigned allows anyone to step in if the original contributor becomes unavailable. +This also supports multiple contributors collaborating on larger or complex issues. + ### I have not contributed to any open source project before. Will I get any guidance? In case you are new to the open source ecosystem, we would be more than happy to guide you through the entire process. Just join our [Discord server](https://bit.ly/heyfoss) and drop a message in the **#foss** channel. From 89f12f8810f55328b40bc7e98e2c076559ca1a93 Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Thu, 20 Feb 2025 10:16:39 +0530 Subject: [PATCH 03/11] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ef1c9e36..5a1ff5aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ You can contribute to the project in any or all of the following ways: ## Code Contribution Guidelines -### Why we do not assign issues anyone? +### Why we do not assign issues to anyone? - By not assigning issues upfront, anyone can feel welcome to contribute without feeling like the issue is already "taken." - This also prevents discouraging new contributors who might feel locked out if issues are pre-assigned. From 922ecd9768c3f66fc61256b9e157aa8feadd3f22 Mon Sep 17 00:00:00 2001 From: HydrogenIITG Date: Fri, 21 Feb 2025 18:03:19 +0530 Subject: [PATCH 04/11] added android specific instructions --- .../platform_specific_instructions.md | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/doc/dev_guide/platform_specific_instructions.md b/doc/dev_guide/platform_specific_instructions.md index 32016e06..df469e6a 100644 --- a/doc/dev_guide/platform_specific_instructions.md +++ b/doc/dev_guide/platform_specific_instructions.md @@ -27,6 +27,18 @@ In case you are having a local build failure on macOS due to "audio_session" do ## Android (Work in Progress) +- If any case targeting the Android API level below 21 or the project and the libraries it references exceed 65,536 methods, you encounter the following build error that indicates your app has reached the limit of the Android build architecture: +``` +trouble writing output: +Too many field references: 131000; max is 65536. +You may try using --multi-dex option. +``` +OR +``` +Conversion to Dalvik format failed: +Unable to execute dex: method ID not in [0, 0xffff]: 65536 +``` +To solve this problem, Add the `multiDexEnabled true` line to the `defaultConfig` section at `android/app/build.gradle file` ``` @@ -39,7 +51,33 @@ android { } ``` -For more information on multidex support, you can refer to the Android developer guide on [Configuring Multidex](https://developer.android.com/studio/build/multidex). +For more information on multidex support, you can refer to the Android developer guide on [Configuring Multidex](https://developer.android.com/studio/build/multidex). + +- If you are experiencing build failure issues while debugging due to Gradle/JDK/AGP version resolving try the following + upgrading gradle version by CLI command +``` +gradle wrapper --gradle-version +``` +In any case above command fails then you need to edit the Gradle distribution reference in the `gradle/wrapper/gradle-wrapper.properties` file. The following example sets the Gradle version to 8.8 in the `gradle-wrapper.properties` file. +``` +... +distributionUrl = https\://services.gradle.org/distributions/gradle-8.8-bin.zip +... +``` +upgrade AGP by specifying the plugin version in the top-level `build.gradle` file. The following example sets the plugin to version 8.8.0 from the `build.gradle` file: +``` +plugins { +... +id 'com.android.application' version '8.8.0' apply false +id 'com.android.library' version '8.8.0' apply false +... +} + +``` +For more information on Gradle and Java version compatibility, you can refer to [Compatibility Matrix](https://docs.gradle.org/current/userguide/compatibility.html). +For more information on Gradle and Android Gradle Plugin compatibility, you can refer to [Update Gradle](https://developer.android.com/build/releases/gradle-plugin). +Note : It is highly recommended that always ensure gradle and agp versions are compatible with your JDK version not the vice-versa and having atleast JDK 17 is recommmended. + ## Web From 7eaa494d85793a0c0f360b5141eaf7e2100bbf60 Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 22 Feb 2025 08:49:46 +0530 Subject: [PATCH 05/11] Update platform_specific_instructions.md --- .../platform_specific_instructions.md | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/doc/dev_guide/platform_specific_instructions.md b/doc/dev_guide/platform_specific_instructions.md index df469e6a..df243103 100644 --- a/doc/dev_guide/platform_specific_instructions.md +++ b/doc/dev_guide/platform_specific_instructions.md @@ -27,19 +27,22 @@ In case you are having a local build failure on macOS due to "audio_session" do ## Android (Work in Progress) -- If any case targeting the Android API level below 21 or the project and the libraries it references exceed 65,536 methods, you encounter the following build error that indicates your app has reached the limit of the Android build architecture: +In case you are targeting the Android API level <21 or the project and the libraries it references exceed 65,536 methods, you encounter the following build error that indicates your app has reached the limit of the Android build architecture: + ``` trouble writing output: Too many field references: 131000; max is 65536. You may try using --multi-dex option. ``` + OR + ``` Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536 ``` -To solve this problem, -Add the `multiDexEnabled true` line to the `defaultConfig` section at `android/app/build.gradle file` + +To solve this problem, add the `multiDexEnabled true` line to the `defaultConfig` section at `android/app/build.gradle file` ``` android { @@ -53,18 +56,22 @@ android { For more information on multidex support, you can refer to the Android developer guide on [Configuring Multidex](https://developer.android.com/studio/build/multidex). -- If you are experiencing build failure issues while debugging due to Gradle/JDK/AGP version resolving try the following - upgrading gradle version by CLI command +If you are experiencing build failure issues while debugging due to Gradle/JDK/AGP version resolving try upgrading the gradle version by CLI command + ``` gradle wrapper --gradle-version ``` -In any case above command fails then you need to edit the Gradle distribution reference in the `gradle/wrapper/gradle-wrapper.properties` file. The following example sets the Gradle version to 8.8 in the `gradle-wrapper.properties` file. + +In case the above command fails, edit the Gradle distribution reference in the `gradle/wrapper/gradle-wrapper.properties` file. The following example sets the Gradle version to 8.8 in the `gradle-wrapper.properties` file. + ``` ... distributionUrl = https\://services.gradle.org/distributions/gradle-8.8-bin.zip ... ``` -upgrade AGP by specifying the plugin version in the top-level `build.gradle` file. The following example sets the plugin to version 8.8.0 from the `build.gradle` file: + +Upgrade AGP by specifying the plugin version in the top-level `build.gradle` file. The following example sets the plugin to version 8.8.0 from the `build.gradle` file: + ``` plugins { ... @@ -72,12 +79,13 @@ id 'com.android.application' version '8.8.0' apply false id 'com.android.library' version '8.8.0' apply false ... } - ``` -For more information on Gradle and Java version compatibility, you can refer to [Compatibility Matrix](https://docs.gradle.org/current/userguide/compatibility.html). -For more information on Gradle and Android Gradle Plugin compatibility, you can refer to [Update Gradle](https://developer.android.com/build/releases/gradle-plugin). -Note : It is highly recommended that always ensure gradle and agp versions are compatible with your JDK version not the vice-versa and having atleast JDK 17 is recommmended. +For more information on: +- Gradle and Java version compatibility, you can refer to [Compatibility Matrix](https://docs.gradle.org/current/userguide/compatibility.html). +- Gradle and Android Gradle Plugin compatibility, you can refer to [Update Gradle](https://developer.android.com/build/releases/gradle-plugin). + +Note : It is highly recommended that always ensure gradle and agp versions are compatible with your JDK version not the vice-versa and having atleast JDK 17 is recommmended. ## Web From a135adec5f21ff798b2f36b442798845b5f2bf5e Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 22 Feb 2025 08:50:06 +0530 Subject: [PATCH 06/11] Update platform_specific_instructions.md --- doc/dev_guide/platform_specific_instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/dev_guide/platform_specific_instructions.md b/doc/dev_guide/platform_specific_instructions.md index df243103..6666ceaf 100644 --- a/doc/dev_guide/platform_specific_instructions.md +++ b/doc/dev_guide/platform_specific_instructions.md @@ -25,7 +25,7 @@ You can read more [here](https://docs.flutter.dev/platform-integration/macos/bui In case you are having a local build failure on macOS due to "audio_session" do check out issue https://github.com/foss42/apidash/issues/510 for solution. -## Android (Work in Progress) +## Android In case you are targeting the Android API level <21 or the project and the libraries it references exceed 65,536 methods, you encounter the following build error that indicates your app has reached the limit of the Android build architecture: From 17272dd01a95657b82b2f8d40edcdf32a4c8b925 Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 22 Feb 2025 08:51:00 +0530 Subject: [PATCH 07/11] Update platform_specific_instructions.md --- doc/dev_guide/platform_specific_instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/dev_guide/platform_specific_instructions.md b/doc/dev_guide/platform_specific_instructions.md index 6666ceaf..14b836ef 100644 --- a/doc/dev_guide/platform_specific_instructions.md +++ b/doc/dev_guide/platform_specific_instructions.md @@ -42,7 +42,7 @@ Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536 ``` -To solve this problem, add the `multiDexEnabled true` line to the `defaultConfig` section at `android/app/build.gradle file` +To solve this problem, add the `multiDexEnabled true` line to the `defaultConfig` section in `android/app/build.gradle file` ``` android { From 584b8bf1722186692f355638076a0e4e5b590f44 Mon Sep 17 00:00:00 2001 From: siddu015 <116783967+siddu015@users.noreply.github.com> Date: Sat, 22 Feb 2025 20:23:54 +0530 Subject: [PATCH 08/11] Fix: Removed Explain Api button from response component --- .../details_card/response_pane.dart | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/lib/screens/home_page/editor_pane/details_card/response_pane.dart b/lib/screens/home_page/editor_pane/details_card/response_pane.dart index 82efcfc7..367bb412 100644 --- a/lib/screens/home_page/editor_pane/details_card/response_pane.dart +++ b/lib/screens/home_page/editor_pane/details_card/response_pane.dart @@ -55,9 +55,6 @@ class ResponseDetails extends ConsumerWidget { final responseModel = ref.watch(selectedRequestModelProvider .select((value) => value?.httpResponseModel)); - final requestModel = ref.watch(selectedRequestModelProvider); - final ollamaService = ref.watch(ollamaServiceProvider); - return Column( children: [ ResponsePaneHeader( @@ -71,42 +68,6 @@ class ResponseDetails extends ConsumerWidget { const Expanded( child: ResponseTabs(), ), - if (requestModel != null && responseModel != null) - Padding( - padding: const EdgeInsets.all(8.0), - child: ElevatedButton( - onPressed: () async { - try { - final explanation = await ollamaService.explainLatestApi( - requestModel: requestModel, - responseModel: responseModel, - ); - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Explanation'), - content: SingleChildScrollView( - child: Text(explanation), - ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('Close'), - ), - ], - ), - ); - } catch (error) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text("Error explaining response."), - ), - ); - } - }, - child: const Text('Explain API'), - ), - ), ], ); } From 9aa209512c3f63579c60bff027e4b97cbb50defb Mon Sep 17 00:00:00 2001 From: DhumalePrasad04 Date: Sun, 23 Feb 2025 01:15:52 +0530 Subject: [PATCH 09/11] Feat: Test Case and GenerateExample added --- lib/services/ollama_service.dart | 232 +++++++++++++++++++++++---- lib/widgets/chatbot_widget.dart | 265 ++++++++++++++++++------------- 2 files changed, 354 insertions(+), 143 deletions(-) diff --git a/lib/services/ollama_service.dart b/lib/services/ollama_service.dart index f6ae682f..69967ee3 100644 --- a/lib/services/ollama_service.dart +++ b/lib/services/ollama_service.dart @@ -2,41 +2,211 @@ import 'dart:convert'; import 'package:ollama_dart/ollama_dart.dart'; 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 - Future generateResponse(String prompt) async { - final response = await _client.generateCompletion( - request: GenerateCompletionRequest( - model: 'llama3.2:3b', - prompt: prompt - ), +// 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"; +} + +// 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; + + + + 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 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 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( + requestModel: requestModel, + responseModel: responseModel, ); - return response.response.toString(); - } - - // Explain latest API request & response - Future 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 = ''' - Explain the API request and response in a simple way: - - **Request Details:** - $requestJson - - **Response Details:** - $responseJson - - Please provide a brief and clear explanation with key insights. - '''; +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"} + +**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 + +**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. +'''; + 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; + + 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"} + +**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; + } 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 56246980..ae41fc27 100644 --- a/lib/widgets/chatbot_widget.dart +++ b/lib/widgets/chatbot_widget.dart @@ -3,140 +3,181 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; class ChatbotWidget extends ConsumerStatefulWidget { - const ChatbotWidget({Key? key}) : super(key: key); +const ChatbotWidget({Key? key}) : super(key: key); - @override - _ChatbotWidgetState createState() => _ChatbotWidgetState(); +@override +_ChatbotWidgetState createState() => _ChatbotWidgetState(); } class _ChatbotWidgetState extends ConsumerState { - final TextEditingController _controller = TextEditingController(); - final List> _messages = []; - bool _isLoading = false; +final TextEditingController _controller = TextEditingController(); +final List> _messages = []; +bool _isLoading = false; - void _sendMessage(String message) async { - if (message.trim().isEmpty) return; - final ollamaService = ref.read(ollamaServiceProvider); - final requestModel = ref.read(selectedRequestModelProvider); - final responseModel = requestModel?.httpResponseModel; +void _sendMessage(String message) async { +if (message.trim().isEmpty) return; +final ollamaService = ref.read(ollamaServiceProvider); +final requestModel = ref.read(selectedRequestModelProvider); +final responseModel = requestModel?.httpResponseModel; - setState(() { - _messages.add({'role': 'user', 'message': message}); - _controller.clear(); - _isLoading = true; - }); - try { - String response; - if (message == "Explain API") { - response = await ollamaService.explainLatestApi( - requestModel: requestModel, - responseModel: responseModel, - ); - } else { - response = await ollamaService.generateResponse(message); +setState(() { + _messages.add({'role': 'user', 'message': message}); + _controller.clear(); + _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,); } - - setState(() { - _messages.add({'role': 'bot', 'message': response}); - }); - } catch (error) { - setState(() { - _messages.add({'role': 'bot', 'message': "Error: ${error.toString()}"}); - }); - } finally { - setState(() => _isLoading = false); - } + else { + response = await ollamaService.generateResponse(message); } - @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( + 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: [ - Row( - children: [ - ElevatedButton.icon( - onPressed: () => _sendMessage("Explain API"), - icon: const Icon(Icons.info_outline), - label: const Text("Explain API"), + 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 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 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, ), ), - IconButton( - icon: const Icon(Icons.send), - onPressed: () => _sendMessage(_controller.text), + + 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 { - final String message; - final bool isUser; +final String message; +final bool isUser; - const ChatBubble({super.key, required this.message, this.isUser = false}); +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 From a04d1e0ea0c031a1f5feca291d8afc76330f84ec Mon Sep 17 00:00:00 2001 From: DhumalePrasad04 Date: Sun, 23 Feb 2025 01:47:50 +0530 Subject: [PATCH 10/11] Generate Test cases --- lib/services/ollama_service.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/services/ollama_service.dart b/lib/services/ollama_service.dart index 69967ee3..f10508b3 100644 --- a/lib/services/ollama_service.dart +++ b/lib/services/ollama_service.dart @@ -73,9 +73,6 @@ Analysis: [structured analysis]'''; return generateResponse(prompt); - - - } Future debugApi({required dynamic requestModel, required dynamic responseModel}) async { From 72ab1740ed25e6c2ba9d39af77f2c8753da61a2b Mon Sep 17 00:00:00 2001 From: DhumalePrasad04 Date: Sun, 23 Feb 2025 10:40:46 +0530 Subject: [PATCH 11/11] generateTestCases prompting improved for structured responses --- lib/services/ollama_service.dart | 39 +++++++++++--------------------- lib/widgets/chatbot_widget.dart | 14 +++++------- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/lib/services/ollama_service.dart b/lib/services/ollama_service.dart index 9feae827..0e675763 100644 --- a/lib/services/ollama_service.dart +++ b/lib/services/ollama_service.dart @@ -10,7 +10,7 @@ class OllamaService { Future generateResponse(String prompt) async { final response = await _client.generateCompletion( request: GenerateCompletionRequest( - model: 'llama3.2:3b', + model: 'llama3.2:1b', prompt: prompt ), ); @@ -112,35 +112,25 @@ Analysis: [structured analysis]'''; 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); @@ -177,15 +167,12 @@ Analyze the following API request and generate structured example parameters. - **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 diff --git a/lib/widgets/chatbot_widget.dart b/lib/widgets/chatbot_widget.dart index 687f7e5e..bc685ccc 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); @@ -90,20 +92,13 @@ class _ChatbotWidgetState extends ConsumerState { onPressed: () => _sendMessage("Debug API"), icon: const Icon(Icons.bug_report), label: const Text("Debug"), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.redAccent, - ), ), ], - const Spacer(), 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(), @@ -171,7 +166,10 @@ class ChatBubble extends StatelessWidget { : Theme.of(context).colorScheme.secondaryContainer, borderRadius: BorderRadius.circular(8), ), - child: Text(message), + child: MarkdownBody( + data: message, + selectable: true, // Allows copying text + ), ), ); }