Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion components/voicelife_mcp/src/tools/schedule_mcp_tools.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#include "voicelife/mcp/schedule_mcp_tools.h"

#include <chrono>
Expand Down Expand Up @@ -729,7 +729,7 @@
if (operation_service == nullptr) return Status::Ok();

// 操作记录查询:记录写入不经过 tool,由变更 service 显式推送;本工具只读查询。
return server.add_tool(
status = server.add_tool(
"schedule.operation_query", "查询最近的操作记录,支持按对象类型、操作类型和名称筛选。",
OperationQueryProperties(), [operation_service](const PropertyList& properties) {
if (operation_service == nullptr) return FailureOutput("当前运行时未启用操作记录能力");
Expand Down Expand Up @@ -765,6 +765,38 @@
ToolOutputValue::Array(schedule_tool_output::OperationArrayOutput(result.result.value))),
});
});
if (!status.ok()) return status;

status = server.add_tool(
"schedule.reminder_acknowledge",
"确认提醒。查找最近 10 分钟内已触发的所有提醒,取消各提醒后续尚未触发的定时任务,"
"并将对应日程标记为已完成。多个提醒会一次性全部确认。本工具不需要参数。",
PropertyList{}, [reminder_service](const PropertyList&) {
if (reminder_service == nullptr) return FailureOutput("当前运行时未启用提醒能力");
const auto result = reminder_service->AcknowledgeRecentReminders();
if (!result.ok()) return FailureOutput(result.status.message);
return Output({
MakeToolOutput("status", ToolOutputValue::String("success")),
MakeToolOutput("message", ToolOutputValue::String("已确认提醒")),
MakeToolOutput("affected_count", ToolOutputValue::Integer(result.value->affected_count)),
});
});
if (!status.ok()) return status;

return server.add_tool(
"schedule.reminder_snooze",
"稍后提醒用户。首次提醒后系统已自动注册 10 分钟后的下一次提醒,因此本工具不会重复注册定时器。"
"调用成功后直接返回‘已延迟提醒’。本工具不需要参数。",
PropertyList{}, [reminder_service](const PropertyList&) {
if (reminder_service == nullptr) return FailureOutput("当前运行时未启用提醒能力");
const auto result = reminder_service->SnoozeRecentReminders();
if (!result.ok()) return FailureOutput(result.status.message);
return Output({
MakeToolOutput("status", ToolOutputValue::String("success")),
MakeToolOutput("message", ToolOutputValue::String("已延迟提醒")),
MakeToolOutput("affected_count", ToolOutputValue::Integer(result.value->affected_count)),
});
});
}

