docs: 为全部 38 个源文件补充详细模块级注释
为 iAs 项目的所有 Rust 源文件添加了系统化的模块级文档注释(//!), 覆盖全部 src/ 下的文件。每个文件包含模块职责、架构设计、数据流 和关键设计决策的说明。 主要变更: - 入口/CLI: main.rs, cli.rs — 系统架构概览、子命令说明 - Daemon/Worker: daemon.rs, worker.rs — 三消费者架构、旧架构说明 - 微信通道: mod.rs, client.rs, types.rs — API 端点、登录流程 - LLM 系统: mod.rs, types.rs, provider.rs, deepseek.rs, conversation.rs — 架构分层、流式处理、工具循环、摘要机制 - 上下文管理: mod.rs, types.rs, builder.rs, tools.rs — Checkpoint 机制、Token Budget、双重摘要 - 工具系统: mod.rs, types.rs, builtin.rs, approval.rs — 两层元工具架构、审批流程 - 内置工具: mod.rs + 6 个工具 — API 端点、认证方式、参数说明 - 消息队列: mod.rs, message_queue.rs, runner.rs — 公平轮转算法、三消费者路由 - 数据库/状态/调度/日志: db/mod.rs, state.rs, scheduler.rs, logger.rs — 表结构、回退策略、定时任务、日志初始化
This commit is contained in:
+15
@@ -1,3 +1,18 @@
|
|||||||
|
//! ## CLI 子命令定义 —— clap 参数解析
|
||||||
|
//!
|
||||||
|
//! 定义了 iAs 的所有命令行子命令和参数:
|
||||||
|
//!
|
||||||
|
//! | 命令 | 说明 |
|
||||||
|
//! |------|------|
|
||||||
|
//! | `ias login` | 扫码登录微信 |
|
||||||
|
//! | `ias listen --llm` | 启动 AI 自动回复 |
|
||||||
|
//! | `ias send --to <id> --text <msg>` | 手动发送消息 |
|
||||||
|
//! | `ias whoami` | 查看登录状态 |
|
||||||
|
//! | `ias usage` | Token 用量统计 |
|
||||||
|
//! | `ias service` | 后台服务模式 |
|
||||||
|
//! | `ias daemon` | 守护进程模式 |
|
||||||
|
//! | `ias tool <tool>` | 调用内置工具 |
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
/// ## iAs —— 微信 AI 智能助手
|
/// ## iAs —— 微信 AI 智能助手
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
//! ## 上下文构建器 —— Token 估算 + 上下文组装 + 摘要触发
|
||||||
|
//!
|
||||||
|
//! 这是 LLM 调用前的关键步骤:
|
||||||
|
//!
|
||||||
|
//! 1. **Token 估算** — 中/英文分别估算 token 消耗
|
||||||
|
//! 2. **上下文构建** — 从 ChatSession 中提取合适的消息,在 token budget 内保留最多上下文
|
||||||
|
//! 3. **摘要触发** — 溢出摘要(token 超 budget)和空闲超时摘要(12h 无消息)
|
||||||
|
//!
|
||||||
|
//! ### Token 估算公式
|
||||||
|
//! - 中文字符 ≈ 1.5 tokens/字
|
||||||
|
//! - 英文字符 ≈ 0.25 tokens/字符
|
||||||
|
//! - 每条消息 + 8 tokens 格式开销
|
||||||
|
|
||||||
use crate::context::types::ChatSession;
|
use crate::context::types::ChatSession;
|
||||||
use crate::llm::conversation::Summarizer;
|
use crate::llm::conversation::Summarizer;
|
||||||
use crate::llm::types::Message;
|
use crate::llm::types::Message;
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
//! ## 上下文管理 —— 对话状态 + Token 预算 + 长期记忆
|
||||||
|
//!
|
||||||
|
//! 管理 LLM 对话的上下文状态,包括:
|
||||||
|
//!
|
||||||
|
//! - `types` — `ChatSession` 核心数据结构(消息历史、摘要、checkpoint)
|
||||||
|
//! - `builder` — Token 预算估算、上下文构建、摘要触发
|
||||||
|
//! - `tools` — `MemoryStore` 长期记忆管理器
|
||||||
|
//!
|
||||||
|
//! ### 关键设计
|
||||||
|
//! - **Checkpoint 机制**:checkpoint 之前的消息被压缩成摘要,之后的消息保持完整
|
||||||
|
//! - **Token Budget**:默认 28000 tokens,留 4000 给回复
|
||||||
|
//! - **双重摘要**:溢出摘要(token 超预算)和空闲超时摘要(12h 无消息)
|
||||||
|
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod tools;
|
pub mod tools;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
//! ## 长期记忆管理器 + 摘要读取工具
|
||||||
|
//!
|
||||||
|
//! 提供两个核心功能:
|
||||||
|
//!
|
||||||
|
//! 1. **MemoryStore** — 按用户隔离的长期记忆存储(内存缓存 + PostgreSQL 双写)
|
||||||
|
//! 2. **read_summaries** — 读取历史会话摘要(内存 + 数据库合并去重)
|
||||||
|
//!
|
||||||
|
//! ### 数据流
|
||||||
|
//! ```text
|
||||||
|
//! 启动时 load(user_id) → 从数据库加载到缓存
|
||||||
|
//! 对话中 read_for() → 从缓存读取,返回 "1. ...\n2. ..." 格式
|
||||||
|
//! 对话中 write_for() → 写入缓存 + 写入数据库
|
||||||
|
//! ```
|
||||||
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
//! ## 聊天会话状态 —— 核心数据结构
|
||||||
|
//!
|
||||||
|
//! 这是整个项目中最重要的数据结构之一 —— 它维护了一次 LLM 对话的完整状态。
|
||||||
|
//!
|
||||||
|
//! ### 关键设计
|
||||||
|
//! - **消息历史** (`messages`) — 从 checkpoint 之后的消息保持完整
|
||||||
|
//! - **摘要机制** (`summaries` + `checkpoint`) — checkpoint 之前的消息被压缩成摘要
|
||||||
|
//! - **Token 预算** (`token_budget`) — 默认 28000,留 4000 给回复
|
||||||
|
//! - **空闲超时** (`idle_timeout_secs`) — 默认 12 小时,超时后生成空闲摘要
|
||||||
|
//! - **会话 ID** (`session_id`) — 每次新对话分配一个 Uuid,用于追踪工具调用往返
|
||||||
|
//!
|
||||||
|
//! ### 消息生命周期
|
||||||
|
//! 1. 用户消息到达 → `add_user()` → 存入 `messages`
|
||||||
|
//! 2. AI 回复 → `add_assistant()` → 存入 `messages`
|
||||||
|
//! 3. Token 超 budget → `trigger_overflow_summary()` → 压缩 checkpoint 前的消息
|
||||||
|
//! 4. 12h 空闲 → `trigger_idle_summary()` → 生成摘要,清空消息
|
||||||
|
|
||||||
use crate::llm::types::Message;
|
use crate::llm::types::Message;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
@@ -9,6 +9,26 @@
|
|||||||
//! 数据库是可选的。如果数据库不可用,系统会优雅地回退到文件存储
|
//! 数据库是可选的。如果数据库不可用,系统会优雅地回退到文件存储
|
||||||
//! (`StateManager`),核心功能仍然可用。
|
//! (`StateManager`),核心功能仍然可用。
|
||||||
|
|
||||||
|
//! ## 数据库管理 —— PostgreSQL 连接 + 迁移
|
||||||
|
//!
|
||||||
|
//! ### 职责
|
||||||
|
//! 1. 从 `DATABASE_URL` 环境变量连接 PostgreSQL
|
||||||
|
//! 2. 自动运行 `./migrations/` 下的 SQL 迁移
|
||||||
|
//! 3. 提供连接池引用给各模块使用
|
||||||
|
//!
|
||||||
|
//! ### 回退策略
|
||||||
|
//! 数据库是可选的。如果数据库不可用,系统会优雅地回退到文件存储
|
||||||
|
//! (`StateManager`),核心功能仍然可用。
|
||||||
|
//!
|
||||||
|
//! ### 表结构
|
||||||
|
//! - `app_state` — 认证状态 KV 存储
|
||||||
|
//! - `chat_records` — 收发消息记录
|
||||||
|
//! - `llm_usage` — Token 用量统计
|
||||||
|
//! - `user_memories` — 用户长期记忆
|
||||||
|
//! - `pending_approvals` — 待审批记录
|
||||||
|
//! - `session_summaries` — 会话摘要
|
||||||
|
//! - `scheduled_tasks` — 定时任务
|
||||||
|
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
|
|||||||
@@ -1,3 +1,25 @@
|
|||||||
|
//! ## Conversation —— LLM 对话管理器(核心类)
|
||||||
|
//!
|
||||||
|
//! 这是整个项目中"与 LLM 对话"的核心抽象,负责:
|
||||||
|
//!
|
||||||
|
//! ### 职责
|
||||||
|
//! 1. **管理对话状态** — 持有 `ChatSession`(消息历史、摘要、token 预算)
|
||||||
|
//! 2. **流式对话** — 调用 provider 发起流式请求,逐 chunk 消费
|
||||||
|
//! 3. **工具循环** — 自动处理 LLM 发起的工具调用,循环直到无工具或达到轮次上限
|
||||||
|
//! 4. **异步工具** — 支持工具通过消息队列异步执行,结果回来后恢复对话
|
||||||
|
//! 5. **上下文管理** — token 预算检查、溢出摘要、空闲超时摘要
|
||||||
|
//!
|
||||||
|
//! ### 工具循环流程
|
||||||
|
//! ```text
|
||||||
|
//! chat_with_tools(user_msg)
|
||||||
|
//! → run_tool_loop()
|
||||||
|
//! → 1. build_context() 构建上下文(含摘要、system prompt)
|
||||||
|
//! → 2. provider.chat_stream() 发起流式请求
|
||||||
|
//! → 3. 消费流,收集文本 + tool_calls
|
||||||
|
//! → 4. 有 tool_calls?→ 执行 → 回到步骤 1(最多 5 轮)
|
||||||
|
//! → 5. 无 tool_calls?→ 返回最终回复
|
||||||
|
//! ```
|
||||||
|
|
||||||
use crate::context::builder;
|
use crate::context::builder;
|
||||||
use crate::context::types::ChatSession;
|
use crate::context::types::ChatSession;
|
||||||
use crate::llm::provider::{BoxedProvider, StreamReceiver, create_provider};
|
use crate::llm::provider::{BoxedProvider, StreamReceiver, create_provider};
|
||||||
|
|||||||
@@ -1,3 +1,30 @@
|
|||||||
|
//! ## DeepSeek LLM 提供商实现
|
||||||
|
//!
|
||||||
|
//! 实现了 `LlmProvider` trait,通过 DeepSeek API 提供流式对话能力。
|
||||||
|
//!
|
||||||
|
//! ### 配置
|
||||||
|
//! 从环境变量读取:
|
||||||
|
//! - `DEEPSEEK_API_KEY` — API 密钥(必需)
|
||||||
|
//! - `DEEPSEEK_BASE_URL` — API 地址(可选,默认 `https://api.deepseek.com/v1`)
|
||||||
|
//! - `DEEPSEEK_MODEL` — 模型名(可选,默认 `deepseek-v4-flash`)
|
||||||
|
//!
|
||||||
|
//! ### 请求格式
|
||||||
|
//! 发送 POST 到 `{base_url}/chat/completions`,标准 OpenAI-compatible 格式:
|
||||||
|
//! ```json
|
||||||
|
//! {
|
||||||
|
//! "model": "deepseek-v4-flash",
|
||||||
|
//! "messages": [...],
|
||||||
|
//! "stream": true,
|
||||||
|
//! "tools": [...],
|
||||||
|
//! "thinking": {...}
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### 流式处理
|
||||||
|
//! 使用 `reqwest` 的 `bytes_stream()` 逐 chunk 读取 SSE 流,
|
||||||
|
//! 通过 `parse_chat_chunk()` 解析每个 delta,通过 mpsc channel 发送给调用方。
|
||||||
|
//! 工具调用的 delta 跨多个 chunk 拼接,在 Done 信号中一次性返回。
|
||||||
|
|
||||||
use crate::llm::provider::{
|
use crate::llm::provider::{
|
||||||
LlmProvider, ParsedChunk, StreamReceiver, StreamSender, parse_chat_chunk,
|
LlmProvider, ParsedChunk, StreamReceiver, StreamSender, parse_chat_chunk,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
//! ## LLM 对话系统 —— DeepSeek API 集成
|
||||||
|
//!
|
||||||
|
//! 封装与 DeepSeek 大语言模型的交互:
|
||||||
|
//!
|
||||||
|
//! - `types` — 消息/角色/工具调用/用量等核心类型
|
||||||
|
//! - `provider` — `LlmProvider` trait 抽象 + SSE 流式解析
|
||||||
|
//! - `deepseek` — DeepSeek API 流式客户端实现
|
||||||
|
//! - `conversation` — `Conversation` 对话管理器(工具循环、摘要、上下文管理)
|
||||||
|
//!
|
||||||
|
//! ### 架构
|
||||||
|
//! ```text
|
||||||
|
//! Conversation (对话管理器)
|
||||||
|
//! ├── ChatSession (消息历史 + 摘要 + checkpoint)
|
||||||
|
//! ├── LlmProvider (trait: chat_stream → StreamReceiver)
|
||||||
|
//! │ └── DeepSeekProvider (实现: SSE 流式请求)
|
||||||
|
//! └── ToolExecutor (回调: 执行 LLM 调用的工具)
|
||||||
|
//! ```
|
||||||
|
|
||||||
pub mod conversation;
|
pub mod conversation;
|
||||||
pub mod deepseek;
|
pub mod deepseek;
|
||||||
pub mod provider;
|
pub mod provider;
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
//! ## LLM 提供商抽象(trait) + SSE 流式解析
|
||||||
|
//!
|
||||||
|
//! 定义了 LLM 层的核心抽象接口 `LlmProvider`,以及 SSE 流式响应的解析工具。
|
||||||
|
//!
|
||||||
|
//! ### 设计模式
|
||||||
|
//! - `LlmProvider` trait — 所有 LLM 提供商需实现的接口
|
||||||
|
//! - `create_provider()` — 工厂函数,根据配置创建对应的提供商实例
|
||||||
|
//! - `parse_chat_chunk()` — SSE 流式 delta 解析器
|
||||||
|
//!
|
||||||
|
//! ### 流式架构
|
||||||
|
//! ```text
|
||||||
|
//! Provider.chat_stream() → mpsc::Receiver<StreamChunk>
|
||||||
|
//! ├── StreamChunk::Text (delta 文本)
|
||||||
|
//! ├── StreamChunk::Reasoning (思考内容)
|
||||||
|
//! ├── StreamChunk::Done (完成信号 + 工具调用 + 用量)
|
||||||
|
//! └── StreamChunk::Error (错误)
|
||||||
|
//! ```
|
||||||
|
|
||||||
use crate::llm::types::{ConversationConfig, Message, StreamChunk};
|
use crate::llm::types::{ConversationConfig, Message, StreamChunk};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
//! ## LLM 对话核心类型定义
|
||||||
|
//!
|
||||||
|
//! 定义了与 LLM API 交互的全部核心类型:
|
||||||
|
//!
|
||||||
|
//! - `Role` — 对话角色(System / User / Assistant / Tool)
|
||||||
|
//! - `Message` — 单条消息(支持文本、工具调用、工具结果)
|
||||||
|
//! - `ToolCall` — LLM 发起的工具调用请求
|
||||||
|
//! - `StreamChunk` — 流式响应块(Text / Reasoning / Done / Error)
|
||||||
|
//! - `Usage` — Token 用量统计
|
||||||
|
//! - `ConversationConfig` — 对话配置参数
|
||||||
|
//!
|
||||||
|
//! 这些类型符合 OpenAI/DeepSeek 的 Chat Completion API 标准格式。
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// ## 对话角色(System / User / Assistant / Tool)
|
/// ## 对话角色(System / User / Assistant / Tool)
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
//! ## 日志初始化
|
||||||
|
//!
|
||||||
|
//! ### 两种模式
|
||||||
|
//! - **终端模式** — 彩色输出到 stderr,适合开发调试
|
||||||
|
//! - **文件模式** (`with_file=true`) — 同时输出到终端和日滚文件
|
||||||
|
//! `~/.ias/logs/ias.log.YYYY-MM-DD`
|
||||||
|
//!
|
||||||
|
//! 日志级别通过 `RUST_LOG` 环境变量控制(默认 `info`)。
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
//! ## 消息队列 —— 多渠道公平轮转 + 三消费者路由
|
||||||
|
//!
|
||||||
|
//! 这是 daemon 架构的消息管线核心:
|
||||||
|
//!
|
||||||
|
//! - `message_queue` — 公平轮转队列(按用户轮转出队)
|
||||||
|
//! - `runner` — 队列运行时(出队 → 按 MessageKind 路由到对应 mpsc 通道)
|
||||||
|
//!
|
||||||
|
//! ### 消费者路由
|
||||||
|
//! ```text
|
||||||
|
//! MessageKind::UserMessage / ToolResult / ScheduledTask → LLM Consumer
|
||||||
|
//! MessageKind::ToolCall / ApprovalRequest → Tool Consumer
|
||||||
|
//! MessageKind::LLMReply → Send Consumer
|
||||||
|
//! ```
|
||||||
|
|
||||||
pub mod message_queue;
|
pub mod message_queue;
|
||||||
pub mod runner;
|
pub mod runner;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
//! ## 定时任务调度器
|
||||||
|
//!
|
||||||
|
//! 轮询 PostgreSQL 中到期的 `scheduled_tasks` 表,执行任务后将结果通知用户。
|
||||||
|
//!
|
||||||
|
//! ### 调度流程
|
||||||
|
//! 1. 每 5 秒检查一次是否有到期任务(`next_run_at <= NOW()`)
|
||||||
|
//! 2. 使用 `FOR UPDATE SKIP LOCKED` 防止多个实例重复执行
|
||||||
|
//! 3. 执行任务 shell 命令(120 秒超时)
|
||||||
|
//! 4. 更新 `next_run_at`(当前时间 + interval_seconds)
|
||||||
|
//! 5. 通过回调函数通知用户结果
|
||||||
|
//!
|
||||||
|
//! ### 安全保障
|
||||||
|
//! - `SKIP LOCKED` — 多实例部署时不会抢同一任务
|
||||||
|
//! - `kill_on_drop` — 子进程在超时时会被杀掉
|
||||||
|
//! - 120 秒超时 — 防止 shell 命令无限执行
|
||||||
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
//! ## 审批管理器 —— 高风险工具的用户确认流程
|
||||||
|
//!
|
||||||
|
//! 当 LLM 调用高风险工具时,需要用户通过微信输入确认码来批准。
|
||||||
|
//!
|
||||||
|
//! ### 完整流程
|
||||||
|
//! 1. `create(user_id, skill_name)` → 生成 6 位随机确认码,SHA-256 哈希后存储
|
||||||
|
//! 2. 向用户发送 "⚠️ 操作确认\n\n技能:{name}\n确认码:{code}"
|
||||||
|
//! 3. 用户在微信聊天中输入确认码
|
||||||
|
//! 4. `handle_reply(user_id, reply)` → 验证确认码哈希
|
||||||
|
//! - 匹配 → `Approved`
|
||||||
|
//! - 不匹配 → 减少尝试次数(最多 3 次)
|
||||||
|
//! - 输入 0 或"取消" → `Rejected`
|
||||||
|
//! - 超时 → `Expired`(每 60 秒清理一次)
|
||||||
|
//!
|
||||||
|
//! ### 安全设计
|
||||||
|
//! - 确认码用 SHA-256 哈希后存储,原始码不持久化
|
||||||
|
//! - 每次生成随机 6 位数字(100000-999999)
|
||||||
|
//! - 最多 3 次尝试,超时 5 分钟
|
||||||
|
//! - 同时支持内存和数据库双写
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
//! ## 内置工具注册表 —— 工具路由中心
|
||||||
|
//!
|
||||||
|
//! 连接 LLM 的元工具调用和实际 Rust 实现的工具函数。
|
||||||
|
//! 所有内置工具在此统一注册,通过名称路由到对应实现。
|
||||||
|
//!
|
||||||
|
//! ### 职责
|
||||||
|
//! 1. `execute(name, args)` — 按名称路由到对应的 builtin 工具执行
|
||||||
|
//! 2. `specs()` — 返回所有内置工具的 SkillSpec(给元工具 `query_capabilities` 使用)
|
||||||
|
//! 3. `is_high_risk(name)` — 判断工具是否需要审批
|
||||||
|
//! 4. `is_builtin(name)` — 判断名称是否对应一个已注册的内置工具
|
||||||
|
|
||||||
use crate::tools::types::{RiskLevel, SkillResult};
|
use crate::tools::types::{RiskLevel, SkillResult};
|
||||||
|
|
||||||
/// ## 内置工具注册表
|
/// ## 内置工具注册表
|
||||||
|
|||||||
@@ -1,3 +1,28 @@
|
|||||||
|
//! ## 高德地图综合工具(Rust 原生实现 — v5 API)
|
||||||
|
//!
|
||||||
|
//! 提供 6 个高德地图相关的工具:
|
||||||
|
//!
|
||||||
|
//! | 工具名 | 功能 | API 版本 |
|
||||||
|
//! |--------|------|---------|
|
||||||
|
//! | `amap_poi_search` | POI(地点)搜索 + 周边搜索 | v5 |
|
||||||
|
//! | `amap_geocode` | 地理编码:地址 → 坐标 | v3 |
|
||||||
|
//! | `amap_reverse_geocode` | 逆地理编码:坐标 → 地址 | v3 |
|
||||||
|
//! | `amap_route_plan` | 路径规划(步行/驾车/骑行/公交) | v5 |
|
||||||
|
//! | `amap_travel_plan` | 智能旅游规划(搜索兴趣点 + 规划路线) | v5 |
|
||||||
|
//! | `amap_map_link` | 生成地图可视化链接 | - |
|
||||||
|
//!
|
||||||
|
//! ### 认证
|
||||||
|
//! 环境变量 `AMAP_WEBSERVICE_KEY` 或 `AMAP_KEY`
|
||||||
|
//! 获取 Key: https://lbs.amap.com/api/webservice/create-project-and-key
|
||||||
|
//!
|
||||||
|
//! ### API 端点
|
||||||
|
//! - POI 文本搜索: `GET /v5/place/text`
|
||||||
|
//! - POI 周边搜索: `GET /v5/place/around`
|
||||||
|
//! - 地理编码: `GET /v3/geocode/geo`
|
||||||
|
//! - 逆地理编码: `GET /v3/geocode/regeo`
|
||||||
|
//! - 步行/驾车/骑行/公交路线: `GET /v5/direction/{type}`
|
||||||
|
//! - 所有请求必须带 `appname=amap-lbs-skill`
|
||||||
|
|
||||||
// 高德地图综合工具(Rust 原生实现 — v5 API)
|
// 高德地图综合工具(Rust 原生实现 — v5 API)
|
||||||
//
|
//
|
||||||
// API:
|
// API:
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
//! 日期时间工具
|
//! ## 日期时间工具 —— 获取当前北京时间
|
||||||
|
//!
|
||||||
|
//! 最简单的内置工具,无网络依赖,纯粹返回本地时间。
|
||||||
|
//! 用于 LLM 需要知道当前时间时调用。
|
||||||
|
//!
|
||||||
|
//! ### 风险等级
|
||||||
|
//! `Low` — 无需用户确认,直接执行
|
||||||
|
//!
|
||||||
|
//! ### 输出格式
|
||||||
|
//! ```json
|
||||||
|
//! {"datetime": "2026-06-10 14:30:00"}
|
||||||
|
//! ```
|
||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
||||||
use super::super::types::{RiskLevel, SkillResult, SkillSpec};
|
use super::super::types::{RiskLevel, SkillResult, SkillSpec};
|
||||||
|
|
||||||
|
/// 返回该工具的元数据描述(供 query_capabilities 元工具使用)
|
||||||
pub fn spec() -> SkillSpec {
|
pub fn spec() -> SkillSpec {
|
||||||
SkillSpec {
|
SkillSpec {
|
||||||
name: "get_current_datetime".into(),
|
name: "get_current_datetime".into(),
|
||||||
@@ -13,6 +26,10 @@ pub fn spec() -> SkillSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 执行日期时间查询
|
||||||
|
///
|
||||||
|
/// 使用 chrono::Local 获取系统当前时间,格式化为 "YYYY-MM-DD HH:MM:SS"。
|
||||||
|
/// 注意:系统时区需设置为 Asia/Shanghai(北京时间 UTC+8)。
|
||||||
pub fn execute() -> SkillResult {
|
pub fn execute() -> SkillResult {
|
||||||
let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
|
let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
|
||||||
SkillResult::ok(serde_json::json!({"datetime": now}).to_string())
|
SkillResult::ok(serde_json::json!({"datetime": now}).to_string())
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
//! 网页内容提取工具
|
//! ## 网页内容提取工具 —— 可读性算法提取正文
|
||||||
//!
|
//!
|
||||||
//! 使用可读性算法自动识别正文区域:清除广告/导航/页脚噪声,
|
//! 自动识别网页正文区域,清除广告、导航、页脚等噪声,
|
||||||
//! 按文本密度评分,取最高分区域。同时支持站点自定义 CSS 选择器。
|
//! 按文本密度评分,取最高分区域作为正文。
|
||||||
//!
|
//!
|
||||||
//! 站点选择器配置: `~/.ias/site_selectors.json`
|
//! ### 提取策略(三层降级)
|
||||||
|
//! 1. **自定义选择器** — 从 `~/.ias/site_selectors.json` 读取按站点的 CSS 选择器
|
||||||
|
//! 2. **可读性算法** — 清除噪声 → 对块级元素评分 → 取最高分
|
||||||
|
//! 3. **回退** — 取 `<body>` 的全部文本
|
||||||
|
//!
|
||||||
|
//! ### 评分因子
|
||||||
|
//! - 文本长度(基础分)
|
||||||
|
//! - 逗号/句号数(句子结构丰富度)
|
||||||
|
//! - `<p>` 标签数(段落丰富度)
|
||||||
|
//! - 链接密度惩罚(链接太多 → 导航/索引)
|
||||||
|
//! - 代码块惩罚(代码多 → 降分)
|
||||||
|
//!
|
||||||
|
//! ### 配置
|
||||||
|
//! 站点选择器配置文件: `~/.ias/site_selectors.json`
|
||||||
|
//! 格式: `{"example.com": ["article.main", "#content"]}`
|
||||||
|
|
||||||
use reqwest::Client as HttpClient;
|
use reqwest::Client as HttpClient;
|
||||||
use scraper::{ElementRef, Html, Selector};
|
use scraper::{ElementRef, Html, Selector};
|
||||||
@@ -11,19 +25,20 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use super::super::types::{RiskLevel, SkillResult, SkillSpec};
|
use super::super::types::{RiskLevel, SkillResult, SkillSpec};
|
||||||
|
|
||||||
/// 站点选择器配置文件路径
|
/// 站点选择器配置文件路径(相对于 home 目录)
|
||||||
const CONFIG_PATH: &str = ".ias/site_selectors.json";
|
const CONFIG_PATH: &str = ".ias/site_selectors.json";
|
||||||
|
|
||||||
/// 最大输出字符数
|
/// 最大输出字符数(超过则截断)
|
||||||
const MAX_OUTPUT_CHARS: usize = 6000;
|
const MAX_OUTPUT_CHARS: usize = 6000;
|
||||||
|
|
||||||
/// 需移除的 HTML 标签
|
/// 需要移除的 HTML 标签列表(这些标签内的内容不可能是正文)
|
||||||
const STRIP_TAGS: &[&str] = &[
|
const STRIP_TAGS: &[&str] = &[
|
||||||
"script", "style", "noscript", "iframe", "svg",
|
"script", "style", "noscript", "iframe", "svg",
|
||||||
"nav", "header", "footer", "aside", "form",
|
"nav", "header", "footer", "aside", "form",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// 需移除的 class/id 关键词
|
/// 需要移除的 class/id 关键词列表
|
||||||
|
/// 匹配元素的 class 或 id 属性,包含这些关键词的视为噪声元素
|
||||||
const NOISE_PATTERNS: &[&str] = &[
|
const NOISE_PATTERNS: &[&str] = &[
|
||||||
"ad", "ads", "advert", "banner",
|
"ad", "ads", "advert", "banner",
|
||||||
"sidebar", "side-bar", "widget",
|
"sidebar", "side-bar", "widget",
|
||||||
@@ -42,7 +57,7 @@ const NOISE_PATTERNS: &[&str] = &[
|
|||||||
"edit-section", "noprint", "thumb",
|
"edit-section", "noprint", "thumb",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// 内容评分阈值:低于此分不取
|
/// 内容评分阈值:低于此分的元素不视为正文候选
|
||||||
const MIN_SCORE_THRESHOLD: f64 = 50.0;
|
const MIN_SCORE_THRESHOLD: f64 = 50.0;
|
||||||
|
|
||||||
// ─── 工具 spec ───
|
// ─── 工具 spec ───
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
//! 备忘录工具 — 文件存储,支持 add/list/delete
|
//! ## 备忘录工具 —— 本地文件存储的简易备忘录
|
||||||
|
//!
|
||||||
|
//! 支持三种操作:
|
||||||
|
//! - `add` — 添加一条备忘录(自动分配自增 ID)
|
||||||
|
//! - `list` — 列出所有备忘录
|
||||||
|
//! - `delete` — 按 ID 删除备忘录
|
||||||
|
//!
|
||||||
|
//! ### 存储
|
||||||
|
//! 数据存储在 `.data/memos.json` 文件中,JSON 数组格式。
|
||||||
|
//! 每条记录包含:`id`、`content`、`created_at`。
|
||||||
|
//!
|
||||||
|
//! ### 智能推断
|
||||||
|
//! 当通过 `call_capability` 调用时,可以从 prompt 文本中推断操作类型:
|
||||||
|
//! - 包含"添加"/"新增"/"add" → 自动转为 add 操作
|
||||||
|
//! - 包含"删除"/"移除"/"delete" → 自动转为 delete 操作
|
||||||
|
//! - 其他 → 默认 list 操作
|
||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
||||||
use super::super::types::{RiskLevel, SkillResult, SkillSpec};
|
use super::super::types::{RiskLevel, SkillResult, SkillSpec};
|
||||||
|
|
||||||
|
/// 返回该工具的元数据描述(供 query_capabilities 元工具使用)
|
||||||
pub fn spec() -> SkillSpec {
|
pub fn spec() -> SkillSpec {
|
||||||
SkillSpec {
|
SkillSpec {
|
||||||
name: "manage_memos".into(),
|
name: "manage_memos".into(),
|
||||||
@@ -20,6 +37,10 @@ pub fn spec() -> SkillSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 执行备忘录操作
|
||||||
|
///
|
||||||
|
/// 从 JSON 参数中解析 action/content/id,执行对应操作。
|
||||||
|
/// 数据文件路径为 `.data/memos.json`,自动创建目录。
|
||||||
pub async fn execute(args_json: &str) -> SkillResult {
|
pub async fn execute(args_json: &str) -> SkillResult {
|
||||||
let mut params: serde_json::Value = serde_json::from_str(args_json).unwrap_or_default();
|
let mut params: serde_json::Value = serde_json::from_str(args_json).unwrap_or_default();
|
||||||
// call_capability 透传完整参数,action/content 在顶层
|
// call_capability 透传完整参数,action/content 在顶层
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
//! ## 内置工具实现集合
|
||||||
|
//!
|
||||||
|
//! 每个子模块对应一个具体的工具实现:
|
||||||
|
//!
|
||||||
|
//! | 模块 | 工具名 | 功能 |
|
||||||
|
//! |------|--------|------|
|
||||||
|
//! | `datetime` | `get_current_datetime` | 获取当前北京时间 |
|
||||||
|
//! | `weather` | `query_weather` | 和风天气查询(实时/预报/逐时) |
|
||||||
|
//! | `web_search` | `web_search` | Tavily 联网搜索 |
|
||||||
|
//! | `fetch_page` | `fetch_page` | 网页正文提取(可读性算法) |
|
||||||
|
//! | `memos` | `manage_memos` | 备忘录增删查 |
|
||||||
|
//! | `amap` | `amap_*` | 高德地图系列(POI/地理编码/路径规划/旅游规划) |
|
||||||
|
//!
|
||||||
|
//! 每个工具都提供 `spec()` 返回元数据,和 `execute()` 执行入口。
|
||||||
|
//! 通过 `BuiltinRegistry` 统一注册和路由。
|
||||||
|
|
||||||
pub mod amap;
|
pub mod amap;
|
||||||
pub mod datetime;
|
pub mod datetime;
|
||||||
pub mod fetch_page;
|
pub mod fetch_page;
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
// 和风天气查询工具(Rust 原生实现,替代 bash 脚本)
|
//! ## 和风天气查询工具(Rust 原生实现)
|
||||||
//
|
//!
|
||||||
// API:
|
//! 通过和风天气 API 查询实时天气、每日预报和逐小时预报。
|
||||||
// GeoAPI: GET /geo/v2/city/lookup?location=城市名 → city_id
|
//!
|
||||||
// Now: GET /v7/weather/now?location=city_id → 实时天气
|
//! ### API 端点
|
||||||
// Daily: GET /v7/weather/{3,7,10,15,30}d?location=id → 每日预报
|
//! - 城市查询: `GET /geo/v2/city/lookup?location=城市名` → 获取 city_id
|
||||||
// Hourly: GET /v7/weather/{24,72,168}h?location=id → 逐时预报
|
//! - 实时天气: `GET /v7/weather/now?location=city_id`
|
||||||
//
|
//! - 每日预报: `GET /v7/weather/{3,7,10,15,30}d?location=id`
|
||||||
// 认证: EdDSA JWT (Ed25519)
|
//! - 逐时预报: `GET /v7/weather/{24,72,168}h?location=id`
|
||||||
// 缓存: city_id 内存缓存 1h TTL
|
//!
|
||||||
|
//! ### 认证方式
|
||||||
|
//! 使用 EdDSA (Ed25519) JWT 进行 API 认证。
|
||||||
|
//! 密钥对存放在 `qweather/` 目录下。
|
||||||
|
//!
|
||||||
|
//! ### 环境变量
|
||||||
|
//! - `QWEATHER_API_HOST` — API 地址(默认 `api.qweather.com`)
|
||||||
|
//! - `QWEATHER_JWT_KEY_ID` — JWT Key ID
|
||||||
|
//! - `QWEATHER_JWT_PROJECT_ID` — 项目 ID
|
||||||
|
//! - `QWEATHER_JWT_PRIVATE_KEY_FILE` — Ed25519 私钥路径
|
||||||
|
//!
|
||||||
|
//! ### 缓存策略
|
||||||
|
//! city_id 在内存中缓存 1 小时(TTL),减少重复查询。
|
||||||
|
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|||||||
@@ -1,15 +1,29 @@
|
|||||||
//! Web 搜索工具 — Tavily Search API
|
//! ## Web 搜索工具 —— Tavily Search API
|
||||||
//!
|
//!
|
||||||
//! API: POST https://api.tavily.com/search
|
//! 通过 Tavily 搜索引擎获取互联网实时信息。
|
||||||
//! 认证: Authorization: Bearer <TAVILY_API_KEY>
|
//! Tavily 是一个专为 AI Agent 设计的搜索引擎,返回结构化搜索结果。
|
||||||
//! 文档: https://docs.tavily.com/documentation/api-reference/endpoint/search
|
//!
|
||||||
|
//! ### API
|
||||||
|
//! - 端点: `POST https://api.tavily.com/search`
|
||||||
|
//! - 认证: `Authorization: Bearer <TAVILY_API_KEY>`
|
||||||
|
//! - 文档: https://docs.tavily.com/documentation/api-reference/endpoint/search
|
||||||
|
//!
|
||||||
|
//! ### 功能特性
|
||||||
|
//! - 支持 AI 摘要(include_answer)
|
||||||
|
//! - 支持多种搜索深度(basic / advanced / fast / ultra-fast)
|
||||||
|
//! - 支持按主题过滤(general / news / finance)
|
||||||
|
//! - 支持按时间范围过滤
|
||||||
|
//! - 支持限定/排除域名
|
||||||
|
//! - 支持按国家优先搜索结果
|
||||||
|
|
||||||
use reqwest::Client as HttpClient;
|
use reqwest::Client as HttpClient;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::super::types::{RiskLevel, SkillResult, SkillSpec};
|
use super::super::types::{RiskLevel, SkillResult, SkillSpec};
|
||||||
|
|
||||||
// ─── 请求 ───
|
// ═══════════════════════════════════════════════
|
||||||
|
// 请求结构体
|
||||||
|
// ═══════════════════════════════════════════════
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct SearchRequest {
|
struct SearchRequest {
|
||||||
@@ -73,7 +87,9 @@ struct SearchRequest {
|
|||||||
safe_search: Option<bool>, // Enterprise only
|
safe_search: Option<bool>, // Enterprise only
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── 响应 ───
|
// ═══════════════════════════════════════════════
|
||||||
|
// 响应结构体
|
||||||
|
// ═══════════════════════════════════════════════
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -126,7 +142,9 @@ struct UsageInfo {
|
|||||||
credits: Option<u32>,
|
credits: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── 工具 spec ───
|
// ═══════════════════════════════════════════════
|
||||||
|
// 工具元数据
|
||||||
|
// ═══════════════════════════════════════════════
|
||||||
|
|
||||||
pub fn spec() -> SkillSpec {
|
pub fn spec() -> SkillSpec {
|
||||||
SkillSpec {
|
SkillSpec {
|
||||||
@@ -161,7 +179,9 @@ pub fn spec() -> SkillSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── 执行 ───
|
// ═══════════════════════════════════════════════
|
||||||
|
// 执行入口
|
||||||
|
// ═══════════════════════════════════════════════
|
||||||
|
|
||||||
pub async fn execute(params: serde_json::Value) -> SkillResult {
|
pub async fn execute(params: serde_json::Value) -> SkillResult {
|
||||||
let api_key = match std::env::var("TAVILY_API_KEY") {
|
let api_key = match std::env::var("TAVILY_API_KEY") {
|
||||||
|
|||||||
@@ -23,6 +23,32 @@
|
|||||||
//! → 低风险?→ BuiltinRegistry::execute() 或 SubprocessRunner::execute()
|
//! → 低风险?→ BuiltinRegistry::execute() 或 SubprocessRunner::execute()
|
||||||
//! → 结果返回 LLM 继续对话
|
//! → 结果返回 LLM 继续对话
|
||||||
|
|
||||||
|
//! ## 工具系统 —— 元工具 + 内置工具 + 审批管理
|
||||||
|
//!
|
||||||
|
//! 本模块是 iAs 的"工具系统",让 LLM 能够调用外部功能。
|
||||||
|
//!
|
||||||
|
//! ### 架构设计(两层元工具)
|
||||||
|
//!
|
||||||
|
//! LLM 不直接感知每个具体工具,而是通过两个**元工具**间接调用:
|
||||||
|
//!
|
||||||
|
//! 1. **`query_capabilities`** — 列出所有可用工具的名称、描述、参数格式
|
||||||
|
//! 2. **`call_capability`** — 按名称调用工具,参数通过 JSON 传递
|
||||||
|
//!
|
||||||
|
//! 这样做的优势:新增工具时只需要在服务端注册,不需要修改 LLM 的 tool definitions。
|
||||||
|
//!
|
||||||
|
//! ### 工具类型
|
||||||
|
//! - **内置工具** (builtins/) — Rust 代码实现的工具(天气、搜索、高德地图等)
|
||||||
|
//! - **上下文工具** — read_memories, write_memory, read_summaries(直接在 daemon 中处理)
|
||||||
|
//!
|
||||||
|
//! ### 工具执行流程
|
||||||
|
//! ```text
|
||||||
|
//! LLM 请求 call_capability("query_weather", {"location":"北京"})
|
||||||
|
//! → daemon 解析出 target_name="query_weather",提取参数
|
||||||
|
//! → 高风险?→ 走 ApprovalManager 审批流
|
||||||
|
//! → 低风险?→ BuiltinRegistry::execute()
|
||||||
|
//! → 结果返回 LLM 继续对话
|
||||||
|
//! ```
|
||||||
|
|
||||||
pub mod approval;
|
pub mod approval;
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
//! ## 工具系统类型定义
|
||||||
|
//!
|
||||||
|
//! 定义了工具系统的核心类型:
|
||||||
|
//!
|
||||||
|
//! - `RiskLevel` — 工具的安全级别(Low/High)
|
||||||
|
//! - `SkillSpec` — 工具的元数据描述(名称、参数、风险等级)
|
||||||
|
//! - `SkillResult` — 工具执行结果
|
||||||
|
//! - `ExecutionContext` — 工具执行时的上下文环境
|
||||||
|
//! - `WechatSender` — 微信发送回调类型别名
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
//! ## 微信 iLink Bot API 客户端
|
||||||
|
//!
|
||||||
|
//! 封装了与微信 iLink Bot API 的所有通信:
|
||||||
|
//!
|
||||||
|
//! ### 核心功能
|
||||||
|
//! - **扫码登录** — `get_qrcode()` → `poll_qr_status()` → `login()`
|
||||||
|
//! - **消息接收** — `receive_messages()`(长轮询)
|
||||||
|
//! - **消息发送** — `send_text()`
|
||||||
|
//! - **生命周期** — `notify_start()` / `notify_stop()`
|
||||||
|
//!
|
||||||
|
//! ### 线程安全
|
||||||
|
//! `#[derive(Clone)]` + 内部 `Arc<Mutex<>>` 保证所有方法可以在 tokio task 间共享。
|
||||||
|
//!
|
||||||
|
//! ### 环境变量
|
||||||
|
//! - `WEIXIN_BASE_URL` — API 地址(默认 `https://ilinkai.weixin.qq.com`)
|
||||||
|
|
||||||
use crate::wechat::types::*;
|
use crate::wechat::types::*;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use reqwest::Client as HttpClient;
|
use reqwest::Client as HttpClient;
|
||||||
|
|||||||
@@ -1,2 +1,16 @@
|
|||||||
|
//! ## 微信 iLink Bot 通道
|
||||||
|
//!
|
||||||
|
//! 封装与微信 iLink Bot API 的所有通信:
|
||||||
|
//!
|
||||||
|
//! - `client` — HTTP 客户端(登录、长轮询收消息、发消息、生命周期管理)
|
||||||
|
//! - `types` — API 协议类型定义(消息结构、请求/响应体)
|
||||||
|
//!
|
||||||
|
//! ### 核心流程
|
||||||
|
//! 1. `login()` — 扫码登录,获取 token + account_id
|
||||||
|
//! 2. `notify_start()` — 注册监听器
|
||||||
|
//! 3. `receive_messages()` — 长轮询接收消息
|
||||||
|
//! 4. `send_text()` — 发送文本回复
|
||||||
|
//! 5. `notify_stop()` — 注销监听器
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
@@ -1,3 +1,15 @@
|
|||||||
|
//! ## 微信 iLink Bot API 协议类型
|
||||||
|
//!
|
||||||
|
//! 定义了与微信 iLink Bot API 通信的全部数据结构:
|
||||||
|
//!
|
||||||
|
//! - `BaseInfo` — 公共请求元数据
|
||||||
|
//! - `WeixinMessage` — 微信消息结构(支持 text/image/voice/file/video)
|
||||||
|
//! - `MessageItem` — 消息条目(5 种类型)
|
||||||
|
//! - `GetUpdatesReq/Resp` — 长轮询收消息的请求/响应
|
||||||
|
//! - `SendMessageReq` — 发送消息请求
|
||||||
|
//! - `QRCodeResponse` / `StatusResponse` — 扫码登录相关
|
||||||
|
//! - `LoginResult` / `MessageEvent` — 业务层结果类型
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
// ─── 基础类型 ───
|
// ─── 基础类型 ───
|
||||||
|
|||||||
Reference in New Issue
Block a user