You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
6.0 KiB
145 lines
6.0 KiB
import 'package:flutter/material.dart';
|
|
|
|
import 'device_category.dart';
|
|
|
|
/// 功能模块(agent)定义。
|
|
///
|
|
/// AI 页与广场页共用同一份注册表:广场展示全部,AI 页只展示用户启用的那些,
|
|
/// 顺序由 [AgentModuleService] 持久化。新增 agent 见 `lib/modules/module_registry.dart`。
|
|
class AgentModule {
|
|
const AgentModule({
|
|
required this.id,
|
|
required this.title,
|
|
required this.icon,
|
|
required this.gradient,
|
|
this.subtitle,
|
|
this.requiresDevice = false,
|
|
this.comingSoon = false,
|
|
this.bailianAppId,
|
|
});
|
|
|
|
/// 唯一标识,持久化时存的就是它,改名会导致用户已有配置失效
|
|
final String id;
|
|
|
|
/// i18n key,展示时 `.tr`(不是中文文案本身)
|
|
final String title;
|
|
|
|
/// i18n key,展示时 `.tr`
|
|
final String? subtitle;
|
|
final IconData icon;
|
|
|
|
/// 卡片图标底色渐变(两个色值)
|
|
final List<Color> gradient;
|
|
|
|
/// 是否依赖设备连接:未连接时卡片置灰,点击提示连接设备
|
|
final bool requiresDevice;
|
|
|
|
/// 尚未实现的占位模块:可以加到 AI 页排版,但点击只提示"敬请期待"
|
|
final bool comingSoon;
|
|
|
|
/// 这个模块用哪个百炼应用(app_id)。null = 用全局默认(EMAI 那个)。
|
|
///
|
|
/// **设备按键唤醒时用它决定接哪个助手**:`DeviceAiSessionService` 按
|
|
/// 厂商 → 品类 → 默认编排第一个模块 → 这个字段,找到该说话的那个 agent。
|
|
/// 车载香薰唤醒的是「EaiCar 车载助手」而不是 EMAI,就是靠这一条。
|
|
///
|
|
/// ⚠️ 放在 data 层的基类而不是 `AgentModuleDescriptor`(modules 层),是为了让
|
|
/// `DeviceAiSessionService`(data 层)读得到 —— **data 层不反向依赖 modules 层**。
|
|
final String? bailianAppId;
|
|
}
|
|
|
|
/// 按模块 id 取它要接的百炼 app_id。查不到返回 null(用全局默认)。
|
|
String? bailianAppIdForModule(String? moduleId) {
|
|
if (moduleId == null) return null;
|
|
for (final m in AgentModules.all) {
|
|
if (m.id == moduleId) return m.bailianAppId;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// 全部可用模块的注册表
|
|
class AgentModules {
|
|
AgentModules._();
|
|
|
|
// ---------- 已实现(对应现有路由) ----------
|
|
static const String meeting = 'meeting';
|
|
static const String simultaneous = 'simultaneous';
|
|
static const String faceToFace = 'faceToFace';
|
|
static const String call = 'call';
|
|
static const String audioVideo = 'audioVideo';
|
|
static const String callRecording = 'callRecording';
|
|
static const String fieldRecording = 'fieldRecording';
|
|
|
|
/// EMAI 助手:原来 AI 页底部那颗悬浮球,改成广场里的一个正式 agent
|
|
static const String emaiAssistant = 'emaiAssistant';
|
|
|
|
/// EaiCar 车载助手(车载香薰的对话 agent,另一个百炼应用)
|
|
static const String eaicarChat = 'smartcarChat'; // 已铺进装机编排的模块 id,改名后值不动
|
|
static const String textTranslation = 'textTranslation';
|
|
static const String pictureTranslation = 'pictureTranslation';
|
|
|
|
// ---------- 规划中(占位,方便测试增删) ----------
|
|
static const String englishChat = 'englishChat';
|
|
static const String englishStudy = 'englishStudy';
|
|
static const String kidsStory = 'kidsStory';
|
|
|
|
/// 全部模块。由 `ModuleRegistry.install()` 在启动时注入——注册表本体在
|
|
/// `lib/modules/module_registry.dart`(描述符要引用各模块的页面,只能放在
|
|
/// modules 层;data 层不反向依赖它)。
|
|
static List<AgentModule> all = const [];
|
|
|
|
/// AI 页首次进入时的默认模块。
|
|
///
|
|
/// 没连过设备也能用的三个(都只依赖手机本身):语音记录助手、EMAI助手、现场录音。
|
|
/// 依赖设备的功能等连上对应设备后由 [defaultsForCategory] 自动补进来。
|
|
static const List<String> defaultEnabled = [
|
|
// 网格区(左大 + 右上下两小)
|
|
call,
|
|
simultaneous,
|
|
faceToFace,
|
|
// 并排方块
|
|
meeting,
|
|
audioVideo,
|
|
// 往下是整行横条,现场录音是第一行
|
|
fieldRecording,
|
|
callRecording,
|
|
textTranslation,
|
|
pictureTranslation,
|
|
emaiAssistant,
|
|
];
|
|
|
|
/// 各设备品类**第一次连接成功**时要铺到 AI 页的默认功能与顺序。
|
|
///
|
|
/// key 用的是 [DeviceCategories] 的品类 id。只在该品类首次连接时生效一次
|
|
/// (由 AgentModuleService 记录已应用过的品类),之后用户怎么增删排序都不再覆盖。
|
|
static const Map<String, List<String>> defaultsByCategory = {
|
|
// AI手机支架(EaiRec):连上后 AI 页默认就这三个 —— 语音纪要 / 现场录音 /
|
|
// EMAI助手。**不含通话录音**:支架没有接管通话音频的能力,摆在这儿点进去
|
|
// 也用不了。
|
|
DeviceCategories.phoneHolder: [meeting, fieldRecording, emaiAssistant],
|
|
// AI录音卡片是另一条产品线,保持原编排不动。
|
|
DeviceCategories.noteCard: [meeting, fieldRecording, callRecording],
|
|
// AI耳机(恒玄):通话 + 翻译整套都能跑,铺开全量默认编排
|
|
DeviceCategories.earphone: defaultEnabled,
|
|
// AI车载香薰(EaiCar):随时开聊 + 现场录音 + 语音纪要。
|
|
// 这三个都不依赖设备能力(只有通话录音/翻译才声明 requiredCapabilities),
|
|
// 所以连上香薰就能直接用,不会灰。
|
|
// ⚠️ 这是**默认编排不是权限限制**——用户仍可去广场把别的功能加回来,
|
|
// 这是产品有意接受的(落地文档 6.5)。
|
|
// ⚠️ 2026-09-11 从「只有 eaicarChat」改成这三个;已经铺过旧编排的装机
|
|
// 由 AgentModuleService 的一次性补铺处理,见那边的 _migrateCarAromaDefaults。
|
|
DeviceCategories.carAroma: [eaicarChat, fieldRecording, meeting],
|
|
};
|
|
|
|
static List<String> defaultsForCategory(String? categoryId) {
|
|
if (categoryId == null) return const [];
|
|
return defaultsByCategory[categoryId] ?? const [];
|
|
}
|
|
|
|
static AgentModule? byId(String id) {
|
|
for (final m in all) {
|
|
if (m.id == id) return m;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|