Files
iAs/src/llm/types.rs
T
wunianxiao b9de3665d9 feat: iPet → iAs 完整迁移,Rust 版微信 AI 助手
Phase 1  CLI 框架 + 配置系统
  - clap 子命令: login / listen / send / whoami / usage
  - config.json + env var 替换
  - tracing 日志系统
  - state 持久化(auth/runtime 文件存 + PostgreSQL)

Phase 2  微信通道
  - wechat::client — 完整 iLink Bot HTTP API 实现
  - 扫码登录(终端二维码 + 轮询状态)
  - 长轮询 getupdates / 消息收发 / 监听注册

Phase 3  AI 对话(纯文本 + function calling)
  - LlmProvider trait: DeepSeek + LM Studio 实现
  - SSE 流式解析(text / reasoning / tool_calls delta / usage)
  - Conversation: 消息历史 + chat / chat_with_tools 工具循环

Phase 4  PostgreSQL 集成
  - app_state(认证 KV 存储)
  - chat_records(消息收发记录)
  - llm_usage(Token 用量统计缓存命中率)
  - user_memories(长期记忆持久化)
  - pending_approvals(审批确认码)
  - scheduled_tasks(定时任务表)

Phase 5  一切皆 Skill(工具系统)
  - SkillRegistry: 系统 + 用户 skills 双目录合并
  - SKILL.md 解析器 + 子进程执行器(stdin JSON → stdout)
  - 9 个系统 Skills: datetime / weather / search / email /
    shell / schedule / memos / read_memories / read_summaries
  - ApprovalManager: High 风险技能 → 确认码审批(透明模式)
  - High 风险技能:确认码审批(透明模式)

Phase 6  定时任务调度器

上下文管理
  - ChatSession: checkpoint + token budget (28K) + summaries
  - Token 估算器(中英文自适应)
  - 12h 空闲 → trigger_idle_summary(不入会话)
  - Budget 溢出 → trigger_overflow_summary(入会话 + drain 旧消息)
  - Summarizer: LLM 生成自然语言摘要(fallback 简单截断)
  - 长期记忆 / 摘要 通过 read_memories / read_summaries 工具按需读取

工具调用日志 + Token 统计
  - INFO: 工具名 + 参数 + 结果摘要
  - DEBUG: 子进程 exit/stdout/stderr
  - ias usage --since --until --model 查看用量和缓存命中率
2026-06-01 17:21:43 +08:00

126 lines
3.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use serde::{Deserialize, Serialize};
// ─── 角色 ───
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Role {
System,
User,
Assistant,
Tool,
}
// ─── 消息 ───
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub role: Role,
#[serde(default)]
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ToolCall>>,
}
impl Message {
pub fn system(content: impl Into<String>) -> Self {
Self { role: Role::System, content: content.into(), tool_call_id: None, name: None, tool_calls: None }
}
pub fn user(content: impl Into<String>) -> Self {
Self { role: Role::User, content: content.into(), tool_call_id: None, name: None, tool_calls: None }
}
pub fn assistant(content: impl Into<String>) -> Self {
Self { role: Role::Assistant, content: content.into(), tool_call_id: None, name: None, tool_calls: None }
}
pub fn assistant_with_tool_calls(tool_calls: Vec<ToolCall>) -> Self {
Self { role: Role::Assistant, content: String::new(), tool_call_id: None, name: None, tool_calls: Some(tool_calls) }
}
pub fn tool_result(tool_call_id: &str, name: &str, result: &str) -> Self {
Self { role: Role::Tool, content: result.to_string(), tool_call_id: Some(tool_call_id.to_string()), name: Some(name.to_string()), tool_calls: None }
}
}
// ─── 工具调用 ───
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
#[serde(rename = "type")]
pub call_type: String, // "function"
pub function: ToolFunctionCall,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolFunctionCall {
pub name: String,
pub arguments: String, // JSON string
}
// ─── 流式响应块 ───
#[derive(Debug, Clone)]
pub enum StreamChunk {
/// 文本片段 delta
Text(String),
/// 思考/推理内容(DeepSeek thinking
Reasoning(String),
/// 完成(携带完整文本,以及可选的工具调用)
Done {
text: String,
reasoning: Option<String>,
tool_calls: Option<Vec<ToolCall>>,
usage: Option<Usage>,
},
/// 错误
Error(String),
}
// ─── Token 用量 ───
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Usage {
pub prompt_tokens: u32,
pub completion_tokens: u32,
pub total_tokens: u32,
#[serde(default)]
pub prompt_cache_hit_tokens: u32,
#[serde(default)]
pub prompt_cache_miss_tokens: u32,
}
// ─── 对话配置 ───
#[derive(Debug, Clone)]
pub struct ConversationConfig {
pub system_prompt: String,
pub model: String,
pub temperature: f32,
pub max_tokens: u32,
pub thinking: bool,
/// LLM function calling 的工具定义(JSON 数组)
pub tools: Option<Vec<serde_json::Value>>,
}
impl Default for ConversationConfig {
fn default() -> Self {
Self {
system_prompt: "You are a concise and helpful assistant replying in Chinese. \
Keep replies practical and natural."
.to_string(),
model: "deepseek-v4-flash".to_string(),
temperature: 0.7,
max_tokens: 4096,
thinking: true,
tools: None,
}
}
}