From aef4ee5039a844123f428ff13c40f57c155a48e2 Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Tue, 2 Sep 2025 12:44:10 +0530 Subject: [PATCH] refactor: remove unused Dashbot context and request provider; update chat viewmodel to use the existing request model --- .../common/pages/dashbot_default_page.dart | 5 +- .../core/model/dashbot_request_context.dart | 29 ----- .../providers/dashbot_request_provider.dart | 9 -- .../providers/dashbot_window_notifier.dart | 17 ++- lib/dashbot/dashbot.dart | 2 - lib/dashbot/dashbot_dashboard.dart | 24 ++-- .../chat/view/widgets/chat_bubble.dart | 21 ++- .../chat/viewmodel/chat_viewmodel.dart | 122 +++++++++--------- lib/providers/dashbot_context_provider.dart | 22 ---- lib/providers/providers.dart | 1 - lib/screens/dashboard.dart | 10 +- pubspec.lock | 23 ---- pubspec.yaml | 3 +- 13 files changed, 97 insertions(+), 191 deletions(-) delete mode 100644 lib/dashbot/core/model/dashbot_request_context.dart delete mode 100644 lib/dashbot/core/providers/dashbot_request_provider.dart delete mode 100644 lib/providers/dashbot_context_provider.dart diff --git a/lib/dashbot/core/common/pages/dashbot_default_page.dart b/lib/dashbot/core/common/pages/dashbot_default_page.dart index 0627a12d..fc1df669 100644 --- a/lib/dashbot/core/common/pages/dashbot_default_page.dart +++ b/lib/dashbot/core/common/pages/dashbot_default_page.dart @@ -14,7 +14,6 @@ class DashbotDefaultPage extends StatelessWidget { children: [ kVSpacer16, DashbotIcons.getDashbotIcon1(width: 60), - kVSpacer20, Text( 'Hello there!', @@ -22,7 +21,7 @@ class DashbotDefaultPage extends StatelessWidget { ), kVSpacer10, Text( - 'Request not made yet', + 'Seems like you haven\'t made any Requests yet', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, @@ -32,7 +31,7 @@ class DashbotDefaultPage extends StatelessWidget { Text( "Why not go ahead and make one?", textAlign: TextAlign.center, - style: TextStyle(fontSize: 16, fontWeight: FontWeight.w200), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w400), ), ], ); diff --git a/lib/dashbot/core/model/dashbot_request_context.dart b/lib/dashbot/core/model/dashbot_request_context.dart deleted file mode 100644 index 4f97cf28..00000000 --- a/lib/dashbot/core/model/dashbot_request_context.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:apidash_core/apidash_core.dart'; - -/// Context object that Dashbot needs from the host app. -/// -/// Host apps should create/override a provider that returns this object -/// so Dashbot can react to changes in the current request selection. -class DashbotRequestContext { - final String? requestId; - final String? requestName; - final String? requestDescription; - final APIType apiType; - final AIRequestModel? aiRequestModel; - final HttpRequestModel? httpRequestModel; - final int? responseStatus; - final String? responseMessage; - final HttpResponseModel? httpResponseModel; - - const DashbotRequestContext({ - required this.apiType, - this.requestId, - this.requestName, - this.requestDescription, - this.aiRequestModel, - this.httpRequestModel, - this.responseStatus, - this.responseMessage, - this.httpResponseModel, - }); -} diff --git a/lib/dashbot/core/providers/dashbot_request_provider.dart b/lib/dashbot/core/providers/dashbot_request_provider.dart deleted file mode 100644 index e9c4a7bd..00000000 --- a/lib/dashbot/core/providers/dashbot_request_provider.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../model/dashbot_request_context.dart'; - -/// Default provider for Dashbot's external request context. -/// The host app should override this provider at the Dashbot subtree. -final dashbotRequestContextProvider = Provider( - (ref) => null, -); \ No newline at end of file diff --git a/lib/dashbot/core/providers/dashbot_window_notifier.dart b/lib/dashbot/core/providers/dashbot_window_notifier.dart index c1626505..7640fb39 100644 --- a/lib/dashbot/core/providers/dashbot_window_notifier.dart +++ b/lib/dashbot/core/providers/dashbot_window_notifier.dart @@ -1,15 +1,9 @@ import '../model/dashbot_window_model.dart'; import 'package:flutter/material.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -part 'dashbot_window_notifier.g.dart'; - -@Riverpod(keepAlive: true) -class DashbotWindowNotifier extends _$DashbotWindowNotifier { - @override - DashbotWindowModel build() { - return const DashbotWindowModel(); - } +class DashbotWindowNotifier extends StateNotifier { + DashbotWindowNotifier() : super(const DashbotWindowModel()); void updatePosition(double dx, double dy, Size screenSize) { state = state.copyWith( @@ -34,3 +28,8 @@ class DashbotWindowNotifier extends _$DashbotWindowNotifier { state = state.copyWith(isActive: !state.isActive); } } + +final dashbotWindowNotifierProvider = + StateNotifierProvider((ref) { + return DashbotWindowNotifier(); +}); diff --git a/lib/dashbot/dashbot.dart b/lib/dashbot/dashbot.dart index 0c969383..76a71656 100644 --- a/lib/dashbot/dashbot.dart +++ b/lib/dashbot/dashbot.dart @@ -1,5 +1,3 @@ export 'dashbot_dashboard.dart'; export 'core/providers/dashbot_window_notifier.dart'; export 'core/utils/utils.dart'; -export 'core/model/dashbot_request_context.dart'; -export 'core/providers/dashbot_request_provider.dart'; diff --git a/lib/dashbot/dashbot_dashboard.dart b/lib/dashbot/dashbot_dashboard.dart index 30060b4c..dbaa1993 100644 --- a/lib/dashbot/dashbot_dashboard.dart +++ b/lib/dashbot/dashbot_dashboard.dart @@ -1,8 +1,9 @@ +import 'package:apidash/providers/providers.dart'; +import 'package:apidash_core/apidash_core.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'core/utils/dashbot_icons.dart'; import 'core/providers/dashbot_window_notifier.dart'; -import 'core/providers/dashbot_request_provider.dart'; import 'core/routes/dashbot_router.dart'; import 'core/routes/dashbot_routes.dart'; import 'package:flutter/material.dart'; @@ -17,7 +18,11 @@ class DashbotWindow extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final windowState = ref.watch(dashbotWindowNotifierProvider); final windowNotifier = ref.read(dashbotWindowNotifierProvider.notifier); - final dashbotCtx = ref.watch(dashbotRequestContextProvider); + final defaultModelJson = ref.watch(settingsProvider).defaultAIModel; + final dashbotCtx = defaultModelJson == null + ? const AIRequestModel() + : AIRequestModel.fromJson(defaultModelJson); + final currentRequest = ref.watch(selectedRequestModelProvider); return Stack( children: [ @@ -66,13 +71,9 @@ class DashbotWindow extends ConsumerWidget { // TODO: remove the show active request name/model in prod kHSpacer12, Text( - dashbotCtx - ?.aiRequestModel - ?.modelApiProvider - ?.name == - null + dashbotCtx.modelApiProvider?.name == null ? 'DashBot' - : 'DashBot · ${dashbotCtx?.aiRequestModel?.modelApiProvider?.name}', + : 'DashBot · ${dashbotCtx.modelApiProvider?.name}', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, @@ -96,10 +97,9 @@ class DashbotWindow extends ConsumerWidget { ), Expanded( child: Navigator( - initialRoute: DashbotRoutes.dashbotHome, - // currentRequest?.responseStatus == null - // ? DashbotRoutes.dashbotDefault - // : DashbotRoutes.dashbotHome, + initialRoute: currentRequest?.responseStatus == null + ? DashbotRoutes.dashbotDefault + : DashbotRoutes.dashbotHome, onGenerateRoute: generateRoute, ), ), diff --git a/lib/dashbot/features/chat/view/widgets/chat_bubble.dart b/lib/dashbot/features/chat/view/widgets/chat_bubble.dart index 6a79b3cf..0bdb0679 100644 --- a/lib/dashbot/features/chat/view/widgets/chat_bubble.dart +++ b/lib/dashbot/features/chat/view/widgets/chat_bubble.dart @@ -37,17 +37,16 @@ class ChatBubble extends StatelessWidget { ); } return Align( - alignment: - role == MessageRole.user - ? Alignment.centerRight - : Alignment.centerLeft, + alignment: role == MessageRole.user + ? Alignment.centerRight + : Alignment.centerLeft, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (role == MessageRole.system) ...[ kVSpacer6, - Image.asset("assets/dashbot_icon_1.png", width: 42), + DashbotIcons.getDashbotIcon1(width: 42), kVSpacer8, ], Container( @@ -57,10 +56,9 @@ class ChatBubble extends StatelessWidget { maxWidth: MediaQuery.of(context).size.width * 0.75, ), decoration: BoxDecoration( - color: - role == MessageRole.user - ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context).colorScheme.surfaceContainerHighest, + color: role == MessageRole.user + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(16.0), ), child: MarkdownBody( @@ -70,11 +68,10 @@ class ChatBubble extends StatelessWidget { Theme.of(context), ).copyWith( p: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: - role == MessageRole.user + color: role == MessageRole.user ? Theme.of(context).colorScheme.surfaceBright : Theme.of(context).colorScheme.onSurface, - ), + ), ), ), ), diff --git a/lib/dashbot/features/chat/viewmodel/chat_viewmodel.dart b/lib/dashbot/features/chat/viewmodel/chat_viewmodel.dart index a5911c1b..24924a29 100644 --- a/lib/dashbot/features/chat/viewmodel/chat_viewmodel.dart +++ b/lib/dashbot/features/chat/viewmodel/chat_viewmodel.dart @@ -1,11 +1,12 @@ import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:genai/genai.dart' as genai; +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/models/models.dart'; import 'package:nanoid/nanoid.dart'; import '../../../core/constants/dashbot_prompts.dart' as dash; -import '../../../core/model/dashbot_request_context.dart'; -import '../../../core/providers/dashbot_request_provider.dart'; import '../view/widgets/chat_bubble.dart'; import '../models/chat_models.dart'; import '../repository/chat_remote_repository.dart'; @@ -17,11 +18,20 @@ class ChatViewmodel extends StateNotifier { StreamSubscription? _sub; ChatRemoteRepository get _repo => _ref.read(chatRepositoryProvider); - DashbotRequestContext? get _ctx => _ref.read(dashbotRequestContextProvider); + // Currently selected request and AI model are read from app providers + RequestModel? get _currentRequest => _ref.read(selectedRequestModelProvider); + genai.AIRequestModel? get _selectedAIModel { + final json = _ref.read(settingsProvider).defaultAIModel; + if (json == null) return null; + try { + return genai.AIRequestModel.fromJson(json); + } catch (_) { + return null; + } + } List get currentMessages { - final id = _ctx?.requestId; - if (id == null) return const []; + final id = _currentRequest?.id ?? 'global'; return state.chatSessions[id] ?? const []; } @@ -30,8 +40,7 @@ class ChatViewmodel extends StateNotifier { ChatMessageType type = ChatMessageType.general, bool countAsUser = true, }) async { - final ctx = _ctx; - final ai = ctx?.aiRequestModel; + final ai = _selectedAIModel; if (text.trim().isEmpty && countAsUser) return; if (ai == null) { _appendSystem( @@ -41,7 +50,7 @@ class ChatViewmodel extends StateNotifier { return; } - final requestId = ctx?.requestId ?? 'global'; + final requestId = _currentRequest?.id ?? 'global'; if (countAsUser) { _addMessage( @@ -56,7 +65,7 @@ class ChatViewmodel extends StateNotifier { ); } - final systemPrompt = _composeSystemPrompt(ctx, type); + final systemPrompt = _composeSystemPrompt(_currentRequest, type); final enriched = ai.copyWith( systemPrompt: systemPrompt, userPrompt: text, @@ -66,39 +75,36 @@ class ChatViewmodel extends StateNotifier { // start stream _sub?.cancel(); state = state.copyWith(isGenerating: true, currentStreamingResponse: ''); - _sub = _repo - .streamChat(request: enriched) - .listen( - (chunk) { - state = state.copyWith( - currentStreamingResponse: - state.currentStreamingResponse + (chunk), - ); - }, - onError: (e) { - state = state.copyWith(isGenerating: false); - _appendSystem('Error: $e', type); - }, - onDone: () { - if (state.currentStreamingResponse.isNotEmpty) { - _addMessage( - requestId, - ChatMessage( - id: nanoid(), - content: state.currentStreamingResponse, - role: MessageRole.system, - timestamp: DateTime.now(), - messageType: type, - ), - ); - } - state = state.copyWith( - isGenerating: false, - currentStreamingResponse: '', - ); - }, - cancelOnError: true, + _sub = _repo.streamChat(request: enriched).listen( + (chunk) { + state = state.copyWith( + currentStreamingResponse: state.currentStreamingResponse + (chunk), ); + }, + onError: (e) { + state = state.copyWith(isGenerating: false); + _appendSystem('Error: $e', type); + }, + onDone: () { + if (state.currentStreamingResponse.isNotEmpty) { + _addMessage( + requestId, + ChatMessage( + id: nanoid(), + content: state.currentStreamingResponse, + role: MessageRole.system, + timestamp: DateTime.now(), + messageType: type, + ), + ); + } + state = state.copyWith( + isGenerating: false, + currentStreamingResponse: '', + ); + }, + cancelOnError: true, + ); } void cancel() { @@ -118,7 +124,7 @@ class ChatViewmodel extends StateNotifier { } void _appendSystem(String text, ChatMessageType type) { - final id = _ctx?.requestId ?? 'global'; + final id = _currentRequest?.id ?? 'global'; _addMessage( id, ChatMessage( @@ -132,12 +138,12 @@ class ChatViewmodel extends StateNotifier { } String _composeSystemPrompt( - DashbotRequestContext? ctx, + RequestModel? req, ChatMessageType type, ) { final history = _buildHistoryBlock(); - final contextBlock = _buildContextBlock(ctx); - final task = _buildTaskPrompt(ctx, type); + final contextBlock = _buildContextBlock(req); + final task = _buildTaskPrompt(req, type); return [ if (task != null) task, if (contextBlock != null) contextBlock, @@ -146,7 +152,7 @@ class ChatViewmodel extends StateNotifier { } String _buildHistoryBlock({int maxTurns = 8}) { - final id = _ctx?.requestId ?? 'global'; + final id = _currentRequest?.id ?? 'global'; final messages = state.chatSessions[id] ?? const []; if (messages.isEmpty) return ''; final start = messages.length > maxTurns ? messages.length - maxTurns : 0; @@ -162,35 +168,35 @@ class ChatViewmodel extends StateNotifier { return buf.toString(); } - String? _buildContextBlock(DashbotRequestContext? ctx) { - final http = ctx?.httpRequestModel; - if (ctx == null || http == null) return null; + String? _buildContextBlock(RequestModel? req) { + final http = req?.httpRequestModel; + if (req == null || http == null) return null; final headers = http.headersMap.entries .map((e) => '"${e.key}": "${e.value}"') .join(', '); return ''' - Request Name: ${ctx.requestName ?? ''} + Request Name: ${req.name} URL: ${http.url} Method: ${http.method.name.toUpperCase()} - Status: ${ctx.responseStatus ?? ''} + Status: ${req.responseStatus ?? ''} Content-Type: ${http.bodyContentType.name} Headers: { $headers } Body: ${http.body ?? ''} - Response: ${ctx.httpResponseModel?.body ?? ''} + Response: ${req.httpResponseModel?.body ?? ''} '''; } - String? _buildTaskPrompt(DashbotRequestContext? ctx, ChatMessageType type) { - if (ctx == null) return null; - final http = ctx.httpRequestModel; - final resp = ctx.httpResponseModel; + String? _buildTaskPrompt(RequestModel? req, ChatMessageType type) { + if (req == null) return null; + final http = req.httpRequestModel; + final resp = req.httpResponseModel; final prompts = dash.DashbotPrompts(); switch (type) { case ChatMessageType.explainResponse: return prompts.explainApiResponsePrompt( url: http?.url, method: http?.method.name.toUpperCase(), - responseStatus: ctx.responseStatus, + responseStatus: req.responseStatus, bodyContentType: http?.bodyContentType.name, message: resp?.body, headersMap: http?.headersMap, @@ -200,7 +206,7 @@ class ChatViewmodel extends StateNotifier { return prompts.debugApiErrorPrompt( url: http?.url, method: http?.method.name.toUpperCase(), - responseStatus: ctx.responseStatus, + responseStatus: req.responseStatus, bodyContentType: http?.bodyContentType.name, message: resp?.body, headersMap: http?.headersMap, diff --git a/lib/providers/dashbot_context_provider.dart b/lib/providers/dashbot_context_provider.dart deleted file mode 100644 index 124f610f..00000000 --- a/lib/providers/dashbot_context_provider.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:apidash/dashbot/dashbot.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'providers.dart'; - -/// Derives the DashbotRequestContext from the app's current selection. -final appDashbotRequestContextProvider = - Provider((ref) { - final req = ref.watch(selectedRequestModelProvider); - if (req == null) return null; - return DashbotRequestContext( - apiType: req.apiType, - requestId: req.id, - requestName: req.name, - requestDescription: req.description, - aiRequestModel: req.aiRequestModel, - httpRequestModel: req.httpRequestModel, - responseStatus: req.responseStatus, - responseMessage: req.message, - httpResponseModel: req.httpResponseModel, - ); -}); diff --git a/lib/providers/providers.dart b/lib/providers/providers.dart index 0747fc02..1a906ce5 100644 --- a/lib/providers/providers.dart +++ b/lib/providers/providers.dart @@ -4,4 +4,3 @@ export 'environment_providers.dart'; export 'history_providers.dart'; export 'settings_providers.dart'; export 'ui_providers.dart'; -export 'dashbot_context_provider.dart'; diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index dfb155d4..3ba4ee1e 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -130,15 +130,7 @@ class Dashboard extends ConsumerWidget { floatingActionButton: isDashBotEnabled ? FloatingActionButton( backgroundColor: Theme.of(context).colorScheme.primaryContainer, - onPressed: () => showDashbotWindow( - context, - ref, - overrides: [ - dashbotRequestContextProvider.overrideWith( - (ref) => ref.watch(appDashbotRequestContextProvider), - ), - ], - ), + onPressed: () => showDashbotWindow(context, ref), child: Padding( padding: const EdgeInsets.symmetric( vertical: 6.0, diff --git a/pubspec.lock b/pubspec.lock index d341422d..0e10a2d7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -405,13 +405,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" - dashbot: - dependency: "direct main" - description: - path: "packages/dashbot" - relative: true - source: path - version: "0.0.1" data_table_2: dependency: "direct main" description: @@ -720,14 +713,6 @@ packages: description: flutter source: sdk version: "0.0.0" - fpdart: - dependency: transitive - description: - name: fpdart - sha256: "1b84ce64453974159f08046f5d05592020d1fcb2099d7fe6ec58da0e7337af77" - url: "https://pub.dev" - source: hosted - version: "1.1.1" freezed: dependency: "direct dev" description: @@ -1467,14 +1452,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.1" - riverpod_annotation: - dependency: transitive - description: - name: riverpod_annotation - sha256: e14b0bf45b71326654e2705d462f21b958f987087be850afd60578fcd502d1b8 - url: "https://pub.dev" - source: hosted - version: "2.6.1" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 815a2fd9..b7a53d3e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,8 +14,6 @@ dependencies: path: packages/apidash_core apidash_design_system: path: packages/apidash_design_system - dashbot: - path: packages/dashbot carousel_slider: ^5.0.0 code_builder: ^4.10.0 csv: ^6.0.0 @@ -105,3 +103,4 @@ flutter: uses-material-design: true assets: - assets/ + - assets/dashbot/