mirror of
https://github.com/foss42/apidash.git
synced 2025-12-09 23:02:21 +08:00
feat: dahbot chat page
This commit is contained in:
134
packages/dashbot/lib/features/chat/models/chat_models.dart
Normal file
134
packages/dashbot/lib/features/chat/models/chat_models.dart
Normal file
@@ -0,0 +1,134 @@
|
||||
import '../view/widgets/chat_bubble.dart';
|
||||
|
||||
// Create a Message class that extends the existing ChatMessage for compatibility
|
||||
class Message extends ChatMessage {
|
||||
const Message({
|
||||
required super.id,
|
||||
required super.content,
|
||||
required super.role,
|
||||
required super.timestamp,
|
||||
required super.messageType,
|
||||
});
|
||||
}
|
||||
|
||||
class ChatState {
|
||||
final Map<String, List<ChatMessage>> 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<String, List<ChatMessage>>? 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;
|
||||
|
||||
const ChatMessage({
|
||||
required this.id,
|
||||
required this.content,
|
||||
required this.role,
|
||||
required this.timestamp,
|
||||
this.messageType,
|
||||
});
|
||||
|
||||
ChatMessage copyWith({
|
||||
String? id,
|
||||
String? content,
|
||||
MessageRole? role,
|
||||
DateTime? timestamp,
|
||||
ChatMessageType? messageType,
|
||||
}) {
|
||||
return ChatMessage(
|
||||
id: id ?? this.id,
|
||||
content: content ?? this.content,
|
||||
role: role ?? this.role,
|
||||
timestamp: timestamp ?? this.timestamp,
|
||||
messageType: messageType ?? this.messageType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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, general }
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:genai/genai.dart';
|
||||
|
||||
/// Repository for talking to the GenAI layer.
|
||||
abstract class ChatRemoteRepository {
|
||||
/// Stream a chat completion with the provided AI request.
|
||||
Stream<String> streamChat({required AIRequestModel request});
|
||||
|
||||
/// Execute a non-streaming chat completion.
|
||||
Future<String?> sendChat({required AIRequestModel request});
|
||||
}
|
||||
|
||||
class ChatRemoteRepositoryImpl implements ChatRemoteRepository {
|
||||
ChatRemoteRepositoryImpl();
|
||||
|
||||
@override
|
||||
Stream<String> streamChat({required AIRequestModel request}) async* {
|
||||
final stream = await streamGenAIRequest(request);
|
||||
await for (final chunk in stream) {
|
||||
if (chunk != null && chunk.isNotEmpty) yield chunk;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> sendChat({required AIRequestModel request}) async {
|
||||
final result = await executeGenAIRequest(request);
|
||||
if (result == null || result.isEmpty) return null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Provider for the repository
|
||||
final chatRepositoryProvider = Provider<ChatRemoteRepository>((ref) {
|
||||
return ChatRemoteRepositoryImpl();
|
||||
});
|
||||
@@ -0,0 +1,193 @@
|
||||
import 'dart:developer';
|
||||
import 'package:dashbot/features/chat/models/chat_models.dart';
|
||||
import 'package:dashbot/features/chat/view/widgets/chat_bubble.dart';
|
||||
import 'package:dashbot/features/chat/viewmodel/chat_viewmodel.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:nanoid/nanoid.dart';
|
||||
|
||||
class ChatScreen extends ConsumerStatefulWidget {
|
||||
final String initialPrompt;
|
||||
const ChatScreen({super.key, required this.initialPrompt});
|
||||
|
||||
@override
|
||||
ConsumerState<ChatScreen> createState() => _ChatScreenState();
|
||||
}
|
||||
|
||||
class _ChatScreenState extends ConsumerState<ChatScreen> {
|
||||
final TextEditingController _textController = TextEditingController();
|
||||
final List<ChatMessage> _messages = [];
|
||||
bool _isGenerating = false;
|
||||
String _currentStreamingResponse = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
_sendMessage(promptOverride: widget.initialPrompt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _sendMessage({String? promptOverride}) async {
|
||||
final messageContent = promptOverride ?? _textController.text;
|
||||
|
||||
if (messageContent.trim().isEmpty) return;
|
||||
|
||||
final userChatMessage = ChatMessage(
|
||||
id: nanoid(),
|
||||
content: messageContent,
|
||||
role: MessageRole.user,
|
||||
timestamp: DateTime.now(),
|
||||
messageType: ChatMessageType.general,
|
||||
);
|
||||
|
||||
if (promptOverride == null) {
|
||||
_textController.clear();
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_messages.add(userChatMessage);
|
||||
_isGenerating = true;
|
||||
_currentStreamingResponse = '';
|
||||
});
|
||||
|
||||
log("Sending message: $messageContent");
|
||||
|
||||
final stream = ref
|
||||
.read(chatViewmodelProvider.notifier)
|
||||
.sendMessage(messageContent, ChatMessageType.general);
|
||||
|
||||
try {
|
||||
await for (final result in stream) {
|
||||
if (!mounted) return;
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
log("Error: ${failure.message}");
|
||||
if (!mounted) return;
|
||||
final errorChatMessage = ChatMessage(
|
||||
id: nanoid(),
|
||||
content: "Error: ${failure.message}",
|
||||
role: MessageRole.system,
|
||||
timestamp: DateTime.now(),
|
||||
messageType: ChatMessageType.general,
|
||||
);
|
||||
setState(() {
|
||||
_messages.add(errorChatMessage);
|
||||
_isGenerating = false;
|
||||
_currentStreamingResponse = '';
|
||||
});
|
||||
},
|
||||
(response) {
|
||||
setState(() {
|
||||
_currentStreamingResponse += response.content;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (_currentStreamingResponse.isNotEmpty) {
|
||||
final assistantChatMessage = ChatMessage(
|
||||
id: nanoid(),
|
||||
content: _currentStreamingResponse,
|
||||
role: MessageRole.system,
|
||||
timestamp: DateTime.now(),
|
||||
messageType: ChatMessageType.general,
|
||||
);
|
||||
_messages.add(assistantChatMessage);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isGenerating = false;
|
||||
});
|
||||
} catch (e) {
|
||||
log("Error receiving stream: $e");
|
||||
if (!mounted) return;
|
||||
final errorChatMessage = ChatMessage(
|
||||
id: nanoid(),
|
||||
content: "Error: $e",
|
||||
role: MessageRole.system,
|
||||
timestamp: DateTime.now(),
|
||||
messageType: ChatMessageType.general,
|
||||
);
|
||||
setState(() {
|
||||
_messages.add(errorChatMessage);
|
||||
_isGenerating = false;
|
||||
_currentStreamingResponse = '';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child:
|
||||
_messages.isEmpty && !_isGenerating
|
||||
? const Center(child: Text("Ask me anything!"))
|
||||
: ListView.builder(
|
||||
itemCount: _messages.length + (_isGenerating ? 1 : 0),
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
reverse: false,
|
||||
itemBuilder: (context, index) {
|
||||
if (_isGenerating && index == _messages.length) {
|
||||
return ChatBubble(
|
||||
message: _currentStreamingResponse,
|
||||
role: MessageRole.system,
|
||||
);
|
||||
}
|
||||
final message = _messages[index];
|
||||
return ChatBubble(
|
||||
message: message.content,
|
||||
role: message.role,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
height: 5,
|
||||
thickness: 6,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _textController,
|
||||
decoration: InputDecoration(
|
||||
hintText:
|
||||
_isGenerating ? 'Generating...' : 'Ask anything',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
enabled: !_isGenerating,
|
||||
onSubmitted: (_) => _isGenerating ? null : _sendMessage(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.send_rounded),
|
||||
onPressed: _isGenerating ? null : _sendMessage,
|
||||
tooltip: 'Send message',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import 'package:apidash_design_system/tokens/tokens.dart';
|
||||
import 'package:dashbot/core/utils/dashbot_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
|
||||
class ChatBubble extends StatelessWidget {
|
||||
final String message;
|
||||
final MessageRole role;
|
||||
final String? promptOverride;
|
||||
|
||||
const ChatBubble({
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.role,
|
||||
this.promptOverride,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (promptOverride != null &&
|
||||
role == MessageRole.user &&
|
||||
message == promptOverride) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
if (message.isEmpty) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
children: [
|
||||
kVSpacer8,
|
||||
DashbotIcons.getDashbotIcon1(width: 42),
|
||||
kVSpacer8,
|
||||
CircularProgressIndicator.adaptive(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return Align(
|
||||
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),
|
||||
kVSpacer8,
|
||||
],
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 5.0),
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.75,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
role == MessageRole.user
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
),
|
||||
child: MarkdownBody(
|
||||
data: message.isEmpty ? " " : message,
|
||||
selectable: true,
|
||||
styleSheet: MarkdownStyleSheet.fromTheme(
|
||||
Theme.of(context),
|
||||
).copyWith(
|
||||
p: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color:
|
||||
role == MessageRole.user
|
||||
? Theme.of(context).colorScheme.surfaceBright
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (role == MessageRole.system) ...[
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: message));
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.copy_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum MessageRole { user, system }
|
||||
226
packages/dashbot/lib/features/chat/viewmodel/chat_viewmodel.dart
Normal file
226
packages/dashbot/lib/features/chat/viewmodel/chat_viewmodel.dart
Normal file
@@ -0,0 +1,226 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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';
|
||||
|
||||
class ChatViewmodel extends StateNotifier<ChatState> {
|
||||
ChatViewmodel(this._ref) : super(const ChatState());
|
||||
|
||||
final Ref _ref;
|
||||
StreamSubscription<String>? _sub;
|
||||
|
||||
ChatRemoteRepository get _repo => _ref.read(chatRepositoryProvider);
|
||||
DashbotRequestContext? get _ctx => _ref.read(dashbotRequestContextProvider);
|
||||
|
||||
List<ChatMessage> get currentMessages {
|
||||
final id = _ctx?.requestId;
|
||||
if (id == null) return const [];
|
||||
return state.chatSessions[id] ?? const [];
|
||||
}
|
||||
|
||||
Future<void> sendMessage({
|
||||
required String text,
|
||||
ChatMessageType type = ChatMessageType.general,
|
||||
bool countAsUser = true,
|
||||
}) async {
|
||||
final ctx = _ctx;
|
||||
final ai = ctx?.aiRequestModel;
|
||||
if (text.trim().isEmpty && countAsUser) return;
|
||||
if (ai == null) {
|
||||
_appendSystem(
|
||||
'AI model is not configured. Please set one in AI Request tab.',
|
||||
type,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final requestId = ctx?.requestId ?? 'global';
|
||||
|
||||
if (countAsUser) {
|
||||
_addMessage(
|
||||
requestId,
|
||||
ChatMessage(
|
||||
id: nanoid(),
|
||||
content: text,
|
||||
role: MessageRole.user,
|
||||
timestamp: DateTime.now(),
|
||||
messageType: type,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final systemPrompt = _composeSystemPrompt(ctx, type);
|
||||
final enriched = ai.copyWith(
|
||||
systemPrompt: systemPrompt,
|
||||
userPrompt: text,
|
||||
stream: true,
|
||||
);
|
||||
|
||||
// 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,
|
||||
);
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
_sub?.cancel();
|
||||
state = state.copyWith(isGenerating: false);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
void _addMessage(String requestId, ChatMessage m) {
|
||||
final msgs = state.chatSessions[requestId] ?? const [];
|
||||
state = state.copyWith(
|
||||
chatSessions: {
|
||||
...state.chatSessions,
|
||||
requestId: [...msgs, m],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _appendSystem(String text, ChatMessageType type) {
|
||||
final id = _ctx?.requestId ?? 'global';
|
||||
_addMessage(
|
||||
id,
|
||||
ChatMessage(
|
||||
id: nanoid(),
|
||||
content: text,
|
||||
role: MessageRole.system,
|
||||
timestamp: DateTime.now(),
|
||||
messageType: type,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _composeSystemPrompt(
|
||||
DashbotRequestContext? ctx,
|
||||
ChatMessageType type,
|
||||
) {
|
||||
final history = _buildHistoryBlock();
|
||||
final contextBlock = _buildContextBlock(ctx);
|
||||
final task = _buildTaskPrompt(ctx, type);
|
||||
return [
|
||||
if (task != null) task,
|
||||
if (contextBlock != null) contextBlock,
|
||||
if (history.isNotEmpty) history,
|
||||
].join('\n\n');
|
||||
}
|
||||
|
||||
String _buildHistoryBlock({int maxTurns = 8}) {
|
||||
final id = _ctx?.requestId ?? 'global';
|
||||
final messages = state.chatSessions[id] ?? const [];
|
||||
if (messages.isEmpty) return '';
|
||||
final start = messages.length > maxTurns ? messages.length - maxTurns : 0;
|
||||
final recent = messages.sublist(start);
|
||||
final buf = StringBuffer('''<conversation_context>
|
||||
Only use the following short chat history to maintain continuity. Do not repeat it back.
|
||||
''');
|
||||
for (final m in recent) {
|
||||
final role = m.role == MessageRole.user ? 'user' : 'assistant';
|
||||
buf.writeln('- $role: ${m.content}');
|
||||
}
|
||||
buf.writeln('</conversation_context>');
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
String? _buildContextBlock(DashbotRequestContext? ctx) {
|
||||
final http = ctx?.httpRequestModel;
|
||||
if (ctx == null || http == null) return null;
|
||||
final headers = http.headersMap.entries
|
||||
.map((e) => '"${e.key}": "${e.value}"')
|
||||
.join(', ');
|
||||
return '''<request_context>
|
||||
Request Name: ${ctx.requestName ?? ''}
|
||||
URL: ${http.url}
|
||||
Method: ${http.method.name.toUpperCase()}
|
||||
Status: ${ctx.responseStatus ?? ''}
|
||||
Content-Type: ${http.bodyContentType.name}
|
||||
Headers: { $headers }
|
||||
Body: ${http.body ?? ''}
|
||||
Response: ${ctx.httpResponseModel?.body ?? ''}
|
||||
</request_context>''';
|
||||
}
|
||||
|
||||
String? _buildTaskPrompt(DashbotRequestContext? ctx, ChatMessageType type) {
|
||||
if (ctx == null) return null;
|
||||
final http = ctx.httpRequestModel;
|
||||
final resp = ctx.httpResponseModel;
|
||||
final prompts = dash.DashbotPrompts();
|
||||
switch (type) {
|
||||
case ChatMessageType.explainResponse:
|
||||
return prompts.explainApiResponsePrompt(
|
||||
url: http?.url,
|
||||
method: http?.method.name.toUpperCase(),
|
||||
responseStatus: ctx.responseStatus,
|
||||
bodyContentType: http?.bodyContentType.name,
|
||||
message: resp?.body,
|
||||
headersMap: http?.headersMap,
|
||||
body: http?.body,
|
||||
);
|
||||
case ChatMessageType.debugError:
|
||||
return prompts.debugApiErrorPrompt(
|
||||
url: http?.url,
|
||||
method: http?.method.name.toUpperCase(),
|
||||
responseStatus: ctx.responseStatus,
|
||||
bodyContentType: http?.bodyContentType.name,
|
||||
message: resp?.body,
|
||||
headersMap: http?.headersMap,
|
||||
body: http?.body,
|
||||
);
|
||||
case ChatMessageType.generateTest:
|
||||
return prompts.generateTestCasesPrompt(
|
||||
url: http?.url,
|
||||
method: http?.method.name.toUpperCase(),
|
||||
headersMap: http?.headersMap,
|
||||
body: http?.body,
|
||||
);
|
||||
case ChatMessageType.general:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final chatViewmodelProvider = StateNotifierProvider<ChatViewmodel, ChatState>((
|
||||
ref,
|
||||
) {
|
||||
return ChatViewmodel(ref);
|
||||
});
|
||||
Reference in New Issue
Block a user