From 6382883b8c3b54366b68093a2b38c135c5724beb Mon Sep 17 00:00:00 2001 From: Manas Hejmadi Date: Sun, 4 May 2025 02:05:28 +0530 Subject: [PATCH] Some MinorRefactors & AIToolGen Dialog Implementation --- lib/apitoolgen/request_consolidator.dart | 2 +- lib/widgets/ai_toolgen_widgets.dart | 261 +++++++++++++++++++++++ lib/widgets/ai_ui_desginer_widgets.dart | 15 +- lib/widgets/response_body_success.dart | 107 +++++++++- lib/widgets/response_pane_header.dart | 114 +--------- 5 files changed, 374 insertions(+), 125 deletions(-) create mode 100644 lib/widgets/ai_toolgen_widgets.dart diff --git a/lib/apitoolgen/request_consolidator.dart b/lib/apitoolgen/request_consolidator.dart index fe8c4d5f..0f86dbe7 100644 --- a/lib/apitoolgen/request_consolidator.dart +++ b/lib/apitoolgen/request_consolidator.dart @@ -91,7 +91,7 @@ ENDPOINT: $endpoint HEADERS: ${headersStr.isEmpty ? '{}' : headersStr} $queryParamStr $bodyDetails - +$responseDetails """; } } diff --git a/lib/widgets/ai_toolgen_widgets.dart b/lib/widgets/ai_toolgen_widgets.dart new file mode 100644 index 00000000..b5320709 --- /dev/null +++ b/lib/widgets/ai_toolgen_widgets.dart @@ -0,0 +1,261 @@ +import 'package:apidash/apitoolgen/request_consolidator.dart'; +import 'package:apidash/consts.dart'; +import 'package:apidash/services/agentic_services/agent_caller.dart'; +import 'package:apidash/services/agentic_services/agents/apitool_funcgen.dart'; +import 'package:apidash/widgets/ai_ui_desginer_widgets.dart'; +import 'package:apidash/widgets/button_copy.dart'; +import 'package:apidash/widgets/previewer_code.dart'; +import 'package:apidash/widgets/widget_sending.dart'; +import 'package:apidash_design_system/tokens/tokens.dart'; +import 'package:apidash_design_system/widgets/popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:genai/agentic_engine/blueprint.dart'; + +class GenerateToolDialog extends ConsumerStatefulWidget { + final APIDashRequestDescription requestDesc; + const GenerateToolDialog({ + super.key, + required this.requestDesc, + }); + + @override + ConsumerState createState() => _GenerateToolDialogState(); +} + +class _GenerateToolDialogState extends ConsumerState { + int index = 0; + TextEditingController controller = TextEditingController(); + + generateAPITool(String lang) async { + final x = await APIDashAgentCaller.instance.call( + APIToolFunctionGenerator(), + ref: ref, + input: AgentInputs(variables: { + 'REQDATA': widget.requestDesc.generateREQDATA, + 'TARGET_LANGUAGE': lang, + }), + ); + print(x); + } + + @override + Widget build(BuildContext context) { + return Container( + height: 600, + width: MediaQuery.of(context).size.width * 0.8, + child: IndexedStack( + index: index, + children: [ + Row( + children: [ + Expanded(child: ToolRequirementSelectorPage()), + GeneratedToolCodeCopyPage( + toolCode: r"""""", + language: 'javascript', + ), + ], + ), + SizedBox( + child: Center( + child: Padding( + padding: const EdgeInsets.only(top: 40.0), + child: Container( + height: 500, + child: SendingWidget( + startSendingTime: DateTime.now(), + showTimeElapsed: false, + ), + ), + ), + ), + ), + ], + ), + ); + } +} + +class ToolRequirementSelectorPage extends StatefulWidget { + const ToolRequirementSelectorPage({super.key}); + + @override + State createState() => + _ToolRequirementSelectorPageState(); +} + +class _ToolRequirementSelectorPageState + extends State { + String targetLanguage = 'PYTHON'; + String agentFramework = 'GEMINI'; + + Map frameworkMapping = { + 'GEMINI': 'Gemini', + 'OPENAI': 'OpenAI', + 'LANGCHAIN': 'LangChain', + 'MICROSOFT_AUTOGEN': 'Microsoft AutoGen', + 'MISTRAL': 'Mistral', + 'ANTRHOPIC': 'Anthropic', + }; + + Map languageMapping = { + 'PYTHON': 'Python 3', + 'JAVASCRIPT': 'JavaScript / NodeJS' + }; + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width * 0.4, // Large dialog + padding: EdgeInsets.all(30), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Generate API Tool", + style: TextStyle( + fontSize: 24, + color: Colors.white, + ), + ), + kVSpacer5, + Padding( + padding: EdgeInsets.only(left: 3), + child: Text( + "Select an agent framework & language", + style: TextStyle(color: Colors.white60, fontSize: 15), + ), + ), + kVSpacer20, + Padding( + padding: EdgeInsets.only(left: 3), + child: Text( + "Agent Framework", + style: TextStyle(color: Colors.white60), + ), + ), + kVSpacer8, + ADPopupMenu( + value: frameworkMapping[agentFramework], + values: [ + ...frameworkMapping.keys + .map((e) => (e.toString(), frameworkMapping[e].toString())), + ], + width: MediaQuery.of(context).size.width * 0.35, + tooltip: '', + onChanged: (x) { + setState(() { + agentFramework = x ?? 'OPENAI'; + + //AutoGen is Python-Only + if (agentFramework == 'MICROSOFT_AUTOGEN') { + targetLanguage = 'JAVASCRIPT'; + } + }); + }, + isOutlined: true, + ), + kVSpacer20, + Padding( + padding: EdgeInsets.only(left: 3), + child: Text( + "Target Language", + style: TextStyle(color: Colors.white60), + ), + ), + kVSpacer8, + ADPopupMenu( + value: languageMapping[targetLanguage], + values: [ + ...languageMapping.keys + .map((e) => (e.toString(), languageMapping[e].toString())), + ], + width: MediaQuery.of(context).size.width * 0.35, + tooltip: '', + onChanged: (x) { + setState(() { + targetLanguage = x ?? 'PYTHON'; + + //AutoGen is Python-Only + if (agentFramework == 'MICROSOFT_AUTOGEN') { + targetLanguage = 'JAVASCRIPT'; + } + }); + }, + isOutlined: true, + ), + kVSpacer20, + FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () {}, + icon: Icon( + Icons.token_outlined, + ), + label: const SizedBox( + child: Text( + "Generate Tool", + ), + ), + ), + ], + ), + ); + } +} + +class GeneratedToolCodeCopyPage extends StatelessWidget { + final String toolCode; + final String language; + const GeneratedToolCodeCopyPage( + {super.key, required this.toolCode, required this.language}); + + @override + Widget build(BuildContext context) { + var codeTheme = Theme.of(context).brightness == Brightness.light + ? kLightCodeTheme + : kDarkCodeTheme; + + if (toolCode.isEmpty) { + return Padding( + padding: const EdgeInsets.only(right: 40), + child: Center( + child: Icon( + Icons.token_outlined, + color: Colors.white12, + size: 500, + ), + ), + ); + } + + return Container( + color: const Color.fromARGB(255, 28, 28, 28), + padding: EdgeInsets.all(20), + constraints: BoxConstraints(maxWidth: 700), + width: MediaQuery.of(context).size.width * 0.55, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + CopyButton( + toCopy: toolCode, + showLabel: true, + ), + Expanded( + child: SingleChildScrollView( + child: CodePreviewer( + code: toolCode, + theme: codeTheme, + language: language.toLowerCase(), + textStyle: kCodeStyle, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/ai_ui_desginer_widgets.dart b/lib/widgets/ai_ui_desginer_widgets.dart index 08ccbf76..177e4e50 100644 --- a/lib/widgets/ai_ui_desginer_widgets.dart +++ b/lib/widgets/ai_ui_desginer_widgets.dart @@ -8,10 +8,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:genai/agentic_engine/blueprint.dart'; import 'package:stac/stac.dart' as stac; - import '../services/agentic_services/agents/agents.dart'; -void showCustomDialog(BuildContext context, String content) { +void showCustomDialog(BuildContext context, Widget dialogContent) { showDialog( context: context, builder: (BuildContext context) { @@ -20,26 +19,24 @@ void showCustomDialog(BuildContext context, String content) { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0), ), - child: DialogContents( - content: content, - ), + child: dialogContent, ); }, ); } -class DialogContents extends ConsumerStatefulWidget { +class GenerateUIDialog extends ConsumerStatefulWidget { final String content; - const DialogContents({ + const GenerateUIDialog({ super.key, required this.content, }); @override - ConsumerState createState() => _DialogContentsState(); + ConsumerState createState() => _GenerateUIDialogState(); } -class _DialogContentsState extends ConsumerState { +class _GenerateUIDialogState extends ConsumerState { int index = 0; TextEditingController controller = TextEditingController(); diff --git a/lib/widgets/response_body_success.dart b/lib/widgets/response_body_success.dart index 44c9b28a..01f39143 100644 --- a/lib/widgets/response_body_success.dart +++ b/lib/widgets/response_body_success.dart @@ -1,3 +1,10 @@ +import 'dart:convert'; + +import 'package:apidash/apitoolgen/request_consolidator.dart'; +import 'package:apidash/providers/collection_providers.dart'; +import 'package:apidash/widgets/ai_toolgen_widgets.dart'; +import 'package:apidash/widgets/ai_ui_desginer_widgets.dart'; + import 'package:apidash_core/apidash_core.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:flutter/foundation.dart'; @@ -5,9 +12,11 @@ import 'package:flutter/material.dart'; import 'package:apidash/utils/utils.dart'; import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/consts.dart'; +import 'package:genai/genai.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'button_share.dart'; -class ResponseBodySuccess extends StatefulWidget { +class ResponseBodySuccess extends ConsumerStatefulWidget { const ResponseBodySuccess({ super.key, required this.mediaType, @@ -31,10 +40,11 @@ class ResponseBodySuccess extends StatefulWidget { final AIRequestModel? aiRequestModel; @override - State createState() => _ResponseBodySuccessState(); + ConsumerState createState() => + _ResponseBodySuccessState(); } -class _ResponseBodySuccessState extends State { +class _ResponseBodySuccessState extends ConsumerState { int segmentIdx = 0; @override @@ -61,6 +71,97 @@ class _ResponseBodySuccessState extends State { padding: kP10, child: Column( children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () async { + final requestModel = ref.watch( + selectedRequestModelProvider + .select((value) => value?.httpRequestModel)); + final responseModel = ref.watch( + selectedRequestModelProvider + .select((value) => value?.httpResponseModel)); + + if (requestModel == null) return; + if (responseModel == null) { + print("AA"); + return; + } + + String? bodyTXT; + Map? bodyJSON; + List? bodyFormData; + + if (requestModel.bodyContentType == + ContentType.formdata) { + bodyFormData = requestModel.formDataMapList; + } else if (requestModel.bodyContentType == + ContentType.json) { + bodyJSON = jsonDecode(requestModel.body.toString()); + } else { + bodyTXT = requestModel.body!; + } + + final reqDesModel = APIDashRequestDescription( + endpoint: requestModel.url, + method: requestModel.method.name.toUpperCase(), + responseType: responseModel.contentType.toString(), + headers: requestModel.headersMap, + response: responseModel.body, + formData: bodyFormData, + bodyTXT: bodyTXT, + bodyJSON: bodyJSON, + ); + + print("GT2"); + showCustomDialog( + context, + GenerateToolDialog( + requestDesc: reqDesModel, + ), + ); + }, + icon: Icon( + Icons.token_outlined, + ), + label: const SizedBox( + child: Text( + "Generate Tool", + ), + ), + ), + kHSpacer10, + FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + final model = ref.watch(selectedRequestModelProvider + .select((value) => value?.httpResponseModel)); + showCustomDialog( + context, + GenerateUIDialog(content: model?.formattedBody ?? ""), + ); + }, + icon: Icon( + Icons.generating_tokens, + ), + label: const SizedBox( + child: Text( + kLabelGenerateUI, + ), + ), + ), + kHSpacer10, + ], + ), + kVSpacer10, Row( children: [ (widget.options == kRawBodyViewOptions) diff --git a/lib/widgets/response_pane_header.dart b/lib/widgets/response_pane_header.dart index 13f0d135..bb318da9 100644 --- a/lib/widgets/response_pane_header.dart +++ b/lib/widgets/response_pane_header.dart @@ -1,20 +1,9 @@ -import 'dart:convert'; - -import 'package:apidash/apitoolgen/request_consolidator.dart'; -import 'package:apidash/providers/collection_providers.dart'; -import 'package:apidash/services/agentic_services/agent_caller.dart'; -import 'package:apidash/services/agentic_services/agents/apitool_funcgen.dart'; -import 'package:apidash/widgets/ai_ui_desginer_widgets.dart'; -import 'package:apidash_core/apidash_core.dart'; import 'package:flutter/material.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:apidash/utils/utils.dart'; -import 'package:apidash/consts.dart'; -import 'package:genai/agentic_engine/blueprint.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'button_clear_response.dart'; -class ResponsePaneHeader extends ConsumerWidget { +class ResponsePaneHeader extends StatelessWidget { const ResponsePaneHeader({ super.key, this.responseStatus, @@ -29,7 +18,7 @@ class ResponsePaneHeader extends ConsumerWidget { final VoidCallback? onClearResponse; @override - Widget build(BuildContext context, WidgetRef ref) { + Widget build(BuildContext context) { final bool showClearButton = onClearResponse != null; return Padding( padding: kPv8, @@ -71,105 +60,6 @@ class ResponsePaneHeader extends ConsumerWidget { : const SizedBox.shrink(), ], ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - FilledButton.tonalIcon( - style: FilledButton.styleFrom( - padding: kPh12, - minimumSize: const Size(44, 44), - ), - onPressed: () async { - final requestModel = ref.watch(selectedRequestModelProvider - .select((value) => value?.httpRequestModel)); - final responseModel = ref.watch(selectedRequestModelProvider - .select((value) => value?.httpResponseModel)); - - if (requestModel == null) return; - if (responseModel == null) { - print("AA"); - return; - } - String? bodyTXT; - Map? bodyJSON; - List? bodyFormData; - - if (requestModel.bodyContentType == ContentType.formdata) { - bodyFormData = requestModel.formDataMapList; - } else if (requestModel.bodyContentType == - ContentType.json) { - bodyJSON = jsonDecode(requestModel.body.toString()); - } else { - bodyTXT = requestModel.body!; - } - - final reqDesModel = APIDashRequestDescription( - endpoint: requestModel.url, - method: requestModel.method.name.toUpperCase(), - responseType: responseModel.contentType.toString(), - headers: requestModel.headersMap, - response: responseModel.body, - formData: bodyFormData, - bodyTXT: bodyTXT, - bodyJSON: bodyJSON, - ); - - print(reqDesModel.generateREQDATA); - return; - - final x = await APIDashAgentCaller.instance.call( - APIToolFunctionGenerator(), - ref: ref, - input: AgentInputs(variables: { - 'REQDATA': reqDesModel.generateREQDATA, - 'TARGET_LANGUAGE': 'JAVASCRIPT' - }), - ); - - print(x); - - // print(reqDesModel.generateREQDATA); - - // final model = ref.watch(selectedRequestModelProvider - // .select((value) => value?.httpResponseModel)); - // showCustomDialog(context, model?.formattedBody ?? ""); - }, - icon: Icon( - Icons.token_outlined, - ), - label: const SizedBox( - child: Text( - "Generate Tool", - ), - ), - ), - kHSpacer10, - FilledButton.tonalIcon( - style: FilledButton.styleFrom( - padding: kPh12, - minimumSize: const Size(44, 44), - ), - onPressed: () { - final model = ref.watch(selectedRequestModelProvider - .select((value) => value?.httpResponseModel)); - if (model == null) return; - final body = (model.sseOutput?.isNotEmpty ?? false) - ? model.sseOutput?.join("\n") - : model.formattedBody ?? model.body; - showCustomDialog(context, body ?? ""); - }, - icon: Icon( - Icons.generating_tokens, - ), - label: const SizedBox( - child: Text( - kLabelGenerateUI, - ), - ), - ), - kHSpacer10, - ], - ) ], ), ),