fix: adhere to system prompts
This commit is contained in:
@@ -483,6 +483,18 @@ class ApiService {
|
||||
'DEBUG: Parsed conversation $id: pinned=$pinned, archived=$archived',
|
||||
);
|
||||
|
||||
String? systemPrompt;
|
||||
final chatObject = chatData['chat'] as Map<String, dynamic>?;
|
||||
if (chatObject != null) {
|
||||
final systemValue = chatObject['system'];
|
||||
if (systemValue is String && systemValue.trim().isNotEmpty) {
|
||||
systemPrompt = systemValue;
|
||||
}
|
||||
} else if (chatData['system'] is String) {
|
||||
final systemValue = (chatData['system'] as String).trim();
|
||||
if (systemValue.isNotEmpty) systemPrompt = systemValue;
|
||||
}
|
||||
|
||||
// For the list endpoint, we don't get the full chat messages
|
||||
// We'll need to fetch individual chats later if needed
|
||||
return Conversation(
|
||||
@@ -490,6 +502,7 @@ class ApiService {
|
||||
title: title,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
systemPrompt: systemPrompt,
|
||||
pinned: pinned,
|
||||
archived: archived,
|
||||
shareId: shareId,
|
||||
@@ -532,6 +545,16 @@ class ApiService {
|
||||
|
||||
// Parse messages from the 'chat' object or top-level messages
|
||||
final chatObject = chatData['chat'] as Map<String, dynamic>?;
|
||||
String? systemPrompt;
|
||||
if (chatObject != null) {
|
||||
final systemValue = chatObject['system'];
|
||||
if (systemValue is String && systemValue.trim().isNotEmpty) {
|
||||
systemPrompt = systemValue;
|
||||
}
|
||||
} else if (chatData['system'] is String) {
|
||||
final systemValue = (chatData['system'] as String).trim();
|
||||
if (systemValue.isNotEmpty) systemPrompt = systemValue;
|
||||
}
|
||||
final messages = <ChatMessage>[];
|
||||
|
||||
// Extract model from chat.models array
|
||||
@@ -663,6 +686,7 @@ class ApiService {
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
model: model,
|
||||
systemPrompt: systemPrompt,
|
||||
pinned: pinned,
|
||||
archived: archived,
|
||||
shareId: shareId,
|
||||
@@ -1031,6 +1055,8 @@ class ApiService {
|
||||
'id': '',
|
||||
'title': title,
|
||||
'models': model != null ? [model] : [],
|
||||
if (systemPrompt != null && systemPrompt.trim().isNotEmpty)
|
||||
'system': systemPrompt,
|
||||
'params': {},
|
||||
'history': {
|
||||
'messages': messagesMap,
|
||||
@@ -1155,6 +1181,8 @@ class ApiService {
|
||||
'chat': {
|
||||
if (title != null) 'title': title, // Include the title if provided
|
||||
'models': model != null ? [model] : [],
|
||||
if (systemPrompt != null && systemPrompt.trim().isNotEmpty)
|
||||
'system': systemPrompt,
|
||||
'messages': messagesArray,
|
||||
'history': {
|
||||
'messages': messagesMap,
|
||||
|
||||
@@ -482,6 +482,7 @@ Future<String> _preseedAssistantAndPersist(
|
||||
dynamic ref, {
|
||||
String? existingAssistantId,
|
||||
required String modelId,
|
||||
String? systemPrompt,
|
||||
}) async {
|
||||
final api = ref.read(apiServiceProvider);
|
||||
final activeConv = ref.read(activeConversationProvider);
|
||||
@@ -525,11 +526,16 @@ Future<String> _preseedAssistantAndPersist(
|
||||
// Persist the skeleton to the server so the web client sees a correct chain
|
||||
try {
|
||||
if (api != null && activeConv != null) {
|
||||
final resolvedSystemPrompt = (systemPrompt != null &&
|
||||
systemPrompt.trim().isNotEmpty)
|
||||
? systemPrompt.trim()
|
||||
: activeConv.systemPrompt;
|
||||
final current = ref.read(chatMessagesProvider);
|
||||
await api.updateConversationWithMessages(
|
||||
activeConv.id,
|
||||
current,
|
||||
model: modelId,
|
||||
systemPrompt: resolvedSystemPrompt,
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
@@ -711,7 +717,7 @@ Future<void> regenerateMessage(
|
||||
throw Exception('No API service or model selected');
|
||||
}
|
||||
|
||||
final activeConversation = ref.read(activeConversationProvider);
|
||||
var activeConversation = ref.read(activeConversationProvider);
|
||||
if (activeConversation == null) {
|
||||
throw Exception('No active conversation');
|
||||
}
|
||||
@@ -753,6 +759,28 @@ Future<void> regenerateMessage(
|
||||
|
||||
// For real API, proceed with regeneration using existing conversation messages
|
||||
try {
|
||||
Map<String, dynamic>? userSettingsData;
|
||||
String? userSystemPrompt;
|
||||
try {
|
||||
userSettingsData = await api!.getUserSettings();
|
||||
final systemValue = userSettingsData?['system'];
|
||||
if (systemValue is String) {
|
||||
final trimmed = systemValue.trim();
|
||||
if (trimmed.isNotEmpty) {
|
||||
userSystemPrompt = trimmed;
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
if ((activeConversation.systemPrompt == null ||
|
||||
activeConversation.systemPrompt!.trim().isEmpty) &&
|
||||
(userSystemPrompt?.isNotEmpty ?? false)) {
|
||||
final updated =
|
||||
activeConversation.copyWith(systemPrompt: userSystemPrompt);
|
||||
ref.read(activeConversationProvider.notifier).state = updated;
|
||||
activeConversation = updated;
|
||||
}
|
||||
|
||||
// Include selected tool ids so provider-native tool calling is triggered
|
||||
final selectedToolIds = ref.read(selectedToolIdsProvider);
|
||||
// Get conversation history for context (excluding the removed assistant message)
|
||||
@@ -787,10 +815,28 @@ Future<void> regenerateMessage(
|
||||
}
|
||||
}
|
||||
|
||||
final conversationSystemPrompt = activeConversation.systemPrompt?.trim();
|
||||
final effectiveSystemPrompt = (conversationSystemPrompt != null &&
|
||||
conversationSystemPrompt.isNotEmpty)
|
||||
? conversationSystemPrompt
|
||||
: userSystemPrompt;
|
||||
if (effectiveSystemPrompt != null && effectiveSystemPrompt.isNotEmpty) {
|
||||
final hasSystemMessage = conversationMessages.any(
|
||||
(m) => (m['role']?.toString().toLowerCase() ?? '') == 'system',
|
||||
);
|
||||
if (!hasSystemMessage) {
|
||||
conversationMessages.insert(
|
||||
0,
|
||||
{'role': 'system', 'content': effectiveSystemPrompt},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-seed assistant skeleton and persist chain
|
||||
final String assistantMessageId = await _preseedAssistantAndPersist(
|
||||
ref,
|
||||
modelId: selectedModel.id,
|
||||
systemPrompt: effectiveSystemPrompt,
|
||||
);
|
||||
|
||||
// Feature toggles
|
||||
@@ -912,14 +958,13 @@ Future<void> regenerateMessage(
|
||||
|
||||
// Resolve tool servers from user settings (if any)
|
||||
List<Map<String, dynamic>>? toolServers;
|
||||
try {
|
||||
final userSettings = await api!.getUserSettings();
|
||||
final ui = userSettings['ui'] as Map<String, dynamic>?;
|
||||
final rawServers = ui != null ? (ui['toolServers'] as List?) : null;
|
||||
if (rawServers != null && rawServers.isNotEmpty) {
|
||||
final uiSettings = userSettingsData?['ui'] as Map<String, dynamic>?;
|
||||
final rawServers = uiSettings != null ? (uiSettings['toolServers'] as List?) : null;
|
||||
if (rawServers != null && rawServers.isNotEmpty) {
|
||||
try {
|
||||
toolServers = await _resolveToolServers(rawServers, api);
|
||||
}
|
||||
} catch (_) {}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Background tasks parity with Web client (safe defaults)
|
||||
bool shouldGenerateTitle = false;
|
||||
@@ -1056,6 +1101,21 @@ Future<void> _sendMessageInternal(
|
||||
throw Exception('No API service or model selected');
|
||||
}
|
||||
|
||||
Map<String, dynamic>? userSettingsData;
|
||||
String? userSystemPrompt;
|
||||
if (!reviewerMode && api != null) {
|
||||
try {
|
||||
userSettingsData = await api.getUserSettings();
|
||||
final systemValue = userSettingsData?['system'];
|
||||
if (systemValue is String) {
|
||||
final trimmed = systemValue.trim();
|
||||
if (trimmed.isNotEmpty) {
|
||||
userSystemPrompt = trimmed;
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Check if we need to create a new conversation first
|
||||
var activeConversation = ref.read(activeConversationProvider);
|
||||
|
||||
@@ -1077,6 +1137,7 @@ Future<void> _sendMessageInternal(
|
||||
title: 'New Chat',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
systemPrompt: userSystemPrompt,
|
||||
messages: [userMessage], // Include the user message
|
||||
);
|
||||
|
||||
@@ -1091,9 +1152,11 @@ Future<void> _sendMessageInternal(
|
||||
title: 'New Chat',
|
||||
messages: [userMessage], // Include the first message in creation
|
||||
model: selectedModel.id,
|
||||
systemPrompt: userSystemPrompt,
|
||||
);
|
||||
final updatedConversation = localConversation.copyWith(
|
||||
id: serverConversation.id,
|
||||
systemPrompt: serverConversation.systemPrompt ?? userSystemPrompt,
|
||||
messages: serverConversation.messages.isNotEmpty
|
||||
? serverConversation.messages
|
||||
: [userMessage],
|
||||
@@ -1131,6 +1194,15 @@ Future<void> _sendMessageInternal(
|
||||
ref.read(chatMessagesProvider.notifier).addMessage(userMessage);
|
||||
}
|
||||
|
||||
if (activeConversation != null &&
|
||||
(activeConversation.systemPrompt == null ||
|
||||
activeConversation.systemPrompt!.trim().isEmpty) &&
|
||||
(userSystemPrompt?.isNotEmpty ?? false)) {
|
||||
final updated = activeConversation.copyWith(systemPrompt: userSystemPrompt);
|
||||
ref.read(activeConversationProvider.notifier).state = updated;
|
||||
activeConversation = updated;
|
||||
}
|
||||
|
||||
// We'll add the assistant message placeholder after we get the message ID from the API (or immediately in reviewer mode)
|
||||
|
||||
// Immediately trigger title generation after user message is sent (first turn only)
|
||||
@@ -1234,6 +1306,23 @@ Future<void> _sendMessageInternal(
|
||||
}
|
||||
}
|
||||
|
||||
final conversationSystemPrompt = activeConversation?.systemPrompt?.trim();
|
||||
final effectiveSystemPrompt = (conversationSystemPrompt != null &&
|
||||
conversationSystemPrompt.isNotEmpty)
|
||||
? conversationSystemPrompt
|
||||
: userSystemPrompt;
|
||||
if (effectiveSystemPrompt != null && effectiveSystemPrompt.isNotEmpty) {
|
||||
final hasSystemMessage = conversationMessages.any(
|
||||
(m) => (m['role']?.toString().toLowerCase() ?? '') == 'system',
|
||||
);
|
||||
if (!hasSystemMessage) {
|
||||
conversationMessages.insert(
|
||||
0,
|
||||
{'role': 'system', 'content': effectiveSystemPrompt},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check feature toggles for API (gated by server availability)
|
||||
final webSearchEnabled =
|
||||
ref.read(webSearchEnabledProvider) &&
|
||||
@@ -1270,6 +1359,7 @@ Future<void> _sendMessageInternal(
|
||||
activeConvForSeed.id,
|
||||
msgsForSeed,
|
||||
model: selectedModel.id,
|
||||
systemPrompt: effectiveSystemPrompt,
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
@@ -1378,14 +1468,13 @@ Future<void> _sendMessageInternal(
|
||||
|
||||
// Resolve tool servers from user settings (if any)
|
||||
List<Map<String, dynamic>>? toolServers;
|
||||
try {
|
||||
final userSettings = await api.getUserSettings();
|
||||
final ui = userSettings['ui'] as Map<String, dynamic>?;
|
||||
final rawServers = ui != null ? (ui['toolServers'] as List?) : null;
|
||||
if (rawServers != null && rawServers.isNotEmpty) {
|
||||
final uiSettings = userSettingsData?['ui'] as Map<String, dynamic>?;
|
||||
final rawServers = uiSettings != null ? (uiSettings['toolServers'] as List?) : null;
|
||||
if (rawServers != null && rawServers.isNotEmpty) {
|
||||
try {
|
||||
toolServers = await _resolveToolServers(rawServers, api);
|
||||
}
|
||||
} catch (_) {}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Background tasks parity with Web client (safe defaults)
|
||||
// Enable title/tags generation on the very first user turn of a new chat.
|
||||
|
||||
Reference in New Issue
Block a user