Manage prompts as a "messages" object during prompt lifecycle

Before, our prompts were just a string like "Translate this text:
user text" and just before being sent to the backend, it was
converted to an "messages" object compatible with OpenAI API like [{
"role": "user", "content": "Translate this text: user text"}].

Now, we use "messages" object directly when building the prompt
because in the near future we will create more complex prompts with
system content and user content so we will need "messages" object
dirrectly.
This commit is contained in:
Théo Poizat
2026-01-29 15:48:21 +01:00
committed by Dat H. Pham
parent 2ab6e0df02
commit fabf3ec54f
7 changed files with 25 additions and 14 deletions
@@ -1,5 +1,6 @@
import 'package:scribe/scribe/ai/data/model/ai_message.dart';
import 'package:scribe/scribe/ai/domain/model/ai_response.dart'; import 'package:scribe/scribe/ai/domain/model/ai_response.dart';
abstract class AIDataSource { abstract class AIDataSource {
Future<AIResponse> generateMessage(String prompt); Future<AIResponse> generateMessage(List<AIMessage> messages);
} }
@@ -1,5 +1,6 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:scribe/scribe/ai/data/datasource/ai_datasource.dart'; import 'package:scribe/scribe/ai/data/datasource/ai_datasource.dart';
import 'package:scribe/scribe/ai/data/model/ai_message.dart';
import 'package:scribe/scribe/ai/data/network/ai_api.dart'; import 'package:scribe/scribe/ai/data/network/ai_api.dart';
import 'package:scribe/scribe/ai/domain/model/ai_response.dart'; import 'package:scribe/scribe/ai/domain/model/ai_response.dart';
@@ -9,9 +10,9 @@ class AIDataSourceImpl implements AIDataSource {
AIDataSourceImpl(this._aiApi); AIDataSourceImpl(this._aiApi);
@override @override
Future<AIResponse> generateMessage(String prompt) async { Future<AIResponse> generateMessage(List<AIMessage> messages) async {
try { try {
final apiResponse = await _aiApi.generateMessage(prompt); final apiResponse = await _aiApi.generateMessage(messages);
return AIResponse(result: apiResponse.content); return AIResponse(result: apiResponse.content);
} on DioException catch (e) { } on DioException catch (e) {
throw Exception('Failed to generate AI text: ${e.message}'); throw Exception('Failed to generate AI text: ${e.message}');
@@ -5,6 +5,7 @@ part 'ai_message.g.dart';
@JsonSerializable() @JsonSerializable()
class AIMessage { class AIMessage {
static const String aiUserRole = 'user'; static const String aiUserRole = 'user';
static const String aiSystemRole = 'system';
final String role; final String role;
final String content; final String content;
@@ -23,4 +24,9 @@ class AIMessage {
role: aiUserRole, role: aiUserRole,
content: content, content: content,
); );
factory AIMessage.ofSystem(String content) => AIMessage(
role: aiSystemRole,
content: content,
);
} }
@@ -9,8 +9,8 @@ class AIApi {
AIApi(this._dioClient, this.aiEndpoint); AIApi(this._dioClient, this.aiEndpoint);
Future<AIApiResponse> generateMessage(String prompt) async { Future<AIApiResponse> generateMessage(List<AIMessage> messages) async {
final aiRequest = _generateRequest(prompt); final aiRequest = AIAPIRequest(messages: messages);
final response = await _dioClient.post( final response = await _dioClient.post(
aiEndpoint, aiEndpoint,
@@ -20,8 +20,4 @@ class AIApi {
return AIApiResponse.fromJson(response); return AIApiResponse.fromJson(response);
} }
AIAPIRequest _generateRequest(String prompt) {
return AIAPIRequest(messages: [AIMessage.ofUser(prompt)]);
}
} }
@@ -1,4 +1,5 @@
import 'package:scribe/scribe/ai/data/datasource/ai_datasource.dart'; import 'package:scribe/scribe/ai/data/datasource/ai_datasource.dart';
import 'package:scribe/scribe/ai/data/model/ai_message.dart';
import 'package:scribe/scribe/ai/domain/model/ai_response.dart'; import 'package:scribe/scribe/ai/domain/model/ai_response.dart';
import 'package:scribe/scribe/ai/domain/repository/ai_scribe_repository.dart'; import 'package:scribe/scribe/ai/domain/repository/ai_scribe_repository.dart';
@@ -8,7 +9,7 @@ class AIScribeRepositoryImpl implements AIScribeRepository {
AIScribeRepositoryImpl(this._aiDataSource); AIScribeRepositoryImpl(this._aiDataSource);
@override @override
Future<AIResponse> generateMessage(String prompt) { Future<AIResponse> generateMessage(List<AIMessage> messages) {
return _aiDataSource.generateMessage(prompt); return _aiDataSource.generateMessage(messages);
} }
} }
@@ -1,3 +1,4 @@
import 'package:scribe/scribe/ai/data/model/ai_message.dart';
import 'package:scribe/scribe/ai/presentation/model/ai_action.dart'; import 'package:scribe/scribe/ai/presentation/model/ai_action.dart';
import 'package:scribe/scribe/ai/presentation/model/ai_scribe_menu_action.dart'; import 'package:scribe/scribe/ai/presentation/model/ai_scribe_menu_action.dart';
@@ -8,8 +9,8 @@ class AIPrompts {
static const _doNotAddInfoPrompt = static const _doNotAddInfoPrompt =
"Do not add any extra information or interpret anything beyond the explicit task."; "Do not add any extra information or interpret anything beyond the explicit task.";
static String buildPrompt(AIAction action, String? text) { static List<AIMessage> buildPrompt(AIAction action, String? text) {
return switch (action) { final prompt = switch (action) {
PredefinedAction(action: final menuAction) => PredefinedAction(action: final menuAction) =>
text?.trim().isNotEmpty == true text?.trim().isNotEmpty == true
? buildPredefinedPrompt(menuAction, text!) ? buildPredefinedPrompt(menuAction, text!)
@@ -17,6 +18,10 @@ class AIPrompts {
CustomPromptAction(prompt: final customPrompt) => CustomPromptAction(prompt: final customPrompt) =>
buildCustomPrompt(customPrompt, text), buildCustomPrompt(customPrompt, text),
}; };
final message = [AIMessage.ofUser(prompt)];
return message;
} }
static String buildPredefinedPrompt(AIScribeMenuAction action, String text) { static String buildPredefinedPrompt(AIScribeMenuAction action, String text) {
@@ -1,5 +1,6 @@
import 'package:scribe/scribe/ai/data/model/ai_message.dart';
import 'package:scribe/scribe/ai/domain/model/ai_response.dart'; import 'package:scribe/scribe/ai/domain/model/ai_response.dart';
abstract class AIScribeRepository { abstract class AIScribeRepository {
Future<AIResponse> generateMessage(String prompt); Future<AIResponse> generateMessage(List<AIMessage> messages);
} }