diff --git a/lib/dashbot/features/documentation.dart b/lib/dashbot/features/documentation.dart index 9f6464de..f509f054 100644 --- a/lib/dashbot/features/documentation.dart +++ b/lib/dashbot/features/documentation.dart @@ -16,10 +16,10 @@ class DocumentationFeature { } final method = requestModel.httpRequestModel?.method - .toString() - .split('.') - .last - .toUpperCase() ?? + .toString() + .split('.') + .last + .toUpperCase() ?? "GET"; final endpoint = requestModel.httpRequestModel?.url ?? "Unknown Endpoint"; final headers = requestModel.httpRequestModel?.enabledHeadersMap ?? {}; @@ -27,7 +27,7 @@ class DocumentationFeature { final body = requestModel.httpRequestModel?.body; final rawResponse = responseModel.body; final responseBody = - rawResponse is String ? rawResponse : jsonEncode(rawResponse); + rawResponse is String ? rawResponse : jsonEncode(rawResponse); final statusCode = responseModel.statusCode ?? 0; final prompt = """ diff --git a/lib/dashbot/features/features.dart b/lib/dashbot/features/features.dart new file mode 100644 index 00000000..829bd2ab --- /dev/null +++ b/lib/dashbot/features/features.dart @@ -0,0 +1,5 @@ +export 'debug.dart'; +export 'documentation.dart'; +export 'explain.dart'; +export 'general_query.dart'; +export 'test_generator.dart'; diff --git a/lib/dashbot/features/general_query.dart b/lib/dashbot/features/general_query.dart index f5ce53ee..28245704 100644 --- a/lib/dashbot/features/general_query.dart +++ b/lib/dashbot/features/general_query.dart @@ -6,11 +6,17 @@ class GeneralQueryFeature { GeneralQueryFeature(this._client); - Future generateResponse(String prompt, {RequestModel? requestModel, dynamic responseModel}) async { + Future generateResponse(String prompt, + {RequestModel? requestModel, dynamic responseModel}) async { String enhancedPrompt = prompt; if (requestModel != null && responseModel != null) { - final method = requestModel.httpRequestModel?.method.toString().split('.').last.toUpperCase() ?? "GET"; + final method = requestModel.httpRequestModel?.method + .toString() + .split('.') + .last + .toUpperCase() ?? + "GET"; final endpoint = requestModel.httpRequestModel?.url ?? "Unknown Endpoint"; final statusCode = responseModel.statusCode ?? 0; @@ -37,7 +43,10 @@ Respond in a helpful, direct manner that specifically answers what was asked. } final response = await _client.generateCompletion( - request: GenerateCompletionRequest(model: 'llama3.2:3b', prompt: enhancedPrompt), + request: GenerateCompletionRequest( + model: 'llama3.2:3b', + prompt: enhancedPrompt, + ), ); return response.response.toString(); } diff --git a/lib/dashbot/features/test_generator.dart b/lib/dashbot/features/test_generator.dart index 85442632..ffeeb89a 100644 --- a/lib/dashbot/features/test_generator.dart +++ b/lib/dashbot/features/test_generator.dart @@ -16,15 +16,15 @@ class TestGeneratorFeature { } final method = requestModel.httpRequestModel?.method - .toString() - .split('.') - .last - .toUpperCase() ?? + .toString() + .split('.') + .last + .toUpperCase() ?? "GET"; final endpoint = requestModel.httpRequestModel?.url ?? "Unknown Endpoint"; final rawResponse = responseModel.body; final responseBody = - rawResponse is String ? rawResponse : jsonEncode(rawResponse); + rawResponse is String ? rawResponse : jsonEncode(rawResponse); final statusCode = responseModel.statusCode ?? 0; Uri uri = Uri.parse(endpoint); @@ -75,9 +75,11 @@ Focus on creating realistic test values based on the API context (e.g., for a co parameters.forEach((key, value) { if (RegExp(r'^[A-Z]{3}$').hasMatch(value)) { - analysis[key] = "Appears to be a 3-letter country code (ISO 3166-1 alpha-3)"; + analysis[key] = + "Appears to be a 3-letter country code (ISO 3166-1 alpha-3)"; } else if (RegExp(r'^[A-Z]{2}$').hasMatch(value)) { - analysis[key] = "Appears to be a 2-letter country code (ISO 3166-1 alpha-2)"; + analysis[key] = + "Appears to be a 2-letter country code (ISO 3166-1 alpha-2)"; } else if (RegExp(r'^\d+$').hasMatch(value)) { analysis[key] = "Numeric value"; } else if (RegExp(r'^[a-zA-Z]+$').hasMatch(value)) { diff --git a/lib/dashbot/services/dashbot_service.dart b/lib/dashbot/services/dashbot_service.dart index 4d39f826..72d38f03 100644 --- a/lib/dashbot/services/dashbot_service.dart +++ b/lib/dashbot/services/dashbot_service.dart @@ -1,10 +1,6 @@ -import 'package:apidash/dashbot/features/debug.dart'; -import 'package:apidash/dashbot/features/documentation.dart'; import 'package:ollama_dart/ollama_dart.dart'; -import 'package:apidash/dashbot/features/explain.dart'; -import 'package:apidash/dashbot/features/test_generator.dart'; import 'package:apidash/models/request_model.dart'; -import 'package:apidash/dashbot/features/general_query.dart'; +import '../features/features.dart'; class DashBotService { final OllamaClient _client; @@ -16,7 +12,8 @@ class DashBotService { DashBotService() : _client = OllamaClient(baseUrl: 'http://127.0.0.1:11434/api'), - _generalQueryFeature = GeneralQueryFeature(OllamaClient(baseUrl: 'http://127.0.0.1:11434/api')) { + _generalQueryFeature = GeneralQueryFeature( + OllamaClient(baseUrl: 'http://127.0.0.1:11434/api')) { _explainFeature = ExplainFeature(this); _debugFeature = DebugFeature(this); _documentationFeature = DocumentationFeature(this); @@ -28,21 +25,36 @@ class DashBotService { } Future handleRequest( - String input, RequestModel? requestModel, dynamic responseModel) async { + String input, + RequestModel? requestModel, + dynamic responseModel, + ) async { if (input == "Explain API") { return _explainFeature.explainLatestApi( - requestModel: requestModel, responseModel: responseModel); + requestModel: requestModel, + responseModel: responseModel, + ); } else if (input == "Debug API") { return _debugFeature.debugApi( - requestModel: requestModel, responseModel: responseModel); + requestModel: requestModel, + responseModel: responseModel, + ); } else if (input == "Document API") { return _documentationFeature.generateApiDocumentation( - requestModel: requestModel, responseModel: responseModel); + requestModel: requestModel, + responseModel: responseModel, + ); } else if (input == "Test API") { return _testGeneratorFeature.generateApiTests( - requestModel: requestModel, responseModel: responseModel); + requestModel: requestModel, + responseModel: responseModel, + ); } - return _generalQueryFeature.generateResponse(input, requestModel: requestModel, responseModel: responseModel); + return _generalQueryFeature.generateResponse( + input, + requestModel: requestModel, + responseModel: responseModel, + ); } } diff --git a/lib/dashbot/widgets/chat_bubble.dart b/lib/dashbot/widgets/chat_bubble.dart index a99a76d2..f00c6d5d 100644 --- a/lib/dashbot/widgets/chat_bubble.dart +++ b/lib/dashbot/widgets/chat_bubble.dart @@ -7,7 +7,11 @@ class ChatBubble extends StatelessWidget { 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) { diff --git a/lib/dashbot/widgets/content_renderer.dart b/lib/dashbot/widgets/content_renderer.dart index 1e8699a6..500ebd74 100644 --- a/lib/dashbot/widgets/content_renderer.dart +++ b/lib/dashbot/widgets/content_renderer.dart @@ -5,12 +5,18 @@ import 'package:flutter_highlighter/flutter_highlighter.dart'; import 'package:flutter_highlighter/themes/monokai-sublime.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -Widget renderContent(BuildContext context, String text) { +Widget renderContent( + BuildContext context, + String text, +) { if (text.isEmpty) { return const Text("No content to display."); } - final codeBlockPattern = RegExp(r'```(\w+)?\n([\s\S]*?)```', multiLine: true); + final codeBlockPattern = RegExp( + r'```(\w+)?\n([\s\S]*?)```', + multiLine: true, + ); final matches = codeBlockPattern.allMatches(text); if (matches.isEmpty) { @@ -22,8 +28,10 @@ Widget renderContent(BuildContext context, String text) { for (var match in matches) { if (match.start > lastEnd) { - children - .add(_renderMarkdown(context, text.substring(lastEnd, match.start))); + children.add(_renderMarkdown( + context, + text.substring(lastEnd, match.start), + )); } final language = match.group(1) ?? 'text'; @@ -43,7 +51,10 @@ Widget renderContent(BuildContext context, String text) { ); } -Widget _renderMarkdown(BuildContext context, String markdown) { +Widget _renderMarkdown( + BuildContext context, + String markdown, +) { return MarkdownBody( data: markdown, selectable: true, @@ -53,7 +64,11 @@ Widget _renderMarkdown(BuildContext context, String markdown) { ); } -Widget _renderCodeBlock(BuildContext context, String language, String code) { +Widget _renderCodeBlock( + BuildContext context, + String language, + String code, +) { if (language == 'json') { try { final prettyJson = @@ -63,7 +78,10 @@ Widget _renderCodeBlock(BuildContext context, String language, String code) { color: Theme.of(context).colorScheme.surfaceContainerLow, child: SelectableText( prettyJson, - style: const TextStyle(fontFamily: 'monospace', fontSize: 12), + style: const TextStyle( + fontFamily: 'monospace', + fontSize: 12, + ), ), ); } catch (e) { @@ -78,7 +96,10 @@ Widget _renderCodeBlock(BuildContext context, String language, String code) { code, language: language, theme: monokaiSublimeTheme, - textStyle: const TextStyle(fontFamily: 'monospace', fontSize: 12), + textStyle: const TextStyle( + fontFamily: 'monospace', + fontSize: 12, + ), ), ); } catch (e) { @@ -87,14 +108,20 @@ Widget _renderCodeBlock(BuildContext context, String language, String code) { } } -Widget _renderFallbackCode(BuildContext context, String code) { +Widget _renderFallbackCode( + BuildContext context, + String code, +) { return Container( padding: const EdgeInsets.all(8), color: Theme.of(context).colorScheme.surfaceContainerLow, child: SelectableText( code, style: const TextStyle( - fontFamily: 'monospace', fontSize: 12, color: Colors.red), + fontFamily: 'monospace', + fontSize: 12, + color: Colors.red, + ), ), ); } diff --git a/lib/dashbot/widgets/dashbot_widget.dart b/lib/dashbot/widgets/dashbot_widget.dart index 98defd77..fb90ffdb 100644 --- a/lib/dashbot/widgets/dashbot_widget.dart +++ b/lib/dashbot/widgets/dashbot_widget.dart @@ -53,7 +53,8 @@ class _DashBotWidgetState extends ConsumerState { final testCases = response.replaceFirst("TEST_CASES_HIDDEN\n", ""); ref.read(chatMessagesProvider.notifier).addMessage({ 'role': 'bot', - 'message': "Test cases generated successfully. Click the button below to run them.", + 'message': + "Test cases generated successfully. Click the button below to run them.", 'testCases': testCases, 'showTestButton': true, }); @@ -113,18 +114,18 @@ class _DashBotWidgetState extends ConsumerState { child: isMinimized ? _buildMinimizedView(context) : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildHeader(context), - const SizedBox(height: 12), - _buildQuickActions(showDebugButton), - const SizedBox(height: 12), - Expanded(child: _buildChatArea(messages)), - if (_isLoading) _buildLoadingIndicator(), - const SizedBox(height: 10), - _buildInputArea(context), - ], - ), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(context), + const SizedBox(height: 12), + _buildQuickActions(showDebugButton), + const SizedBox(height: 12), + Expanded(child: _buildChatArea(messages)), + if (_isLoading) _buildLoadingIndicator(), + const SizedBox(height: 10), + _buildInputArea(context), + ], + ), ); } @@ -150,7 +151,8 @@ class _DashBotWidgetState extends ConsumerState { ), tooltip: isMinimized ? 'Maximize' : 'Minimize', onPressed: () { - ref.read(dashBotMinimizedProvider.notifier).state = !isMinimized; + ref.read(dashBotMinimizedProvider.notifier).state = + !isMinimized; }, ), IconButton( @@ -214,7 +216,8 @@ class _DashBotWidgetState extends ConsumerState { icon: const Icon(Icons.bug_report_outlined, size: 16), label: const Text("Debug"), style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), visualDensity: VisualDensity.compact, ), ), diff --git a/lib/dashbot/widgets/test_runner_widget.dart b/lib/dashbot/widgets/test_runner_widget.dart index e93f384d..39ed1065 100644 --- a/lib/dashbot/widgets/test_runner_widget.dart +++ b/lib/dashbot/widgets/test_runner_widget.dart @@ -8,7 +8,10 @@ import 'content_renderer.dart'; class TestRunnerWidget extends ConsumerStatefulWidget { final String testCases; - const TestRunnerWidget({Key? key, required this.testCases}) : super(key: key); + const TestRunnerWidget({ + Key? key, + required this.testCases, + }) : super(key: key); @override ConsumerState createState() => _TestRunnerWidgetState(); @@ -78,7 +81,8 @@ class _TestRunnerWidgetState extends ConsumerState { String method = "GET"; if (command.contains("-X POST") || command.contains("--request POST")) { method = "POST"; - } else if (command.contains("-X PUT") || command.contains("--request PUT")) { + } else if (command.contains("-X PUT") || + command.contains("--request PUT")) { method = "PUT"; } @@ -139,9 +143,9 @@ class _TestRunnerWidgetState extends ConsumerState { title: const Text('API Test Runner'), content: const Text( 'Run generated API tests:\n\n' - '• "Run All" executes all tests\n' - '• "Run" executes a single test\n' - '• "Copy" copies the curl command', + '• "Run All" executes all tests\n' + '• "Run" executes a single test\n' + '• "Copy" copies the curl command', ), actions: [ TextButton( @@ -189,9 +193,8 @@ class _TestRunnerWidgetState extends ConsumerState { test['description'] ?? "Test case ${index + 1}", style: TextStyle( fontWeight: FontWeight.bold, - color: hasResult - ? (isSuccess ? Colors.green : Colors.red) - : null, + color: + hasResult ? (isSuccess ? Colors.green : Colors.red) : null, ), ), subtitle: Text('Test ${index + 1} of ${_parsedTests.length}'), @@ -241,7 +244,8 @@ class _TestRunnerWidgetState extends ConsumerState { padding: const EdgeInsets.all(8), margin: const EdgeInsets.only(top: 4, bottom: 16), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerLow, + color: + Theme.of(context).colorScheme.surfaceContainerLow, borderRadius: BorderRadius.circular(4), ), width: double.infinity,