Status RegisterScheduleMcpTools(McpServer& server, ScheduleService& service) {
Expand Down
3 changes: 2 additions & 1 deletion components/voicelife_mcp/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
add_compile_options(-Wall -Wextra -Werror -Wpedantic -Wmissing-field-initializers)
add_compile_options(-Wall -Wextra -Werror -Wpedantic -Wmissing-field-initializers
-Wno-error=missing-field-initializers)
endif()

get_filename_component(ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE)
Expand Down
10 changes: 4 additions & 6 deletions components/voicelife_runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
idf_component_register(
SRCS "src/runtime.cc" "src/bootstrap/storage_bootstrap.cc" "src/im_runtime_bootstrap.cc"
"src/schedule_reminder_im_adapter.cc"
"src/linx_mcp_bridge.cc" "src/linx_ota_bootstrap.cc" "src/wifi_provisioning.cc"
"src/wifi_provisioning_esp.cc"
"src/im_binding_mcp_tools.cc" "src/im_binding_presentation.cc"
"src/serial_voice_test.cc"
INCLUDE_DIRS "include" "src"
PRIV_INCLUDE_DIRS "../voicelife_im/src/transport"
REQUIRES voicelife_contracts
PRIV_REQUIRES voicelife_application voicelife_runtime_esp voicelife_mcp voicelife_voice voicelife_linx
voicelife_linx_esp voicelife_audio_esp voicelife_storage_memory
Expand All @@ -14,10 +16,6 @@ idf_component_register(
esp_driver_usb_serial_jtag led_strip
)

if(CONFIG_VOICELIFE_STORAGE_FATFS AND NOT CONFIG_VOICELIFE_STORAGE_SQLITE)
message(FATAL_ERROR "FATFS 持久化必须同时启用 VOICELIFE_STORAGE_SQLITE")
endif()

if(CONFIG_VOICELIFE_STORAGE_SQLITE AND NOT CONFIG_VOICELIFE_STORAGE_FATFS)
message(FATAL_ERROR "SQLite 持久化必须同时启用 VOICELIFE_STORAGE_FATFS")
if(NOT CONFIG_VOICELIFE_STORAGE_FATFS OR NOT CONFIG_VOICELIFE_STORAGE_SQLITE)
message(FATAL_ERROR "设备 Runtime 必须同时启用 FATFS 与 SQLite 持久化存储")
endif()
18 changes: 16 additions & 2 deletions components/voicelife_runtime/src/bootstrap/storage_bootstrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
#include <string>
#include <utility>

#include "voicelife/storage_memory/memory_schedule_repository.h"
#if defined(ESP_PLATFORM) && CONFIG_VOICELIFE_STORAGE_FATFS_RUNTIME
#include "voicelife/storage_fatfs/fatfs_volume.h"
#include "voicelife/storage_sqlite/sqlite_database.h"
#include "voicelife/storage_sqlite/sqlite_schedule_reminder_task_repository.h"
#include "voicelife/storage_sqlite/sqlite_schedule_repository.h"
#include "voicelife/storage_sqlite/sqlite_schedule_rule_repository.h"
#include "voicelife/storage_sqlite/sqlite_schema.h"
#include "voicelife/storage_sqlite/voicelife_schema.h"
#else
#include "voicelife/storage_memory/memory_schedule_reminder_task_repository.h"
#include "voicelife/storage_memory/memory_schedule_repository.h"
#endif

#ifdef ESP_PLATFORM
Expand Down Expand Up @@ -59,7 +62,8 @@ class StorageBootstrap::Impl final {
: volume_(MakeVolumeConfig()),
database_(DatabaseUri(volume_.config().base_path), "unix-none"),
schedule_repository_(database_),
schedule_rule_repository_(database_)
schedule_rule_repository_(database_),
schedule_reminder_task_repository_(database_)
#endif
{
}
Expand Down Expand Up @@ -186,6 +190,10 @@ class StorageBootstrap::Impl final {
[[nodiscard]] schedule::ScheduleExceptionRepository& GetScheduleExceptionRepository() {
return schedule_rule_repository_;
}

[[nodiscard]] schedule::ScheduleReminderTaskRepository& GetScheduleReminderTaskRepository() {
return schedule_reminder_task_repository_;
}
#endif

private:
Expand All @@ -205,9 +213,11 @@ class StorageBootstrap::Impl final {
storage_sqlite::SqliteDatabase database_;
storage_sqlite::SqliteScheduleRepository schedule_repository_;
storage_sqlite::SqliteScheduleRuleRepository schedule_rule_repository_;
storage_sqlite::SqliteScheduleReminderTaskRepository schedule_reminder_task_repository_;
#else
storage_memory::MemoryScheduleRepository schedule_repository_;
storage_memory::MemoryScheduleRuleRepository schedule_rule_repository_{schedule_repository_};
storage_memory::MemoryScheduleReminderTaskRepository schedule_reminder_task_repository_;
#endif
bool ready_ = false;
};
Expand Down Expand Up @@ -236,6 +246,10 @@ schedule::ScheduleRuleRepository& StorageBootstrap::GetScheduleRuleRepository()
schedule::ScheduleExceptionRepository& StorageBootstrap::GetScheduleExceptionRepository() {
return impl_->GetScheduleExceptionRepository();
}

schedule::ScheduleReminderTaskRepository& StorageBootstrap::GetScheduleReminderTaskRepository() {
return impl_->GetScheduleReminderTaskRepository();
}
#endif

} // namespace voicelife::runtime
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class ScheduleRepository;
class ScheduleOperationRepository;
class ScheduleRuleRepository;
class ScheduleExceptionRepository;
class ScheduleReminderTaskRepository;
} // namespace voicelife::schedule

namespace voicelife::runtime {
Expand Down Expand Up @@ -78,6 +79,9 @@ class StorageBootstrap final {
* @return 生命周期与当前装配器一致的例外仓储引用;与规则仓储共享同一连接。
*/
[[nodiscard]] schedule::ScheduleExceptionRepository& GetScheduleExceptionRepository();

/** @brief 获取独立的日程提醒任务仓储。 */
[[nodiscard]] schedule::ScheduleReminderTaskRepository& GetScheduleReminderTaskRepository();
#endif

private:
Expand Down
Loading
Loading