From f2ce403006ad6fe5fa9d9c9add98e1f218c184d2 Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Thu, 25 Sep 2025 13:20:43 +0530 Subject: [PATCH] feat: split dashbot models --- .../common/pages/dashbot_default_page.dart | 5 +- .../core/common/widgets/dashbot_action.dart | 3 +- .../dashbot_add_test_button.dart | 2 +- .../dashbot_apply_curl_button.dart | 2 +- .../dashbot_apply_openapi_button.dart | 2 +- .../dashbot_auto_fix_button.dart | 2 +- .../dashbot_download_doc_button.dart | 2 +- .../dashbot_generate_codeblock.dart | 2 +- ...shbot_generate_language_picker_button.dart | 3 +- .../dashbot_import_now_button.dart | 2 +- .../dashbot_upload_requests_button.dart | 3 +- .../dsahbot_select_operation_button.dart | 3 +- lib/dashbot/core/constants/constants.dart | 143 ++++++++ lib/dashbot/core/error/chat_failure.dart | 8 + lib/dashbot/core/routes/dashbot_router.dart | 2 +- .../services/actions/auto_fix_service.dart | 3 +- .../actions/request_apply_service.dart | 2 +- .../core/services/agent/prompt_builder.dart | 3 +- .../features/chat/models/chat_action.dart | 47 +++ .../features/chat/models/chat_message.dart | 68 ++++ .../features/chat/models/chat_models.dart | 316 ------------------ .../features/chat/models/chat_response.dart | 35 ++ .../features/chat/models/chat_state.dart | 35 ++ .../chat/view/pages/dashbot_chat_page.dart | 2 +- .../chat/view/widgets/chat_bubble.dart | 3 +- .../view/widgets/dashbot_task_buttons.dart | 2 +- .../chat/viewmodel/chat_viewmodel.dart | 5 +- .../features/home/view/pages/home_page.dart | 2 +- 28 files changed, 367 insertions(+), 340 deletions(-) create mode 100644 lib/dashbot/core/constants/constants.dart create mode 100644 lib/dashbot/core/error/chat_failure.dart create mode 100644 lib/dashbot/features/chat/models/chat_action.dart create mode 100644 lib/dashbot/features/chat/models/chat_message.dart delete mode 100644 lib/dashbot/features/chat/models/chat_models.dart create mode 100644 lib/dashbot/features/chat/models/chat_response.dart create mode 100644 lib/dashbot/features/chat/models/chat_state.dart diff --git a/lib/dashbot/core/common/pages/dashbot_default_page.dart b/lib/dashbot/core/common/pages/dashbot_default_page.dart index ef75d2ee..0b7afcd1 100644 --- a/lib/dashbot/core/common/pages/dashbot_default_page.dart +++ b/lib/dashbot/core/common/pages/dashbot_default_page.dart @@ -1,7 +1,6 @@ -import 'package:apidash_design_system/apidash_design_system.dart' - show kVSpacer20, kVSpacer16, kVSpacer10; -import '../../../features/chat/models/chat_models.dart'; +import 'package:apidash_design_system/apidash_design_system.dart'; import '../../../features/home/view/widgets/home_screen_task_button.dart'; +import '../../constants/constants.dart'; import '../../routes/dashbot_routes.dart'; import '../../utils/dashbot_icons.dart'; import 'package:flutter/material.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action.dart b/lib/dashbot/core/common/widgets/dashbot_action.dart index 72bd0029..b5fe7f9e 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../features/chat/models/chat_models.dart'; +import '../../../features/chat/models/chat_action.dart'; +import '../../constants/constants.dart'; import 'dashbot_action_buttons/dashbot_actions_buttons.dart'; /// Base mixin for action widgets. diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_add_test_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_add_test_button.dart index ef623b70..61b884da 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_add_test_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_add_test_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../../../../features/chat/viewmodel/chat_viewmodel.dart'; import '../dashbot_action.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_apply_curl_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_apply_curl_button.dart index 7f52a4f6..dfa3e911 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_apply_curl_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_apply_curl_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../../../../features/chat/viewmodel/chat_viewmodel.dart'; import '../dashbot_action.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_apply_openapi_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_apply_openapi_button.dart index 2d66db86..d7dbc317 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_apply_openapi_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_apply_openapi_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../../../../features/chat/viewmodel/chat_viewmodel.dart'; import '../dashbot_action.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_auto_fix_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_auto_fix_button.dart index 41e11538..1ef3e2d9 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_auto_fix_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_auto_fix_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../../../../features/chat/viewmodel/chat_viewmodel.dart'; import '../dashbot_action.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_download_doc_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_download_doc_button.dart index a22fda6b..2a81c701 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_download_doc_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_download_doc_button.dart @@ -3,7 +3,7 @@ import 'dart:typed_data'; import 'package:apidash/utils/utils.dart'; import 'package:flutter/material.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../dashbot_action.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_codeblock.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_codeblock.dart index 1980f135..34ca1dd6 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_codeblock.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_codeblock.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../dashbot_action.dart'; class DashbotGeneratedCodeBlock extends StatelessWidget diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_language_picker_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_language_picker_button.dart index 6ac85226..dd7033f3 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_language_picker_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_language_picker_button.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../../../../features/chat/viewmodel/chat_viewmodel.dart'; +import '../../../constants/constants.dart'; import '../dashbot_action.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_import_now_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_import_now_button.dart index 287c00b1..8de744fe 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_import_now_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_import_now_button.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:openapi_spec/openapi_spec.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../../../../features/chat/view/widgets/openapi_operation_picker_dialog.dart'; import '../../../../features/chat/viewmodel/chat_viewmodel.dart'; import '../../../providers/dashbot_window_notifier.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_upload_requests_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_upload_requests_button.dart index 1e919262..2f59959e 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_upload_requests_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_upload_requests_button.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../../../../features/chat/viewmodel/chat_viewmodel.dart'; +import '../../../constants/constants.dart'; import '../dashbot_action.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../providers/attachments_provider.dart'; diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dsahbot_select_operation_button.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dsahbot_select_operation_button.dart index 2c497b26..fe44473a 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dsahbot_select_operation_button.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dsahbot_select_operation_button.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; -import '../../../../features/chat/models/chat_models.dart'; +import '../../../../features/chat/models/chat_action.dart'; import '../../../../features/chat/viewmodel/chat_viewmodel.dart'; import '../dashbot_action.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - class DashbotSelectOperationButton extends ConsumerWidget with DashbotActionMixin { @override diff --git a/lib/dashbot/core/constants/constants.dart b/lib/dashbot/core/constants/constants.dart new file mode 100644 index 00000000..32a80192 --- /dev/null +++ b/lib/dashbot/core/constants/constants.dart @@ -0,0 +1,143 @@ +/// Role of a chat message author. +enum MessageRole { user, system } + +enum ChatMessageType { + explainResponse, + debugError, + generateTest, + generateDoc, + generateCode, + importCurl, + importOpenApi, + general +} + +enum ChatActionType { + updateField, + addHeader, + updateHeader, + deleteHeader, + updateBody, + updateUrl, + updateMethod, + showLanguages, + applyCurl, + applyOpenApi, + downloadDoc, + other, + noAction, + uploadAsset, +} + +enum ChatActionTarget { + httpRequestModel, + codegen, + test, + code, + attachment, + documentation, +} + +ChatActionType chatActionTypeFromString(String s) { + switch (s) { + case 'update_field': + return ChatActionType.updateField; + case 'add_header': + return ChatActionType.addHeader; + case 'update_header': + return ChatActionType.updateHeader; + case 'delete_header': + return ChatActionType.deleteHeader; + case 'update_body': + return ChatActionType.updateBody; + case 'update_url': + return ChatActionType.updateUrl; + case 'update_method': + return ChatActionType.updateMethod; + case 'show_languages': + return ChatActionType.showLanguages; + case 'apply_curl': + return ChatActionType.applyCurl; + case 'apply_openapi': + return ChatActionType.applyOpenApi; + case 'download_doc': + return ChatActionType.downloadDoc; + case 'upload_asset': + return ChatActionType.uploadAsset; + case 'no_action': + return ChatActionType.noAction; + case 'other': + return ChatActionType.other; + default: + return ChatActionType.other; + } +} + +String chatActionTypeToString(ChatActionType t) { + switch (t) { + case ChatActionType.updateField: + return 'update_field'; + case ChatActionType.addHeader: + return 'add_header'; + case ChatActionType.updateHeader: + return 'update_header'; + case ChatActionType.deleteHeader: + return 'delete_header'; + case ChatActionType.updateBody: + return 'update_body'; + case ChatActionType.updateUrl: + return 'update_url'; + case ChatActionType.updateMethod: + return 'update_method'; + case ChatActionType.showLanguages: + return 'show_languages'; + case ChatActionType.applyCurl: + return 'apply_curl'; + case ChatActionType.applyOpenApi: + return 'apply_openapi'; + case ChatActionType.downloadDoc: + return 'download_doc'; + case ChatActionType.other: + return 'other'; + case ChatActionType.noAction: + return 'no_action'; + case ChatActionType.uploadAsset: + return 'upload_asset'; + } +} + +ChatActionTarget chatActionTargetFromString(String s) { + switch (s) { + case 'httpRequestModel': + return ChatActionTarget.httpRequestModel; + case 'codegen': + return ChatActionTarget.codegen; + case 'test': + return ChatActionTarget.test; + case 'code': + return ChatActionTarget.code; + case 'attachment': + return ChatActionTarget.attachment; + case 'documentation': + return ChatActionTarget.documentation; + default: + return ChatActionTarget.httpRequestModel; + } +} + +String chatActionTargetToString(ChatActionTarget t) { + switch (t) { + case ChatActionTarget.httpRequestModel: + return 'httpRequestModel'; + case ChatActionTarget.codegen: + return 'codegen'; + case ChatActionTarget.test: + return 'test'; + case ChatActionTarget.code: + return 'code'; + case ChatActionTarget.attachment: + return 'attachment'; + case ChatActionTarget.documentation: + return 'documentation'; + } +} diff --git a/lib/dashbot/core/error/chat_failure.dart b/lib/dashbot/core/error/chat_failure.dart new file mode 100644 index 00000000..29172255 --- /dev/null +++ b/lib/dashbot/core/error/chat_failure.dart @@ -0,0 +1,8 @@ +class ChatFailure implements Exception { + final String message; + final String? code; + const ChatFailure(this.message, {this.code}); + + @override + String toString() => 'ChatFailure: $message'; +} diff --git a/lib/dashbot/core/routes/dashbot_router.dart b/lib/dashbot/core/routes/dashbot_router.dart index 858b5172..004c6e34 100644 --- a/lib/dashbot/core/routes/dashbot_router.dart +++ b/lib/dashbot/core/routes/dashbot_router.dart @@ -1,6 +1,6 @@ import '../../features/chat/view/pages/dashbot_chat_page.dart'; -import '../../features/chat/models/chat_models.dart'; +import '../constants/constants.dart'; import 'dashbot_routes.dart'; import '../common/pages/dashbot_default_page.dart'; import '../../features/home/view/pages/home_page.dart'; diff --git a/lib/dashbot/core/services/actions/auto_fix_service.dart b/lib/dashbot/core/services/actions/auto_fix_service.dart index c8977eef..2d0edc48 100644 --- a/lib/dashbot/core/services/actions/auto_fix_service.dart +++ b/lib/dashbot/core/services/actions/auto_fix_service.dart @@ -1,7 +1,8 @@ import 'package:apidash_core/apidash_core.dart'; import 'package:apidash/models/models.dart'; -import '../../../features/chat/models/chat_models.dart'; +import '../../../features/chat/models/chat_action.dart'; +import '../../constants/constants.dart'; import 'request_apply_service.dart'; class AutoFixService { diff --git a/lib/dashbot/core/services/actions/request_apply_service.dart b/lib/dashbot/core/services/actions/request_apply_service.dart index 5ee56021..a1969290 100644 --- a/lib/dashbot/core/services/actions/request_apply_service.dart +++ b/lib/dashbot/core/services/actions/request_apply_service.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:apidash_core/apidash_core.dart'; -import '../../../features/chat/models/chat_models.dart'; +import '../../constants/constants.dart'; import '../base/url_env_service.dart'; class ApplyResult { diff --git a/lib/dashbot/core/services/agent/prompt_builder.dart b/lib/dashbot/core/services/agent/prompt_builder.dart index 4107fdc3..541e7615 100644 --- a/lib/dashbot/core/services/agent/prompt_builder.dart +++ b/lib/dashbot/core/services/agent/prompt_builder.dart @@ -1,7 +1,8 @@ import 'package:apidash/models/models.dart'; import 'package:apidash/dashbot/core/constants/dashbot_prompts.dart' as dash; -import '../../../features/chat/models/chat_models.dart'; +import '../../../features/chat/models/chat_message.dart'; +import '../../constants/constants.dart'; class PromptBuilder { String buildSystemPrompt( diff --git a/lib/dashbot/features/chat/models/chat_action.dart b/lib/dashbot/features/chat/models/chat_action.dart new file mode 100644 index 00000000..5b21da05 --- /dev/null +++ b/lib/dashbot/features/chat/models/chat_action.dart @@ -0,0 +1,47 @@ +import '../../../core/constants/constants.dart'; + +class ChatAction { + final String action; + final String target; + final String field; + final String? path; + final dynamic value; + final ChatActionType actionType; // enum representation + final ChatActionTarget targetType; // enum representation + + const ChatAction({ + required this.action, + required this.target, + this.field = '', // Default to empty string + this.path, + this.value, + required this.actionType, + required this.targetType, + }); + + factory ChatAction.fromJson(Map json) { + final actionStr = json['action'] as String? ?? 'other'; + final targetStr = json['target'] as String? ?? 'httpRequestModel'; + return ChatAction( + action: actionStr, + target: targetStr, + field: json['field'] as String? ?? '', + path: json['path'] as String?, + value: json['value'], + actionType: chatActionTypeFromString(actionStr), + targetType: chatActionTargetFromString(targetStr), + ); + } + + Map toJson() { + return { + 'action': action, + 'target': target, + 'field': field, + 'path': path, + 'value': value, + 'action_type': chatActionTypeToString(actionType), + 'target_type': chatActionTargetToString(targetType), + }; + } +} diff --git a/lib/dashbot/features/chat/models/chat_message.dart b/lib/dashbot/features/chat/models/chat_message.dart new file mode 100644 index 00000000..33ea22ec --- /dev/null +++ b/lib/dashbot/features/chat/models/chat_message.dart @@ -0,0 +1,68 @@ +import 'package:flutter/foundation.dart'; + +import '../../../core/constants/constants.dart'; +import 'chat_action.dart'; + +class ChatMessage { + final String id; + final String content; + final MessageRole role; + final DateTime timestamp; + final ChatMessageType? messageType; + // Multiple actions support. If provided, UI should render these. + final List? actions; + + const ChatMessage({ + required this.id, + required this.content, + required this.role, + required this.timestamp, + this.messageType, + this.actions, + }); + + ChatMessage copyWith({ + String? id, + String? content, + MessageRole? role, + DateTime? timestamp, + ChatMessageType? messageType, + List? actions, + }) { + return ChatMessage( + id: id ?? this.id, + content: content ?? this.content, + role: role ?? this.role, + timestamp: timestamp ?? this.timestamp, + messageType: messageType ?? this.messageType, + actions: actions ?? this.actions, + ); + } + + @override + String toString() { + return 'ChatMessage(id: $id, content: $content, role: $role, timestamp: $timestamp, messageType: $messageType, actions: $actions)'; + } + + @override + bool operator ==(covariant ChatMessage other) { + if (identical(this, other)) return true; + + return other.id == id && + other.content == content && + other.role == role && + other.timestamp == timestamp && + other.messageType == messageType && + listEquals(other.actions, actions); + } + + @override + int get hashCode { + return id.hashCode ^ + content.hashCode ^ + role.hashCode ^ + timestamp.hashCode ^ + messageType.hashCode ^ + actions.hashCode; + } +} diff --git a/lib/dashbot/features/chat/models/chat_models.dart b/lib/dashbot/features/chat/models/chat_models.dart deleted file mode 100644 index 70690ae6..00000000 --- a/lib/dashbot/features/chat/models/chat_models.dart +++ /dev/null @@ -1,316 +0,0 @@ -/// Role of a chat message author. -enum MessageRole { user, system } - -class ChatState { - final Map> chatSessions; // requestId -> messages - final bool isGenerating; - final String currentStreamingResponse; - final String? currentRequestId; - final ChatFailure? lastError; - - const ChatState({ - this.chatSessions = const {}, - this.isGenerating = false, - this.currentStreamingResponse = '', - this.currentRequestId, - this.lastError, - }); - - ChatState copyWith({ - Map>? chatSessions, - bool? isGenerating, - String? currentStreamingResponse, - String? currentRequestId, - ChatFailure? lastError, - }) { - return ChatState( - chatSessions: chatSessions ?? this.chatSessions, - isGenerating: isGenerating ?? this.isGenerating, - currentStreamingResponse: - currentStreamingResponse ?? this.currentStreamingResponse, - currentRequestId: currentRequestId ?? this.currentRequestId, - lastError: lastError ?? this.lastError, - ); - } -} - -class ChatMessage { - final String id; - final String content; - final MessageRole role; - final DateTime timestamp; - final ChatMessageType? messageType; - // Multiple actions support. If provided, UI should render these. - final List? actions; - - const ChatMessage({ - required this.id, - required this.content, - required this.role, - required this.timestamp, - this.messageType, - this.actions, - }); - - ChatMessage copyWith({ - String? id, - String? content, - MessageRole? role, - DateTime? timestamp, - ChatMessageType? messageType, - List? actions, - }) { - return ChatMessage( - id: id ?? this.id, - content: content ?? this.content, - role: role ?? this.role, - timestamp: timestamp ?? this.timestamp, - messageType: messageType ?? this.messageType, - actions: actions ?? this.actions, - ); - } -} - -class ChatResponse { - final String content; - final ChatMessageType? messageType; - - const ChatResponse({required this.content, this.messageType}); - - ChatResponse copyWith({String? content, ChatMessageType? messageType}) { - return ChatResponse( - content: content ?? this.content, - messageType: messageType ?? this.messageType, - ); - } -} - -enum ChatMessageType { - explainResponse, - debugError, - generateTest, - generateDoc, - generateCode, - importCurl, - importOpenApi, - general -} - -// Enum definitions for action types and targets to reduce stringly-typed logic. -enum ChatActionType { - updateField, - addHeader, - updateHeader, - deleteHeader, - updateBody, - updateUrl, - updateMethod, - showLanguages, - applyCurl, - applyOpenApi, - downloadDoc, - other, - noAction, - uploadAsset, -} - -enum ChatActionTarget { - httpRequestModel, - codegen, - test, - code, - attachment, - documentation, -} - -ChatActionType _chatActionTypeFromString(String s) { - switch (s) { - case 'update_field': - return ChatActionType.updateField; - case 'add_header': - return ChatActionType.addHeader; - case 'update_header': - return ChatActionType.updateHeader; - case 'delete_header': - return ChatActionType.deleteHeader; - case 'update_body': - return ChatActionType.updateBody; - case 'update_url': - return ChatActionType.updateUrl; - case 'update_method': - return ChatActionType.updateMethod; - case 'show_languages': - return ChatActionType.showLanguages; - case 'apply_curl': - return ChatActionType.applyCurl; - case 'apply_openapi': - return ChatActionType.applyOpenApi; - case 'download_doc': - return ChatActionType.downloadDoc; - case 'upload_asset': - return ChatActionType.uploadAsset; - case 'no_action': - return ChatActionType.noAction; - case 'other': - return ChatActionType.other; - default: - return ChatActionType.other; - } -} - -String chatActionTypeToString(ChatActionType t) { - switch (t) { - case ChatActionType.updateField: - return 'update_field'; - case ChatActionType.addHeader: - return 'add_header'; - case ChatActionType.updateHeader: - return 'update_header'; - case ChatActionType.deleteHeader: - return 'delete_header'; - case ChatActionType.updateBody: - return 'update_body'; - case ChatActionType.updateUrl: - return 'update_url'; - case ChatActionType.updateMethod: - return 'update_method'; - case ChatActionType.showLanguages: - return 'show_languages'; - case ChatActionType.applyCurl: - return 'apply_curl'; - case ChatActionType.applyOpenApi: - return 'apply_openapi'; - case ChatActionType.downloadDoc: - return 'download_doc'; - case ChatActionType.other: - return 'other'; - case ChatActionType.noAction: - return 'no_action'; - case ChatActionType.uploadAsset: - return 'upload_asset'; - } -} - -ChatActionTarget _chatActionTargetFromString(String s) { - switch (s) { - case 'httpRequestModel': - return ChatActionTarget.httpRequestModel; - case 'codegen': - return ChatActionTarget.codegen; - case 'test': - return ChatActionTarget.test; - case 'code': - return ChatActionTarget.code; - case 'attachment': - return ChatActionTarget.attachment; - case 'documentation': - return ChatActionTarget.documentation; - default: - return ChatActionTarget.httpRequestModel; - } -} - -String chatActionTargetToString(ChatActionTarget t) { - switch (t) { - case ChatActionTarget.httpRequestModel: - return 'httpRequestModel'; - case ChatActionTarget.codegen: - return 'codegen'; - case ChatActionTarget.test: - return 'test'; - case ChatActionTarget.code: - return 'code'; - case ChatActionTarget.attachment: - return 'attachment'; - case ChatActionTarget.documentation: - return 'documentation'; - } -} - -// Action model for auto-fix functionality -class ChatAction { - final String action; - final String target; - final String field; - final String? path; - final dynamic value; - final ChatActionType actionType; // enum representation - final ChatActionTarget targetType; // enum representation - - const ChatAction({ - required this.action, - required this.target, - this.field = '', // Default to empty string - this.path, - this.value, - required this.actionType, - required this.targetType, - }); - - factory ChatAction.fromJson(Map json) { - final actionStr = json['action'] as String? ?? 'other'; - final targetStr = json['target'] as String? ?? 'httpRequestModel'; - return ChatAction( - action: actionStr, - target: targetStr, - field: json['field'] as String? ?? '', - path: json['path'] as String?, - value: json['value'], - actionType: _chatActionTypeFromString(actionStr), - targetType: _chatActionTargetFromString(targetStr), - ); - } - - Map toJson() { - return { - 'action': action, - 'target': target, - 'field': field, - 'path': path, - 'value': value, - 'action_type': chatActionTypeToString(actionType), - 'target_type': chatActionTargetToString(targetType), - }; - } -} - -// Failure classes using fpdart Either pattern -abstract class ChatFailure implements Exception { - final String message; - final String? code; - const ChatFailure(this.message, {this.code}); - - @override - String toString() => 'ChatFailure: $message'; -} - -class NetworkFailure extends ChatFailure { - const NetworkFailure(super.message, {super.code}); -} - -class AIModelNotConfiguredFailure extends ChatFailure { - const AIModelNotConfiguredFailure() - : super("Please configure an AI model in the AI Request tab"); -} - -class APIKeyMissingFailure extends ChatFailure { - const APIKeyMissingFailure(String provider) - : super("API key missing for $provider"); -} - -class NoRequestSelectedFailure extends ChatFailure { - const NoRequestSelectedFailure() : super("No request selected"); -} - -class InvalidRequestContextFailure extends ChatFailure { - const InvalidRequestContextFailure(super.message); -} - -class RateLimitFailure extends ChatFailure { - const RateLimitFailure() - : super("Rate limit exceeded. Please try again later."); -} - -class StreamingFailure extends ChatFailure { - const StreamingFailure(super.message); -} diff --git a/lib/dashbot/features/chat/models/chat_response.dart b/lib/dashbot/features/chat/models/chat_response.dart new file mode 100644 index 00000000..7036a5f5 --- /dev/null +++ b/lib/dashbot/features/chat/models/chat_response.dart @@ -0,0 +1,35 @@ +import '../../../core/constants/constants.dart'; + +class ChatResponse { + final String content; + final ChatMessageType? messageType; + + const ChatResponse({ + required this.content, + this.messageType, + }); + + ChatResponse copyWith({ + String? content, + ChatMessageType? messageType, + }) { + return ChatResponse( + content: content ?? this.content, + messageType: messageType ?? this.messageType, + ); + } + + @override + String toString() => + 'ChatResponse(content: $content, messageType: $messageType)'; + + @override + bool operator ==(covariant ChatResponse other) { + if (identical(this, other)) return true; + + return other.content == content && other.messageType == messageType; + } + + @override + int get hashCode => content.hashCode ^ messageType.hashCode; +} diff --git a/lib/dashbot/features/chat/models/chat_state.dart b/lib/dashbot/features/chat/models/chat_state.dart new file mode 100644 index 00000000..1c53dd4d --- /dev/null +++ b/lib/dashbot/features/chat/models/chat_state.dart @@ -0,0 +1,35 @@ +import '../../../core/error/chat_failure.dart'; +import 'chat_message.dart'; + +class ChatState { + final Map> chatSessions; // requestId -> messages + final bool isGenerating; + final String currentStreamingResponse; + final String? currentRequestId; + final ChatFailure? lastError; + + const ChatState({ + this.chatSessions = const {}, + this.isGenerating = false, + this.currentStreamingResponse = '', + this.currentRequestId, + this.lastError, + }); + + ChatState copyWith({ + Map>? chatSessions, + bool? isGenerating, + String? currentStreamingResponse, + String? currentRequestId, + ChatFailure? lastError, + }) { + return ChatState( + chatSessions: chatSessions ?? this.chatSessions, + isGenerating: isGenerating ?? this.isGenerating, + currentStreamingResponse: + currentStreamingResponse ?? this.currentStreamingResponse, + currentRequestId: currentRequestId ?? this.currentRequestId, + lastError: lastError ?? this.lastError, + ); + } +} diff --git a/lib/dashbot/features/chat/view/pages/dashbot_chat_page.dart b/lib/dashbot/features/chat/view/pages/dashbot_chat_page.dart index 849699a1..0085f925 100644 --- a/lib/dashbot/features/chat/view/pages/dashbot_chat_page.dart +++ b/lib/dashbot/features/chat/view/pages/dashbot_chat_page.dart @@ -1,7 +1,7 @@ import 'package:apidash/dashbot/features/chat/view/widgets/dashbot_task_buttons.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; -import '../../models/chat_models.dart'; +import '../../../../core/constants/constants.dart'; import '../widgets/chat_bubble.dart'; import '../../viewmodel/chat_viewmodel.dart'; import 'package:flutter/material.dart'; diff --git a/lib/dashbot/features/chat/view/widgets/chat_bubble.dart b/lib/dashbot/features/chat/view/widgets/chat_bubble.dart index 5a3b8122..53150009 100644 --- a/lib/dashbot/features/chat/view/widgets/chat_bubble.dart +++ b/lib/dashbot/features/chat/view/widgets/chat_bubble.dart @@ -1,12 +1,13 @@ import 'package:apidash/dashbot/core/utils/safe_parse_json_message.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; +import '../../../../core/constants/constants.dart'; import '../../../../core/utils/dashbot_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../models/chat_models.dart'; import '../../../../core/common/widgets/dashbot_action.dart'; +import '../../models/chat_action.dart'; class ChatBubble extends ConsumerWidget { final String message; diff --git a/lib/dashbot/features/chat/view/widgets/dashbot_task_buttons.dart b/lib/dashbot/features/chat/view/widgets/dashbot_task_buttons.dart index 411fd6f2..bd6770b7 100644 --- a/lib/dashbot/features/chat/view/widgets/dashbot_task_buttons.dart +++ b/lib/dashbot/features/chat/view/widgets/dashbot_task_buttons.dart @@ -6,7 +6,7 @@ import 'package:apidash/screens/common_widgets/agentic_ui_features/tool_generati import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../models/chat_models.dart'; +import '../../../../core/constants/constants.dart'; import '../../../home/view/widgets/home_screen_task_button.dart'; import '../../../../core/providers/dashbot_window_notifier.dart'; diff --git a/lib/dashbot/features/chat/viewmodel/chat_viewmodel.dart b/lib/dashbot/features/chat/viewmodel/chat_viewmodel.dart index fb100378..1d28325c 100644 --- a/lib/dashbot/features/chat/viewmodel/chat_viewmodel.dart +++ b/lib/dashbot/features/chat/viewmodel/chat_viewmodel.dart @@ -1,17 +1,20 @@ import 'dart:convert'; +import 'package:apidash/dashbot/features/chat/models/chat_message.dart'; import 'package:apidash_core/apidash_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; import 'package:apidash/models/models.dart'; import 'package:apidash/utils/utils.dart'; +import '../../../core/constants/constants.dart'; import '../../../core/model/chat_attachment.dart'; import '../../../core/services/curl_import_service.dart'; import '../../../core/services/openapi_import_service.dart'; import '../../../core/utils/safe_parse_json_message.dart'; import '../../../core/constants/dashbot_prompts.dart' as dash; -import '../models/chat_models.dart'; +import '../models/chat_action.dart'; +import '../models/chat_state.dart'; import '../repository/chat_remote_repository.dart'; import '../providers/service_providers.dart'; diff --git a/lib/dashbot/features/home/view/pages/home_page.dart b/lib/dashbot/features/home/view/pages/home_page.dart index bcd9ebec..926325d7 100644 --- a/lib/dashbot/features/home/view/pages/home_page.dart +++ b/lib/dashbot/features/home/view/pages/home_page.dart @@ -3,6 +3,7 @@ import 'package:apidash/screens/common_widgets/agentic_ui_features/ai_ui_designe import 'package:apidash/screens/common_widgets/agentic_ui_features/tool_generation/generate_tool_dialog.dart'; import 'package:flutter/foundation.dart'; +import '../../../../core/constants/constants.dart'; import '../../../../core/utils/dashbot_icons.dart'; import '../../../../core/providers/dashbot_window_notifier.dart'; @@ -10,7 +11,6 @@ import '../../../../core/routes/dashbot_routes.dart'; import 'package:apidash_design_system/tokens/measurements.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../../chat/models/chat_models.dart'; import '../widgets/home_screen_task_button.dart'; class DashbotHomePage extends ConsumerStatefulWidget {