From 6d004a5448e3fab523565afc488a435165b05d13 Mon Sep 17 00:00:00 2001 From: saisreesatyassss Date: Tue, 1 Apr 2025 11:52:51 +0530 Subject: [PATCH] Added features dashbot-features --- lib/dashbot/features/debug.dart | 3 +- lib/dashbot/features/explain.dart | 5 +- lib/dashbot/features/generateApiDoc.dart | 440 ++++++++++++++++++ .../features/visualizationFeature.dart | 139 ++++++ lib/dashbot/services/dashbot_service.dart | 154 +++++- lib/dashbot/widgets/dashbot_widget.dart | 71 ++- pubspec.lock | 8 + pubspec.yaml | 1 + 8 files changed, 797 insertions(+), 24 deletions(-) create mode 100644 lib/dashbot/features/generateApiDoc.dart create mode 100644 lib/dashbot/features/visualizationFeature.dart diff --git a/lib/dashbot/features/debug.dart b/lib/dashbot/features/debug.dart index c7d86ef9b..29b7002eb 100644 --- a/lib/dashbot/features/debug.dart +++ b/lib/dashbot/features/debug.dart @@ -10,6 +10,7 @@ class DebugFeature { Future debugApi({ required RequestModel? requestModel, required dynamic responseModel, + required String selectedAI, }) async { if (requestModel == null || responseModel == null) { return "No recent API requests found."; @@ -58,6 +59,6 @@ $responseBody Provide a CLEAR, ACTIONABLE solution in the SIMPLEST possible language. """; - return _service.generateResponse(prompt); + return _service.generateResponse(prompt,selectedAI); } } diff --git a/lib/dashbot/features/explain.dart b/lib/dashbot/features/explain.dart index 4b008e6cf..f0e3c00ef 100644 --- a/lib/dashbot/features/explain.dart +++ b/lib/dashbot/features/explain.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unused_local_variable + import 'dart:convert'; import '../services/dashbot_service.dart'; import 'package:apidash/models/request_model.dart'; @@ -10,6 +12,7 @@ class ExplainFeature { Future explainLatestApi({ required RequestModel? requestModel, required dynamic responseModel, + required String selectedAI, }) async { if (requestModel == null || responseModel == null) { return "No recent API requests found."; @@ -63,6 +66,6 @@ AVOID: Deliver a CRYSTAL CLEAR, CONCISE explanation that anyone can understand. '''; - return _service.generateResponse(prompt); + return _service.generateResponse(prompt, selectedAI); } } diff --git a/lib/dashbot/features/generateApiDoc.dart b/lib/dashbot/features/generateApiDoc.dart new file mode 100644 index 000000000..6a634bfc0 --- /dev/null +++ b/lib/dashbot/features/generateApiDoc.dart @@ -0,0 +1,440 @@ +// ignore_for_file: unused_import + +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart' as pw; +import 'package:fl_chart/fl_chart.dart'; +import '../services/dashbot_service.dart'; +import 'package:apidash/models/request_model.dart'; + +class VisualizationFeature { + final DashBotService _service; + + VisualizationFeature(this._service); + + Future generateApiPlots({ + required RequestModel? requestModel, + required dynamic responseModel, + required String selectedAI, + required BuildContext context, + }) async { + if (requestModel == null || responseModel == null) { + return "No recent API response available for visualization."; + } + + final prompt = """ +Extract the relevant data from the API response to plot a graph. Identify the key X and Y axis values and labels. Ensure: +- X-axis represents a logical progression (time, categories, indexes, etc.). +- Y-axis represents numerical values. +- Provide labels for both axes. +- Return the structured data in JSON format like: + +{ + "xAxis": ["Label1", "Label2", "Label3"], + "yAxis": [10, 20, 30], + "xLabel": "Time", + "yLabel": "Value" +} + +Use the response data below to determine the best fields for visualization: +$responseModel +"""; + + try { + final aiResponse = await _service.generateResponse(prompt, selectedAI); + + // Debug: Log the raw response + // print("Raw AI response: $aiResponse"); + + // Extract JSON from the AI response + // This handles cases where the AI might return explanatory text along with JSON + final jsonRegExp = RegExp(r'({[\s\S]*})'); + final match = jsonRegExp.firstMatch(aiResponse); + + if (match == null) { + return "⚠️ Could not find JSON in AI response."; + } + + final jsonString = match.group(1)?.trim(); + if (jsonString == null || jsonString.isEmpty) { + return "⚠️ Extracted JSON is empty."; + } + + // Clean the JSON string - sometimes AI adds backticks or code formatting + final cleanJsonString = + jsonString.replaceAll("```json", "").replaceAll("```", "").trim(); + + // print("Cleaned JSON string: $cleanJsonString"); + + final extractedData = jsonDecode(cleanJsonString); + + if (extractedData is! Map || + !extractedData.containsKey('xAxis') || + !extractedData.containsKey('yAxis') || + !extractedData.containsKey('xLabel') || + !extractedData.containsKey('yLabel')) { + return "⚠️ AI response is missing required fields (xAxis, yAxis, xLabel, yLabel)."; + } + + final List xAxisRaw = extractedData['xAxis']; + final List yAxisRaw = extractedData['yAxis']; + + // Convert all elements to string for xAxis and num for yAxis + final List xAxis = xAxisRaw.map((e) => e.toString()).toList(); + + // Handle null values in yAxis by converting them to 0 + final List yAxis = yAxisRaw.map((e) { + if (e == null) return 0; + return num.tryParse(e.toString()) ?? 0; + }).toList(); + + final String xLabel = extractedData['xLabel'].toString(); + final String yLabel = extractedData['yLabel'].toString(); + + if (xAxis.isEmpty || yAxis.isEmpty) { + return "⚠️ Extracted data contains empty axes."; + } + + if (xAxis.length != yAxis.length) { + // print( + // "⚠️ Warning: X and Y axes have different lengths. X: ${xAxis.length}, Y: ${yAxis.length}"); + } + + // Navigate to visualization page instead of showing dialog + if (context.mounted) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => VisualizationPage( + xAxis: xAxis, + yAxis: yAxis, + xLabel: xLabel, + yLabel: yLabel, + ), + ), + ); + return "✅ Visualization generated successfully!"; + } else { + return "⚠️ Context is not mounted, cannot show visualization."; + } + // ignore: unused_catch_stack + } catch (e, stackTrace) { + // More detailed error message with stack trace + // print("Error details: $e"); + // print("Stack trace: $stackTrace"); + return "⚠️ Failed to parse AI response for visualization: ${e.toString()}"; + } + } + + int min(int a, int b) => a < b ? a : b; + int max(int a, int b) => a > b ? a : b; +} + +// New dedicated page for visualization +class VisualizationPage extends StatelessWidget { + final List xAxis; + final List yAxis; + final String xLabel; + final String yLabel; + + const VisualizationPage({ + Key? key, + required this.xAxis, + required this.yAxis, + required this.xLabel, + required this.yLabel, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + // Find min and max values for better scaling + final nonZeroValues = yAxis.where((value) => value != 0).toList(); + final minY = nonZeroValues.isEmpty + ? 0.0 + : nonZeroValues.reduce((a, b) => a < b ? a : b).toDouble(); + + final maxY = + yAxis.isEmpty ? 10.0 : yAxis.reduce((a, b) => a > b ? a : b).toDouble(); + + final padding = maxY == 0 ? 1.0 : (maxY - minY) * 0.1; // 10% padding + + return Scaffold( + appBar: AppBar( + title: const Text("API Data Visualization"), + actions: [ + IconButton( + icon: const Icon(Icons.save), + onPressed: () => _savePdf(context), + tooltip: "Export to PDF", + ), + ], + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Text( + "X-Axis: $xLabel, Y-Axis: $yLabel", + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + textAlign: TextAlign.center, + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: LineChart( + LineChartData( + gridData: FlGridData( + show: true, + drawVerticalLine: true, + horizontalInterval: + (maxY - minY) / 5 == 0 ? 1.0 : (maxY - minY) / 5, + verticalInterval: 1, + ), + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 40, + getTitlesWidget: (value, meta) { + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text( + value.toStringAsFixed(1), + style: const TextStyle(fontSize: 10), + ), + ); + }, + interval: (maxY - minY) / 5 == 0 + ? 1.0 + : (maxY - minY) / 5, + ), + axisNameWidget: Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + yLabel, + style: const TextStyle(fontSize: 12), + ), + ), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + int index = value.toInt(); + if (index >= 0 && + index < xAxis.length && + index % max(1, (xAxis.length ~/ 5)) == 0) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + xAxis[index], + style: const TextStyle(fontSize: 10), + ), + ); + } + return const SizedBox.shrink(); + }, + interval: 1, + ), + axisNameWidget: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + xLabel, + style: const TextStyle(fontSize: 12), + ), + ), + ), + rightTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + topTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + ), + borderData: FlBorderData( + show: true, + border: Border.all(color: Colors.black12), + ), + minX: 0, + maxX: (xAxis.length - 1).toDouble(), + minY: minY - padding, + maxY: maxY + padding, + lineBarsData: [ + LineChartBarData( + spots: List.generate( + min(xAxis.length, yAxis.length), + (index) => + FlSpot(index.toDouble(), yAxis[index].toDouble()), + ), + isCurved: true, + barWidth: 3, + color: Colors.blue, + dotData: FlDotData( + show: true, + getDotPainter: (spot, percent, barData, index) => + FlDotCirclePainter( + radius: 4, + color: Colors.blue, + strokeColor: Colors.white, + strokeWidth: 2, + ), + ), + belowBarData: BarAreaData( + show: true, + // ignore: deprecated_member_use + color: Colors.blue.withOpacity(0.2), + ), + ), + ], + lineTouchData: LineTouchData( + touchTooltipData: LineTouchTooltipData( + getTooltipItems: (List touchedBarSpots) { + return touchedBarSpots.map((barSpot) { + final int index = barSpot.x.toInt(); + return LineTooltipItem( + '${xAxis[index]}: ${barSpot.y.toStringAsFixed(2)}', + const TextStyle(color: Colors.white), + ); + }).toList(); + }, + ), + ), + ), + ), + ), + ), + const SizedBox(height: 16), + Expanded( + child: Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: ListView.builder( + itemCount: min(xAxis.length, yAxis.length), + itemBuilder: (context, index) { + return ListTile( + title: Text(xAxis[index]), + trailing: Text( + yAxis[index] == 0 + ? "N/A" + : yAxis[index].toStringAsFixed(2), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ); + }, + ), + ), + ), + ), + ], + ), + ), + ); + } + + int min(int a, int b) => a < b ? a : b; + int max(int a, int b) => a > b ? a : b; + + Future _savePdf(BuildContext context) async { + final pdf = pw.Document(); + + pdf.addPage( + pw.Page( + build: (pw.Context pdfContext) { + return pw.Column( + crossAxisAlignment: pw.CrossAxisAlignment.center, + children: [ + pw.Text('API Data Visualization', + style: pw.TextStyle( + fontSize: 20, fontWeight: pw.FontWeight.bold)), + pw.SizedBox(height: 20), + pw.Text('X-Axis: $xLabel, Y-Axis: $yLabel'), + pw.SizedBox(height: 10), + pw.Table( + border: pw.TableBorder.all(), + children: [ + pw.TableRow( + children: [ + pw.Padding( + padding: const pw.EdgeInsets.all(5), + child: pw.Text('X Value', + style: + pw.TextStyle(fontWeight: pw.FontWeight.bold)), + ), + pw.Padding( + padding: const pw.EdgeInsets.all(5), + child: pw.Text('Y Value', + style: + pw.TextStyle(fontWeight: pw.FontWeight.bold)), + ), + ], + ), + ...List.generate( + min(xAxis.length, yAxis.length), + (index) => pw.TableRow( + children: [ + pw.Padding( + padding: const pw.EdgeInsets.all(5), + child: pw.Text(xAxis[index]), + ), + pw.Padding( + padding: const pw.EdgeInsets.all(5), + child: pw.Text(yAxis[index] == 0 + ? "N/A" + : yAxis[index].toString()), + ), + ], + ), + ), + ], + ), + pw.SizedBox(height: 15), + pw.Text( + 'Generated on ${DateTime.now().toString().split('.')[0]}'), + ], + ); + }, + ), + ); + + try { + final dir = await getApplicationDocumentsDirectory(); + final file = File( + '${dir.path}/api_visualization_${DateTime.now().millisecondsSinceEpoch}.pdf'); + await file.writeAsBytes(await pdf.save()); + + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('PDF saved to: ${file.path}'), + duration: const Duration(seconds: 5), + action: SnackBarAction( + label: 'OK', + onPressed: () {}, + ), + ), + ); + } + + // print("PDF saved to: ${file.path}"); + } catch (e) { + // print("Failed to save PDF: $e"); + + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to save PDF: $e'), + backgroundColor: Colors.red, + ), + ); + } + } + } +} \ No newline at end of file diff --git a/lib/dashbot/features/visualizationFeature.dart b/lib/dashbot/features/visualizationFeature.dart new file mode 100644 index 000000000..8e50a8f69 --- /dev/null +++ b/lib/dashbot/features/visualizationFeature.dart @@ -0,0 +1,139 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:path_provider/path_provider.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart' as pw; +import '../services/dashbot_service.dart'; +import 'package:apidash/models/request_model.dart'; + +class DocumentationFeature { + final DashBotService _service; + + DocumentationFeature(this._service); + + Future generateApiDoc({ + required RequestModel? requestModel, + required dynamic responseModel, + required String selectedAI, + }) async { + if (requestModel == null) { + return "No recent API requests found for documentation."; + } + + 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 prompt = """ +# API Documentation + +## Overview +This document provides a detailed specification of the API request, including its endpoint, method, parameters, headers, request body, and expected response format. + +## API Endpoint +- **URL:** `$endpoint` +- **Method:** `$method` + +## Request Details +- **Headers:** ${headers.isNotEmpty ? jsonEncode(headers) : "No headers"} +- **Parameters:** ${parameters.isNotEmpty ? jsonEncode(parameters) : "No parameters"} +- **Request Body:** ${body ?? "Empty body"} + +## Expected Response +Provide a structured and detailed documentation explaining: +1. The expected response format +2. The meaning of each field in the response +3. Error codes and their meanings (if applicable) +4. Examples of successful and failed responses +5. Additional usage guidelines + +Ensure the documentation is **well-formatted** and **easy to understand**. +"""; + + // Get AI-generated response + final aiResponse = await _service.generateResponse(prompt, selectedAI); + + // Save AI response to a PDF + // final pdfPath = await saveToPDF(aiResponse); + + return aiResponse; + } + + Future saveToPDF(String documentation) async { + final pdf = pw.Document(); + + pdf.addPage( + pw.MultiPage( + pageFormat: PdfPageFormat.a4, + margin: const pw.EdgeInsets.all(20), + build: (pw.Context context) => _formatMarkdown(documentation), + ), + ); + + final directory = await getApplicationDocumentsDirectory(); + final filePath = "${directory.path}/api_documentation.pdf"; + final file = File(filePath); + await file.writeAsBytes(await pdf.save()); + + // print("✅ PDF saved at: $filePath"); + return filePath; // Return file path + } + + List _formatMarkdown(String text) { + List widgets = []; + List lines = text.split("\n"); + + for (String line in lines) { + if (line.startsWith("# ")) { + widgets.add(pw.Text(line.replaceFirst("# ", ""), + style: pw.TextStyle(fontSize: 22, fontWeight: pw.FontWeight.bold))); + } else if (line.startsWith("## ")) { + widgets.add(pw.Text(line.replaceFirst("## ", ""), + style: pw.TextStyle( + fontSize: 18, + fontWeight: pw.FontWeight.bold, + color: PdfColors.blue))); + } else if (line.startsWith("- **")) { + widgets.add(pw.Bullet( + text: line.replaceFirst("- **", ""), + style: pw.TextStyle(fontSize: 12, fontWeight: pw.FontWeight.bold))); + } else if (line.startsWith("* ")) { + widgets.add(pw.Bullet( + text: line.replaceFirst("* ", ""), + style: pw.TextStyle(fontSize: 12))); + } else if (line.startsWith("```json")) { + widgets.add(_jsonBlock()); // JSON Block Styling + } else if (line.startsWith("```")) { + continue; // Ignore closing code block indicators + } else if (line.trim().startsWith("{")) { + widgets.add(_jsonBlock(line)); // JSON Block Styling + } else { + widgets.add(pw.Text(line, style: pw.TextStyle(fontSize: 12))); + } + widgets.add(pw.SizedBox(height: 5)); // Add spacing + } + + return widgets; + } + + pw.Container _jsonBlock([String jsonText = ""]) { + return pw.Container( + padding: const pw.EdgeInsets.all(8), + decoration: pw.BoxDecoration( + color: PdfColors.grey300, + borderRadius: pw.BorderRadius.circular(5), + ), + child: pw.Text(jsonText, + style: pw.TextStyle( + fontSize: 10, + )), + ); + } +} diff --git a/lib/dashbot/services/dashbot_service.dart b/lib/dashbot/services/dashbot_service.dart index 8eb0087c8..4bd6797b4 100644 --- a/lib/dashbot/services/dashbot_service.dart +++ b/lib/dashbot/services/dashbot_service.dart @@ -1,36 +1,152 @@ + + +// // i removed ollama_dart and use google gemini api so i can test the dashbot + +// import 'dart:convert'; +// import 'package:http/http.dart' as http; +// import 'package:apidash/dashbot/features/debug.dart'; +// import 'package:apidash/dashbot/features/explain.dart'; +// import 'package:apidash/models/request_model.dart'; + +// class DashBotService { +// final String _apiKey = "Here add you api "; +// final String _baseUrl = +// "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"; + +// late final ExplainFeature _explainFeature; +// late final DebugFeature _debugFeature; + +// DashBotService() { +// _explainFeature = ExplainFeature(this); +// _debugFeature = DebugFeature(this); +// } + +// Future generateResponse(String prompt) async { +// final url = Uri.parse("$_baseUrl?key=$_apiKey"); +// final response = await http.post( +// url, +// headers: {'Content-Type': 'application/json'}, +// body: jsonEncode({ +// "contents": [ +// { +// "parts": [ +// {"text": prompt} +// ] +// } +// ] +// }), +// ); + +// if (response.statusCode == 200) { +// final responseData = jsonDecode(response.body); +// return responseData["candidates"]?[0]["content"]?["parts"]?[0]["text"] ?? +// "No response received."; +// } else { +// return "Error: ${response.statusCode} - ${response.body}"; +// } +// } + +// Future handleRequest( +// String input, RequestModel? requestModel, dynamic responseModel) async { +// if (input == "Explain API") { +// return _explainFeature.explainLatestApi( +// requestModel: requestModel, responseModel: responseModel); +// } else if (input == "Debug API") { +// return _debugFeature.debugApi( +// requestModel: requestModel, responseModel: responseModel); +// } + +// return generateResponse(input); +// } +// } + +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; import 'package:apidash/dashbot/features/debug.dart'; -import 'package:ollama_dart/ollama_dart.dart'; -import '../features/explain.dart'; +import 'package:apidash/dashbot/features/explain.dart'; +import 'package:apidash/dashbot/features/generateApiDoc.dart'; // Added missing import +import 'package:apidash/dashbot/features/visualizationFeature.dart'; // Added missing import import 'package:apidash/models/request_model.dart'; class DashBotService { - final OllamaClient _client; + final Map _apiEndpoints = { + "LLaMA": "https://ai-endpoint-seven.vercel.app/llama", + "Snowflake": "https://ai-endpoint-seven.vercel.app/snowflake", + "Google": "https://ai-endpoint-seven.vercel.app/google", + }; +// these apis will just call as llm these i have create in diffrent repo :-https://github.com/saisreesatyassss/ai_endpoint/tree/main + late final ExplainFeature _explainFeature; late final DebugFeature _debugFeature; + late final DocumentationFeature + _documentationFeature; + late final VisualizationFeature + _visualizationFeature; - DashBotService() - : _client = OllamaClient(baseUrl: 'http://127.0.0.1:11434/api') { + DashBotService() { _explainFeature = ExplainFeature(this); _debugFeature = DebugFeature(this); + _documentationFeature = DocumentationFeature(this); + _visualizationFeature = VisualizationFeature(this); } - Future generateResponse(String prompt) async { - final response = await _client.generateCompletion( - request: GenerateCompletionRequest(model: 'llama3.2:3b', prompt: prompt), + Future generateResponse(String prompt, String selectedAI) async { + // print("Selected AI before lookup: '$selectedAI'"); + + final String? apiUrl = _apiEndpoints[selectedAI.trim()]; + + if (apiUrl == null) { + // print("Invalid AI model selected, defaulting to Google"); + return "Error: Invalid AI Model Selected"; + } + + final Uri url = Uri.parse(apiUrl); + // print("API URL being used: $apiUrl"); + + final response = await http.post( + url, + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({"text": prompt}), ); - return response.response.toString(); - } - Future handleRequest( - String input, RequestModel? requestModel, dynamic responseModel) async { - if (input == "Explain API") { - return _explainFeature.explainLatestApi( - requestModel: requestModel, responseModel: responseModel); - } else if (input == "Debug API") { - return _debugFeature.debugApi( - requestModel: requestModel, responseModel: responseModel); + if (response.statusCode == 200) { + final responseData = jsonDecode(response.body); + return responseData["response"] ?? "No response received."; + } else { + // print("Error from API: ${response.statusCode} - ${response.body}"); + return "Error: ${response.statusCode} - ${response.body}"; } + } +Future handleRequest(String input, RequestModel? requestModel, + dynamic responseModel, String selectedAI, BuildContext context, // Add context as a parameter +) async { + // print("Handling request with AI model: $selectedAI"); - return generateResponse(input); + if (input == "Explain API") { + return _explainFeature.explainLatestApi( + requestModel: requestModel, + responseModel: responseModel, + selectedAI: selectedAI); + } else if (input == "Debug API") { + return _debugFeature.debugApi( + requestModel: requestModel, + responseModel: responseModel, + selectedAI: selectedAI); + } else if (input == "Generate API Documentation") { + return _documentationFeature.generateApiDoc( + requestModel: requestModel, + responseModel: responseModel, + selectedAI: selectedAI); + } else if (input == "Generate API Plots") { + return _visualizationFeature.generateApiPlots( + requestModel: requestModel, + responseModel: responseModel, + selectedAI: selectedAI, context: context +); } + + return generateResponse(input, selectedAI); +} + } diff --git a/lib/dashbot/widgets/dashbot_widget.dart b/lib/dashbot/widgets/dashbot_widget.dart index 200d4c5fa..b5a48d361 100644 --- a/lib/dashbot/widgets/dashbot_widget.dart +++ b/lib/dashbot/widgets/dashbot_widget.dart @@ -18,6 +18,8 @@ class _DashBotWidgetState extends ConsumerState { final TextEditingController _controller = TextEditingController(); late ScrollController _scrollController; bool _isLoading = false; + final List aiModels = ["Google", "LLaMA", "Snowflake"]; + String selectedAI = "Google"; @override void initState() { @@ -34,10 +36,15 @@ class _DashBotWidgetState extends ConsumerState { Future _sendMessage(String message) async { if (message.trim().isEmpty) return; + + _controller.clear(); // Clears input field after sending + final dashBotService = ref.read(dashBotServiceProvider); final requestModel = ref.read(selectedRequestModelProvider); final responseModel = requestModel?.httpResponseModel; + // print("Sending message with AI model: $selectedAI"); + setState(() => _isLoading = true); ref.read(chatMessagesProvider.notifier).addMessage({ @@ -47,14 +54,16 @@ class _DashBotWidgetState extends ConsumerState { try { final response = await dashBotService.handleRequest( - message, requestModel, responseModel); + message, requestModel, responseModel, selectedAI, context); + ref.read(chatMessagesProvider.notifier).addMessage({ 'role': 'bot', 'message': response, }); + // ignore: unused_catch_stack } catch (error, stackTrace) { - debugPrint('Error in _sendMessage: $error'); - debugPrint('StackTrace: $stackTrace'); + // debugPrint('Error in _sendMessage: $error'); + // debugPrint('StackTrace: $stackTrace'); ref.read(chatMessagesProvider.notifier).addMessage({ 'role': 'bot', 'message': "Error: ${error.toString()}", @@ -134,6 +143,62 @@ class _DashBotWidgetState extends ConsumerState { padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), ), + + + ElevatedButton.icon( + onPressed: () => _sendMessage("Generate API Documentation"), + icon: const Icon(Icons.description), + label: const Text("Generate Docs"), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + ), + ), + ElevatedButton.icon( + onPressed: () => _sendMessage("Generate API Plots"), + icon: const Icon(Icons.bar_chart), + label: const Text("Generate Plots"), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0), + child: DropdownButton( + value: selectedAI, + onChanged: (String? newValue) { + if (newValue != null) { + setState(() { + selectedAI = newValue; + }); + } + }, + items: aiModels.map((String model) { + return DropdownMenuItem( + value: model, + child: Text( + model, + style: TextStyle( + fontSize: 14, + color: Colors.black87, + ), + ), + ); + }).toList(), + icon: Icon( + Icons.arrow_drop_down, + color: Colors.blue, + size: 24, + ), + style: TextStyle( + fontSize: 14, + color: Colors.black87, + ), + isExpanded: false, + underline: Container(), + ), + ), + if (showDebugButton) ElevatedButton.icon( onPressed: () => _sendMessage("Debug API"), diff --git a/pubspec.lock b/pubspec.lock index ba47984ea..0a59ff9ad 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -502,6 +502,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237" + url: "https://pub.dev" + source: hosted + version: "0.70.2" flex_color_scheme: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d4c2ae41a..7dfb41a96 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -69,6 +69,7 @@ dependencies: url: https://github.com/google/flutter-desktop-embedding.git path: plugins/window_size carousel_slider: ^5.0.0 + fl_chart: ^0.70.2 dependency_overrides: extended_text_field: ^16.0.0