From 7b7daa7dac013a491469101f249c6e12a0ce7410 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Thu, 28 Aug 2025 23:34:28 +0530 Subject: [PATCH] Add AI request model support and improve type handling Refactored collection state management to handle API type changes and AI request models. Updated widgets and tests to support nullable HTTP methods and AI request models, and improved response body rendering for AI responses. --- lib/providers/collection_providers.dart | 79 ++++++++++++------- .../common_widgets/ai/ai_model_selector.dart | 2 +- lib/screens/home_page/collection_pane.dart | 2 +- .../home_page/editor_pane/url_card.dart | 4 + lib/widgets/card_sidebar_request.dart | 4 +- lib/widgets/response_body.dart | 4 +- lib/widgets/response_body_success.dart | 19 ++++- lib/widgets/texts.dart | 6 +- pubspec.lock | 8 ++ test/models/history_models.dart | 1 + test/models/request_models.dart | 3 +- 11 files changed, 92 insertions(+), 40 deletions(-) diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index 4ef67bf9..3d9f91f0 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -241,34 +241,54 @@ class CollectionStateNotifier } var currentModel = state![rId]!; var currentHttpRequestModel = currentModel.httpRequestModel; - final newModel = currentModel.copyWith( - apiType: apiType ?? currentModel.apiType, - name: name ?? currentModel.name, - description: description ?? currentModel.description, - requestTabIndex: requestTabIndex ?? currentModel.requestTabIndex, - httpRequestModel: currentHttpRequestModel?.copyWith( - method: method ?? currentHttpRequestModel.method, - url: url ?? currentHttpRequestModel.url, - headers: headers ?? currentHttpRequestModel.headers, - params: params ?? currentHttpRequestModel.params, - authModel: authModel ?? currentHttpRequestModel.authModel, - isHeaderEnabledList: - isHeaderEnabledList ?? currentHttpRequestModel.isHeaderEnabledList, - isParamEnabledList: - isParamEnabledList ?? currentHttpRequestModel.isParamEnabledList, - bodyContentType: - bodyContentType ?? currentHttpRequestModel.bodyContentType, - body: body ?? currentHttpRequestModel.body, - query: query ?? currentHttpRequestModel.query, - formData: formData ?? currentHttpRequestModel.formData, - ), - responseStatus: responseStatus ?? currentModel.responseStatus, - message: message ?? currentModel.message, - httpResponseModel: httpResponseModel ?? currentModel.httpResponseModel, - preRequestScript: preRequestScript ?? currentModel.preRequestScript, - postRequestScript: postRequestScript ?? currentModel.postRequestScript, - aiRequestModel: aiRequestModel ?? currentModel.aiRequestModel, - ); + + RequestModel newModel; + + if (apiType != null && currentModel.apiType != apiType) { + newModel = switch (apiType) { + APIType.rest || APIType.graphql => currentModel.copyWith( + apiType: apiType, + name: name ?? currentModel.name, + description: description ?? currentModel.description, + httpRequestModel: const HttpRequestModel(), + aiRequestModel: null), + APIType.ai => currentModel.copyWith( + apiType: apiType, + name: name ?? currentModel.name, + description: description ?? currentModel.description, + httpRequestModel: null, + aiRequestModel: const AIRequestModel()), + }; + } else { + newModel = currentModel.copyWith( + apiType: apiType ?? currentModel.apiType, + name: name ?? currentModel.name, + description: description ?? currentModel.description, + requestTabIndex: requestTabIndex ?? currentModel.requestTabIndex, + httpRequestModel: currentHttpRequestModel?.copyWith( + method: method ?? currentHttpRequestModel.method, + url: url ?? currentHttpRequestModel.url, + headers: headers ?? currentHttpRequestModel.headers, + params: params ?? currentHttpRequestModel.params, + authModel: authModel ?? currentHttpRequestModel.authModel, + isHeaderEnabledList: isHeaderEnabledList ?? + currentHttpRequestModel.isHeaderEnabledList, + isParamEnabledList: + isParamEnabledList ?? currentHttpRequestModel.isParamEnabledList, + bodyContentType: + bodyContentType ?? currentHttpRequestModel.bodyContentType, + body: body ?? currentHttpRequestModel.body, + query: query ?? currentHttpRequestModel.query, + formData: formData ?? currentHttpRequestModel.formData, + ), + responseStatus: responseStatus ?? currentModel.responseStatus, + message: message ?? currentModel.message, + httpResponseModel: httpResponseModel ?? currentModel.httpResponseModel, + preRequestScript: preRequestScript ?? currentModel.preRequestScript, + postRequestScript: postRequestScript ?? currentModel.postRequestScript, + aiRequestModel: aiRequestModel ?? currentModel.aiRequestModel, + ); + } var map = {...state!}; map[rId] = newModel; @@ -285,7 +305,8 @@ class CollectionStateNotifier } RequestModel? requestModel = state![requestId]; - if (requestModel?.httpRequestModel == null) { + if (requestModel?.httpRequestModel == null && + requestModel?.aiRequestModel == null) { return; } diff --git a/lib/screens/common_widgets/ai/ai_model_selector.dart b/lib/screens/common_widgets/ai/ai_model_selector.dart index 2ba45581..d746889a 100644 --- a/lib/screens/common_widgets/ai/ai_model_selector.dart +++ b/lib/screens/common_widgets/ai/ai_model_selector.dart @@ -15,7 +15,7 @@ class AIModelSelector extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { AIRequestModel? aiRequestModel; - if (readOnlyModel != null) { + if (readOnlyModel == null) { ref.watch(selectedIdStateProvider); aiRequestModel = ref.watch(selectedRequestModelProvider .select((value) => value?.aiRequestModel)); diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index 4a014971..cc1a2a3b 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -193,7 +193,7 @@ class RequestItem extends ConsumerWidget { return SidebarRequestCard( id: id, apiType: requestModel.apiType, - method: requestModel.httpRequestModel!.method, + method: requestModel.httpRequestModel?.method, name: requestModel.name, url: requestModel.httpRequestModel?.url, selectedId: selectedId, diff --git a/lib/screens/home_page/editor_pane/url_card.dart b/lib/screens/home_page/editor_pane/url_card.dart index db9ab0f7..d84f27a7 100644 --- a/lib/screens/home_page/editor_pane/url_card.dart +++ b/lib/screens/home_page/editor_pane/url_card.dart @@ -102,6 +102,10 @@ class URLTextField extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final selectedId = ref.watch(selectedIdStateProvider); + ref.watch(selectedRequestModelProvider + .select((value) => value?.aiRequestModel?.url)); + ref.watch(selectedRequestModelProvider + .select((value) => value?.httpRequestModel?.url)); final requestModel = ref .read(collectionStateNotifierProvider.notifier) .getRequestModel(selectedId!)!; diff --git a/lib/widgets/card_sidebar_request.dart b/lib/widgets/card_sidebar_request.dart index 87dab9e4..372d88b5 100644 --- a/lib/widgets/card_sidebar_request.dart +++ b/lib/widgets/card_sidebar_request.dart @@ -11,7 +11,7 @@ class SidebarRequestCard extends StatelessWidget { super.key, required this.id, required this.apiType, - required this.method, + this.method, this.name, this.url, this.selectedId, @@ -30,7 +30,7 @@ class SidebarRequestCard extends StatelessWidget { final APIType apiType; final String? name; final String? url; - final HTTPVerb method; + final HTTPVerb? method; final String? selectedId; final String? editRequestId; final void Function()? onTap; diff --git a/lib/widgets/response_body.dart b/lib/widgets/response_body.dart index 7a679d8c..f1c866e9 100644 --- a/lib/widgets/response_body.dart +++ b/lib/widgets/response_body.dart @@ -49,8 +49,7 @@ class ResponseBody extends StatelessWidget { // '$kMsgUnknowContentType - ${responseModel.contentType}. $kUnexpectedRaiseIssue'); // } - var responseBodyView = (selectedRequestModel?.apiType == APIType.ai && - (responseModel.sseOutput?.isNotEmpty ?? false)) + var responseBodyView = selectedRequestModel?.apiType == APIType.ai ? (kAnswerRawBodyViewOptions, kSubTypePlain) : getResponseBodyViewOptions(mediaType); var options = responseBodyView.$1; @@ -70,6 +69,7 @@ class ResponseBody extends StatelessWidget { formattedBody: formattedBody, highlightLanguage: highlightLanguage, sseOutput: responseModel.sseOutput, + isAIResponse: selectedRequestModel?.apiType == APIType.ai, aiRequestModel: selectedRequestModel?.aiRequestModel, ); } diff --git a/lib/widgets/response_body_success.dart b/lib/widgets/response_body_success.dart index a2ea46b9..44c9b28a 100644 --- a/lib/widgets/response_body_success.dart +++ b/lib/widgets/response_body_success.dart @@ -17,6 +17,7 @@ class ResponseBodySuccess extends StatefulWidget { this.formattedBody, this.highlightLanguage, this.sseOutput, + this.isAIResponse = false, this.aiRequestModel, }); final MediaType mediaType; @@ -26,6 +27,7 @@ class ResponseBodySuccess extends StatefulWidget { final String? formattedBody; final List? sseOutput; final String? highlightLanguage; + final bool isAIResponse; final AIRequestModel? aiRequestModel; @override @@ -137,7 +139,7 @@ class _ResponseBodySuccessState extends State { ), ), ), - ResponseBodyView.raw || ResponseBodyView.answer => Expanded( + ResponseBodyView.answer => Expanded( child: Container( width: double.maxFinite, padding: kP8, @@ -150,6 +152,21 @@ class _ResponseBodySuccessState extends State { ), ), ), + ResponseBodyView.raw => Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: SingleChildScrollView( + child: SelectableText( + widget.isAIResponse + ? widget.body + : (widget.formattedBody ?? widget.body), + style: kCodeStyle, + ), + ), + ), + ), ResponseBodyView.sse => Expanded( child: Container( width: double.maxFinite, diff --git a/lib/widgets/texts.dart b/lib/widgets/texts.dart index 1ec47eb4..9d2e1a84 100644 --- a/lib/widgets/texts.dart +++ b/lib/widgets/texts.dart @@ -7,10 +7,10 @@ class SidebarRequestCardTextBox extends StatelessWidget { const SidebarRequestCardTextBox({ super.key, required this.apiType, - required this.method, + this.method, }); final APIType apiType; - final HTTPVerb method; + final HTTPVerb? method; @override Widget build(BuildContext context) { @@ -18,7 +18,7 @@ class SidebarRequestCardTextBox extends StatelessWidget { width: 24, child: Text( switch (apiType) { - APIType.rest => method.abbr, + APIType.rest => method!.abbr, APIType.graphql => apiType.abbr, APIType.ai => apiType.abbr, }, diff --git a/pubspec.lock b/pubspec.lock index 4f4c43ff..c6d22be0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1117,6 +1117,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + nanoid: + dependency: transitive + description: + name: nanoid + sha256: be3f8752d9046c825df2f3914195151eb876f3ad64b9d833dd0b799b77b8759e + url: "https://pub.dev" + source: hosted + version: "1.0.0" nanoid2: dependency: transitive description: diff --git a/test/models/history_models.dart b/test/models/history_models.dart index 6de41dc1..4256c7d4 100644 --- a/test/models/history_models.dart +++ b/test/models/history_models.dart @@ -57,6 +57,7 @@ final Map historyRequestModelJson1 = { "historyId": "historyId1", "metaData": historyMetaModelJson1, "httpRequestModel": httpRequestModelGet4Json, + 'aiRequestModel': null, "httpResponseModel": responseModelJson, 'preRequestScript': null, 'postRequestScript': null, diff --git a/test/models/request_models.dart b/test/models/request_models.dart index 1762706c..585cbc44 100644 --- a/test/models/request_models.dart +++ b/test/models/request_models.dart @@ -218,7 +218,8 @@ Map requestModelJson = { 'message': null, 'httpResponseModel': responseModelJson, 'preRequestScript': null, - 'postRequestScript': null + 'postRequestScript': null, + 'aiRequestModel': null }; /// Basic GET request model for apidash.dev