refactor: remove unused Message class; update AI model handling in ChatViewmodel

This commit is contained in:
Udhay-Adithya
2025-09-02 13:14:54 +05:30
parent aef4ee5039
commit c8e7c6b4e6
2 changed files with 50 additions and 20 deletions

View File

@@ -1,16 +1,5 @@
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;
@@ -108,12 +97,12 @@ class NetworkFailure extends ChatFailure {
class AIModelNotConfiguredFailure extends ChatFailure {
const AIModelNotConfiguredFailure()
: super("Please configure an AI model in the AI Request tab");
: 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");
: super("API key missing for $provider");
}
class NoRequestSelectedFailure extends ChatFailure {
@@ -126,7 +115,7 @@ class InvalidRequestContextFailure extends ChatFailure {
class RateLimitFailure extends ChatFailure {
const RateLimitFailure()
: super("Rate limit exceeded. Please try again later.");
: super("Rate limit exceeded. Please try again later.");
}
class StreamingFailure extends ChatFailure {

View File

@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:apidash_core/apidash_core.dart';
import 'package:flutter/foundation.dart';
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';
@@ -20,11 +20,11 @@ class ChatViewmodel extends StateNotifier<ChatState> {
ChatRemoteRepository get _repo => _ref.read(chatRepositoryProvider);
// Currently selected request and AI model are read from app providers
RequestModel? get _currentRequest => _ref.read(selectedRequestModelProvider);
genai.AIRequestModel? get _selectedAIModel {
AIRequestModel? get _selectedAIModel {
final json = _ref.read(settingsProvider).defaultAIModel;
if (json == null) return null;
try {
return genai.AIRequestModel.fromJson(json);
return AIRequestModel.fromJson(json);
} catch (_) {
return null;
}
@@ -40,9 +40,12 @@ class ChatViewmodel extends StateNotifier<ChatState> {
ChatMessageType type = ChatMessageType.general,
bool countAsUser = true,
}) async {
debugPrint(
'[Chat] sendMessage start: type=$type, countAsUser=$countAsUser');
final ai = _selectedAIModel;
if (text.trim().isEmpty && countAsUser) return;
if (ai == null) {
debugPrint('[Chat] No AI model configured');
_appendSystem(
'AI model is not configured. Please set one in AI Request tab.',
type,
@@ -51,6 +54,7 @@ class ChatViewmodel extends StateNotifier<ChatState> {
}
final requestId = _currentRequest?.id ?? 'global';
debugPrint('[Chat] using requestId=$requestId');
if (countAsUser) {
_addMessage(
@@ -66,26 +70,38 @@ class ChatViewmodel extends StateNotifier<ChatState> {
}
final systemPrompt = _composeSystemPrompt(_currentRequest, type);
final userPrompt = (text.trim().isEmpty && !countAsUser)
? 'Please complete the task based on the provided context.'
: text;
final enriched = ai.copyWith(
systemPrompt: systemPrompt,
userPrompt: text,
userPrompt: userPrompt,
stream: true,
);
debugPrint(
'[Chat] prompts prepared: system=${systemPrompt.length} chars, user=${userPrompt.length} chars');
// start stream
_sub?.cancel();
state = state.copyWith(isGenerating: true, currentStreamingResponse: '');
bool receivedAnyChunk = false;
_sub = _repo.streamChat(request: enriched).listen(
(chunk) {
receivedAnyChunk = true;
if (chunk.isEmpty) return;
debugPrint('[Chat] chunk(${chunk.length})');
state = state.copyWith(
currentStreamingResponse: state.currentStreamingResponse + (chunk),
);
},
onError: (e) {
debugPrint('[Chat] stream error: $e');
state = state.copyWith(isGenerating: false);
_appendSystem('Error: $e', type);
},
onDone: () {
onDone: () async {
debugPrint(
'[Chat] stream done. total=${state.currentStreamingResponse.length}, anyChunk=$receivedAnyChunk');
if (state.currentStreamingResponse.isNotEmpty) {
_addMessage(
requestId,
@@ -97,6 +113,31 @@ class ChatViewmodel extends StateNotifier<ChatState> {
messageType: type,
),
);
} else if (!receivedAnyChunk) {
// Fallback to non-streaming request
debugPrint(
'[Chat] no streamed content; attempting non-streaming fallback');
try {
final fallback =
await _repo.sendChat(request: enriched.copyWith(stream: false));
if (fallback != null && fallback.isNotEmpty) {
_addMessage(
requestId,
ChatMessage(
id: nanoid(),
content: fallback,
role: MessageRole.system,
timestamp: DateTime.now(),
messageType: type,
),
);
} else {
_appendSystem('No response received from the AI.', type);
}
} catch (err) {
debugPrint('[Chat] fallback error: $err');
_appendSystem('Error: $err', type);
}
}
state = state.copyWith(
isGenerating: false,