Files

129 lines
3.6 KiB
Dart

import 'json_util.dart';
/// 会话列表项(不含消息)
class ChatSessionItem {
final int id;
final String title;
final String updateTime;
/// 本会话绑定的人设 id。null = 内置默认人设。
///
/// 会话的人设在**新建时确定、之后不可改** —— 一次对话从头到尾是同一个人。
final int? personaId;
/// 人设显示名。人设被删了会回落成「默认人设」,历史会话不会因此打不开
final String personaName;
/// 人设头像相对路径。null = 用默认头像
final String? personaAvatar;
const ChatSessionItem({
required this.id,
required this.title,
required this.updateTime,
this.personaId,
this.personaName = '默认人设',
this.personaAvatar,
});
factory ChatSessionItem.fromJson(Map<String, dynamic> json) {
return ChatSessionItem(
id: asInt(json['id']),
title: asNonEmpty(json['title'], '新对话'),
updateTime: formatDateTime(json['updateTime']),
personaId: asNullableInt(json['personaId']),
personaName: asNonEmpty(json['personaName'], '默认人设'),
personaAvatar: asNonEmpty(json['personaAvatar'], ''),
);
}
}
/// 会话里的一条消息。工具调用的中间过程后端不入库,所以只有 user / assistant
class ChatMessageItem {
final int id;
final String role;
final String content;
const ChatMessageItem({
required this.id,
required this.role,
required this.content,
});
bool get isUser => role == 'user';
factory ChatMessageItem.fromJson(Map<String, dynamic> json) {
return ChatMessageItem(
id: asInt(json['id']),
role: asString(json['role'], fallback: 'assistant'),
content: asString(json['content']),
);
}
}
/// 页面上的一个气泡。
///
/// 和 [ChatMessageItem] 的区别:这个没有 id、带 streaming 标记,
/// 因为正在生成的那条回答还没落库。
class ChatBubble {
final String role;
final String content;
/// 正在生成中(末尾会显示光标 / 工具提示)
final bool streaming;
const ChatBubble({
required this.role,
required this.content,
this.streaming = false,
});
const ChatBubble.user(String content) : this(role: 'user', content: content);
const ChatBubble.assistant(String content, {bool streaming = false})
: this(role: 'assistant', content: content, streaming: streaming);
bool get isUser => role == 'user';
ChatBubble copyWith({String? content, bool? streaming}) {
return ChatBubble(
role: role,
content: content ?? this.content,
streaming: streaming ?? this.streaming,
);
}
factory ChatBubble.fromItem(ChatMessageItem item) =>
ChatBubble(role: item.role, content: item.content);
}
/// SSE 事件类型
enum AiEventType { delta, tool, done, error }
/// 一次 SSE 事件。后端只会发这四种,见 AiChatService。
class AiEvent {
final AiEventType type;
/// delta:本次新增的文本
final String? content;
/// tool:正在调用的工具名
final String? toolName;
/// done:会话 ID(新会话时后端才刚创建,App 要靠它接着聊)
final int? sessionId;
/// error:错误提示
final String? message;
const AiEvent._(this.type, {this.content, this.toolName, this.sessionId, this.message});
const AiEvent.delta(String content) : this._(AiEventType.delta, content: content);
const AiEvent.tool(String name) : this._(AiEventType.tool, toolName: name);
const AiEvent.done(int? sessionId) : this._(AiEventType.done, sessionId: sessionId);
const AiEvent.error(String message) : this._(AiEventType.error, message: message);
}