Skip to content

Commit 36fb841

Browse files
committed
feat(chat): 增加会话目录支持并优化消息处理
- 在创建会话时添加目录参数支持 - 优化消息模型处理,支持从摘要合成文本内容 - 增加API请求超时时间以避免大消息中断 - 修复项目ID同步问题,确保始终使用最新项目ID - 避免向后端发送空字段以节省带宽 - 更新应用版本号至1.0.1+2
1 parent 9c6fcaf commit 36fb841

6 files changed

Lines changed: 100 additions & 13 deletions

File tree

lib/data/datasources/chat_remote_datasource.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,25 @@ class ChatRemoteDataSourceImpl implements ChatRemoteDataSource {
295295
final response = await dio.get(
296296
'/session/$sessionId/message',
297297
queryParameters: queryParams.isNotEmpty ? queryParams : null,
298+
options: Options(
299+
// 会话历史可能较大,提升接收超时以避免 60 秒中断
300+
receiveTimeout: const Duration(minutes: 3),
301+
),
298302
);
299303

300304
if (response.statusCode == 200) {
301305
final List<dynamic> data = response.data;
302-
return data.map((json) => ChatMessageModel.fromJson(json)).toList();
306+
// Each item follows { info: MessageObject, parts: Part[] }
307+
// Flatten to a single map compatible with ChatMessageModel.fromJson
308+
return data.map((item) {
309+
final map = item as Map<String, dynamic>;
310+
final info = (map['info'] as Map<String, dynamic>?) ?? <String, dynamic>{};
311+
final parts = (map['parts'] as List<dynamic>?) ?? <dynamic>[];
312+
return ChatMessageModel.fromJson({
313+
...info,
314+
'parts': parts,
315+
});
316+
}).toList();
303317
} else {
304318
throw const ServerException('服务器错误');
305319
}
@@ -324,6 +338,10 @@ class ChatRemoteDataSourceImpl implements ChatRemoteDataSource {
324338
final response = await dio.get(
325339
'/session/$sessionId/message/$messageId',
326340
queryParameters: queryParams.isNotEmpty ? queryParams : null,
341+
options: Options(
342+
// 单条消息也可能较慢,统一提升接收超时
343+
receiveTimeout: const Duration(minutes: 3),
344+
),
327345
);
328346

329347
if (response.statusCode == 200) {

lib/data/models/chat_message_model.dart

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,52 @@ class ChatMessageModel {
102102
// Manually handle completedTime
103103
final completedTime = _completedTimeFromJson(json['time']);
104104

105+
// Prepare parts and synthesize text from summary if needed
106+
final List<MessagePartModel> computedParts = List<MessagePartModel>.from(model.parts);
107+
108+
// For UserMessage: server may provide `summary` object without `parts`
109+
// We synthesize a text part so UI can display meaningful content.
110+
final dynamic summary = json['summary'];
111+
if (computedParts.isEmpty && summary is Map<String, dynamic>) {
112+
final title = (summary['title'] as String?)?.trim();
113+
final body = (summary['body'] as String?)?.trim();
114+
final diffs = summary['diffs'] as List<dynamic>?;
115+
116+
final buffer = StringBuffer();
117+
if (title != null && title.isNotEmpty) buffer.writeln(title);
118+
if (body != null && body.isNotEmpty) buffer.writeln(body);
119+
if (diffs != null && diffs.isNotEmpty) {
120+
for (final d in diffs) {
121+
if (d is Map<String, dynamic>) {
122+
final file = (d['file'] as String?) ?? '';
123+
final after = (d['after'] as String?) ?? '';
124+
if (file.isNotEmpty) buffer.writeln('File: $file');
125+
if (after.isNotEmpty) buffer.writeln(after);
126+
}
127+
}
128+
}
129+
130+
final synthesizedText = buffer.toString().trim();
131+
if (synthesizedText.isNotEmpty) {
132+
computedParts.add(
133+
MessagePartModel(
134+
id: 'prt_${DateTime.now().millisecondsSinceEpoch}_summary',
135+
messageId: model.id,
136+
sessionId: model.sessionId,
137+
type: 'text',
138+
text: synthesizedText,
139+
),
140+
);
141+
}
142+
}
143+
105144
return ChatMessageModel(
106145
id: model.id,
107146
sessionId: model.sessionId,
108147
role: model.role,
109148
time: model.time,
110149
completedTime: completedTime,
111-
parts: model.parts,
150+
parts: computedParts,
112151
providerId: model.providerId,
113152
modelId: model.modelId,
114153
cost: model.cost,

lib/data/models/chat_session_model.dart

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,17 @@ class SessionCreateInputModel {
248248
factory SessionCreateInputModel.fromJson(Map<String, dynamic> json) =>
249249
_$SessionCreateInputModelFromJson(json);
250250

251-
Map<String, dynamic> toJson() => _$SessionCreateInputModelToJson(this);
251+
// 手动实现 toJson 以避免向后端发送 null 字段
252+
Map<String, dynamic> toJson() {
253+
final map = <String, dynamic>{};
254+
if (parentId != null) {
255+
map['parentID'] = parentId;
256+
}
257+
if (title != null) {
258+
map['title'] = title;
259+
}
260+
return map;
261+
}
252262

253263
static SessionCreateInputModel fromDomain(SessionCreateInput input) {
254264
return SessionCreateInputModel(
@@ -268,7 +278,14 @@ class SessionUpdateInputModel {
268278
factory SessionUpdateInputModel.fromJson(Map<String, dynamic> json) =>
269279
_$SessionUpdateInputModelFromJson(json);
270280

271-
Map<String, dynamic> toJson() => _$SessionUpdateInputModelToJson(this);
281+
// 同样避免发送空字段
282+
Map<String, dynamic> toJson() {
283+
final map = <String, dynamic>{};
284+
if (title != null) {
285+
map['title'] = title;
286+
}
287+
return map;
288+
}
272289

273290
static SessionUpdateInputModel fromDomain(SessionUpdateInput input) {
274291
return SessionUpdateInputModel(title: input.title);

lib/domain/usecases/create_chat_session.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ class CreateChatSession {
1313
Future<Either<Failure, ChatSession>> call(
1414
CreateChatSessionParams params,
1515
) async {
16-
return repository.createSession(params.projectId, params.input);
16+
return repository.createSession(
17+
params.projectId,
18+
params.input,
19+
directory: params.directory,
20+
);
1721
}
1822
}
1923

@@ -22,8 +26,10 @@ class CreateChatSessionParams {
2226
const CreateChatSessionParams({
2327
required this.projectId,
2428
required this.input,
29+
this.directory,
2530
});
2631

2732
final String projectId;
2833
final SessionCreateInput input;
34+
final String? directory;
2935
}

lib/presentation/providers/chat_provider.dart

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ class ChatProvider extends ChangeNotifier {
232232
/// 创建新会话
233233
Future<void> createNewSession({String? parentId, String? title}) async {
234234
final projectId = projectProvider.currentProjectId;
235+
final directory = projectProvider.currentProject?.path;
235236
_setState(ChatState.loading);
236237

237238
// 生成基于时间的标题
@@ -245,6 +246,7 @@ class ChatProvider extends ChangeNotifier {
245246
parentId: parentId,
246247
title: defaultTitle,
247248
),
249+
directory: directory,
248250
),
249251
);
250252

@@ -300,13 +302,14 @@ class ChatProvider extends ChangeNotifier {
300302

301303
/// 加载消息列表
302304
Future<void> loadMessages(String sessionId) async {
303-
if (_currentProjectId == null) return; // 确保有项目ID
304-
305+
// 同步项目ID(根据 ProjectProvider),新接口对 projectId 非必需
306+
_currentProjectId = projectProvider.currentProjectId;
307+
305308
_setState(ChatState.loading);
306309

307310
final result = await getChatMessages(
308311
GetChatMessagesParams(
309-
projectId: _currentProjectId!,
312+
projectId: projectProvider.currentProjectId,
310313
sessionId: sessionId,
311314
),
312315
);
@@ -323,6 +326,9 @@ class ChatProvider extends ChangeNotifier {
323326

324327
_setState(ChatState.sending);
325328

329+
// 同步项目ID(根据 ProjectProvider)
330+
_currentProjectId = projectProvider.currentProjectId;
331+
326332
// 生成消息 ID
327333
final messageId = 'msg_${DateTime.now().millisecondsSinceEpoch}';
328334

@@ -368,7 +374,7 @@ class ChatProvider extends ChangeNotifier {
368374
_messageSubscription =
369375
sendChatMessage(
370376
SendChatMessageParams(
371-
projectId: _currentProjectId!,
377+
projectId: projectProvider.currentProjectId,
372378
sessionId: _currentSession!.id,
373379
input: input,
374380
),
@@ -447,11 +453,12 @@ class ChatProvider extends ChangeNotifier {
447453

448454
/// 删除会话
449455
Future<void> deleteSession(String sessionId) async {
450-
if (_currentProjectId == null) return; // 确保有项目ID
451-
456+
// 同步项目ID(根据 ProjectProvider)
457+
_currentProjectId = projectProvider.currentProjectId;
458+
452459
final result = await deleteChatSession(
453460
DeleteChatSessionParams(
454-
projectId: _currentProjectId!,
461+
projectId: projectProvider.currentProjectId,
455462
sessionId: sessionId,
456463
),
457464
);

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
1616
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
1717
# In Windows, build-name is used as the major, minor, and patch parts
1818
# of the product and file versions while build-number is used as the build suffix.
19-
version: 1.0.0+1
19+
version: 1.0.1+2
2020

2121
environment:
2222
sdk: ^3.8.1

0 commit comments

Comments
 (0)