Saga Reader 0.9.9 版本亮点:深入解析核心新功能实现
Saga Reader 在 0.9.9 版本中迎来了一系列激动人心的更新,显著增强了其功能性、灵活性和用户体验。本次更新的核心亮点包括对更多外部大语言模型(LLM)的支持、引入了经典的 RSS 订阅源、实现了守护进程模式以及增加了用户期待已久的主题切换功能。本文将作为一篇技术博客,深入剖析这些核心功能的实现原理、关键技术点和主要代码实现,帮助开发者和感兴趣的用户更好地理解 Saga Reader 的内部工作机制。
项目介绍:什么是Saga Reader(麒睿智库)
Saga Reader(麒睿智库)是一款基于AI技术的轻量级跨平台阅读器,核心功能涵盖RSS订阅、内容智能抓取、AI内容处理(如翻译、摘要)及本地存储。项目采用Rust(后端)+Svelte(前端)+Tauri(跨平台框架)的技术组合,目标是在老旧设备上实现"低于10MB内存占用"的极致性能,同时提供流畅的用户交互体验。关于Saga Reader的渊源,见《开源我的一款自用AI阅读器,引流Web前端、Rust、Tauri、AI应用开发》。
运行截图

码农开源不易,各位好人路过请给个小星星Star。
关键词:端智能,边缘大模型;Tauri 2.0;桌面端安装包 < 5MB,内存占用 < 20MB。
1. 扩充外部模型:拥抱 OpenAI 兼容生态
为了打破特定 LLM 供应商的限制,Saga Reader 0.9.9 版本引入了对所有兼容 OpenAI API 格式的云端大模型的支持。这意味着用户现在可以灵活接入并使用包括但不限于 Groq、Moonshot AI (Kimi)、Yi a以及其他任何提供标准 OpenAI 接口的 LLM 服务。
实现原理与技术点
此功能的核心在于抽象和泛化。我们没有为每一种新的 LLM 服务都编写一套独立的客户端代码,而是创建了一个通用的服务层,专门处理与 OpenAI 兼容 API 的交互。
通用服务层
OpenAILikeCompletionService我们在
crates/llm/src/providers/llm_openaibase_like.rs文件中定义了OpenAILikeCompletionService。这个结构体封装了发送请求、处理认证和解析响应的通用逻辑。- 动态配置:它通过
OpenAILLMProvider结构(定义于crates/types/src/lib.rs)接收配置,该结构包含了api_base_url、api_key和model_name等关键信息。 - 标准化请求:它使用统一的
RequestParameters结构体来构建请求体,确保与 OpenAI API 的格式完全一致。 - 通用客户端:内部使用
reqwest客户端发送 HTTP POST 请求,并通过Authorization头传入 API Key。
// ...
pub struct OpenAILikeCompletionService {
pub provider: OpenAILLMProvider,
} impl OpenAILikeCompletionService {
pub async fn completion(&self, messages: Vec<Message>) -> Result<String, LLMError> {
// ...
let client = reqwest::Client::new();
let res = client
.post(&self.provider.api_base_url)
.bearer_auth(&self.provider.api_key)
.json(¶ms)
.send()
.await?;
// ...
}
}
- 动态配置:它通过
重构现有服务
原有的
GLMCompletionService(智谱 AI)和MistralQinoAgentService也被重构,改为在内部直接调用OpenAILikeCompletionService。这极大地简化了代码,并统一了所有云端 LLM 的处理逻辑。// ...
impl GLMCompletionService {
pub async fn completion(&self, messages: Vec<Message>) -> Result<String, LLMError> {
let open_ai_like_service = OpenAILikeCompletionService {
provider: OpenAILLMProvider {
// ... 配置 GLM 的特定参数
},
};
open_ai_like_service.completion(messages).await
}
}
前端配置界面
在设置页面 (
app/src/routes/settings/sections/ai.svelte),我们为用户提供了清晰的 UI 来配置 OpenAI 兼容服务的 URL、API Key 和模型名称。这些配置会通过 Tauri 的invoke调用传递给 Rust 后端进行保存和使用。<!-- Svelte code for OpenAI-like provider settings -->
<Input
label="API URL"
bind:value={$llmFormOpenAILikeBaseURI}
error={$llmFormOpenAILikeBaseURIErr}
/>
<Input
label="API Key"
type="password"
bind:value={$llmFormOpenAILikeKey}
error={$llmFormOpenAILikeKeyErr}
/>
<Input
label="Model Name"
bind:value={$llmFormOpenAILikeModelName}
error={$llmFormOpenAILikeModelNameErr}
/>
2. RSS 订阅源支持:回归经典的内容获取方式
除了基于搜索引擎的智能抓取,0.9.9 版本重新引入了对传统 RSS 订阅源的支持,为用户提供了更稳定、更直接的内容订阅渠道。
实现原理与技术点
该功能的实现依赖于一个统一的内容抓取接口和针对不同源类型的具体实现。
统一抓取接口
IFetcher我们在
crates/scrap/src/types.rs中定义了一个IFetchertrait。这个 trait 抽象了所有内容抓取行为,只包含一个核心的fetch方法,它接收一个源地址(URL 或关键词),返回一个文章列表。#[async_trait]
pub trait IFetcher {
async fn fetch(&self, source: &str) -> Result<Vec<Article>>;
}
RSSFetcher的实现在
crates/scrap/src/rss/mod.rs中,我们创建了RSSFetcher结构体并为它实现了IFetchertrait。它使用rsscrate 来解析 RSS feed。- 获取内容:通过
reqwest异步获取 RSS URL 的内容。 - 解析 Feed:使用
rss::Channel::read_from将获取到的 XML 文本解析为结构化的Channel对象。 - 格式化文章:遍历
Channel中的item,将其转换为我们应用内部统一的Article结构。
// ...
use rss::Channel; #[async_trait]
impl IFetcher for RSSFetcher {
async fn fetch(&self, url: &str) -> Result<Vec<Article>> {
let content = reqwest::get(url).await?.bytes().await?;
let channel = Channel::read_from(&content[..])?; let articles = channel.into_items().into_iter().map(|item| {
Article {
title: item.title().unwrap_or_default().to_string(),
url: item.link().unwrap_or_default().to_string(),
// ...
}
}).collect(); Ok(articles)
}
}
- 获取内容:通过
动态选择抓取器
在核心的
update_feed_contents函数 (crates/feed_api_rs/src/features/impl_default.rs) 中,系统会根据订阅源的fetcher_id(rss或scrap)来动态决定使用RSSFetcher还是原有的ScrapProviderEnums(搜索引擎抓取)。这种策略模式的设计使得未来扩展更多类型的内容源变得非常容易。// ...
pub async fn update_feed_contents(&self, ftd: Feed) -> Result<Vec<Article>> {
let articles = match ftd.fetcher_id.as_str() {
"scrap" => self.scrap_provider.fetch(&ftd.url).await?,
"rss" => RSSFetcher::default().fetch(&ftd.url).await?,
_ => vec![],
};
// ...
}
3. 守护进程模式:实现后台静默更新
为了让用户无需时刻打开应用也能及时获取最新资讯,0.9.9 版本引入了守护进程(Daemon)模式。即使在主窗口关闭后,应用依然能在后台静默运行,并定时执行内容更新任务。
实现原理与技术点
此功能主要利用了 Tauri 框架对系统托盘和后台运行的支持。
防止应用完全退出
在
tauri.conf.json中,我们配置了macOSPrivateApi的close_instead_of_quit选项为true。这使得在 macOS 上,当用户点击窗口的关闭按钮时,应用不会完全退出,而是仅仅关闭窗口,主进程继续在后台运行。处理应用重开事件
当应用在后台运行时,如果用户再次点击 Dock 中的图标,我们需要重新显示主窗口。这通过在
crates/tauri-plugin-feed-api/src/lib.rs中监听RunEvent::Reopen事件来实现。当该事件触发时,我们会找到主窗口并调用show()方法。// ...
.on_event(|app_handle, event| {
if let RunEvent::Reopen { .. } = event {
if let Some(window) = app_handle.get_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
})
// ...
后台定时任务
虽然本次 diff 未直接展示定时任务的创建,但守护进程模式为后台定时任务(如定时刷新所有订阅源)的实现奠定了基础。这类任务通常通过在 Rust 后端启动一个独立的线程或使用像
tokio::time::interval这样的异步定时器来实现,它会周期性地调用update_feed_contents函数。
4. 主题切换:个性化的阅读体验
为了提升长时间阅读的舒适度和满足用户的个性化偏好,新版本增加了亮色(Light)和暗色(Dark)主题的切换功能。
实现原理与技术点
该功能的实现是前端技术与 Tauri API 结合的典范。
TailwindCSS 暗色模式
我们在
app/tailwind.config.js中将暗色模式的策略设置为class。这意味着当<html>元素包含dark类名时,所有 Tailwind 的暗色变体(如dark:bg-gray-800,dark:text-white)都会被激活。export default {
// ...
darkMode: 'class',
// ...
};
Svelte 状态管理与 Tauri API
在设置页面 (
app/src/routes/settings/+page.svelte) 中,我们使用 Svelte 的 store 来管理当前的主题状态。switchTheme函数是核心逻辑所在:- 它首先切换本地的
isDarkModeEnabled状态。 - 然后,它根据新的状态向
<html>元素动态添加或移除dark类。 - 最后,它调用 Tauri 的
appWindow.setThemeAPI,将应用窗口本身的主题(如标题栏)也进行同步切换,并持久化用户的选择。
<script lang="ts">
import { appWindow } from '@tauri-apps/api/window';
// ...
let isDarkModeEnabled = false; async function switchTheme() {
isDarkModeEnabled = !isDarkModeEnabled;
const theme = isDarkModeEnabled ? 'dark' : 'light'; if (isDarkModeEnabled) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
} await appWindow.setTheme(theme);
await setTheme(theme);
} onMount(async () => {
const theme = await getTheme();
isDarkModeEnabled = theme === 'dark';
});
</script>
- 它首先切换本地的
总结
Saga Reader 0.9.9 版本的更新是全面且深入的。通过拥抱 OpenAI 兼容生态、回归经典的 RSS、实现后台守护进程以及提供个性化的主题切换,Saga Reader 不仅在功能上更加强大和灵活,也在用户体验上迈出了坚实的一步。这些功能的实现充分展示了 Rust 的高性能、Tauri 框架的跨平台能力以及 Svelte 在构建响应式前端界面方面的优势。我们期待这些新功能能为用户带来更高效、更愉悦的阅读和信息获取体验。
Saga Reader系列技术文章
- 开源我的一款自用AI阅读器,引流Web前端、Rust、Tauri、AI应用开发
- 【实战】深入浅出 Rust 并发:RwLock 与 Mutex 在 Tauri 项目中的实践
- 【实战】Rust与前端协同开发:基于Tauri的跨平台AI阅读器实践
- 揭秘 Saga Reader 智能核心:灵活的多 LLM Provider 集成实践 (Ollama, GLM, Mistral 等)
- Svelte 5 在跨平台 AI 阅读助手中的实践:轻量化前端架构的极致性能优化
- Svelte 5状态管理实战:基于Tauri框架的AI阅读器Saga Reader开发实践
- Svelte 5 状态管理全解析:从响应式核心到项目实战
- 【实战】基于 Tauri 和 Rust 实现基于无头浏览器的高可用网页抓取
-Saga Reader 0.9.9 版本亮点:深入解析核心新功能实现
Saga Reader 0.9.9 版本亮点:深入解析核心新功能实现的更多相关文章
- [译] OpenStack Ocata 版本中的 53 个新功能盘点
原文链接:https://www.mirantis.com/blog/53-new-things-to-look-for-in-openstack-ocata/ 原文作者:Nick Chase, Ra ...
- 【开源】OSharp3.0框架解说系列:新版本说明及新功能规划预览
OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...
- [译] OpenStack Pike 版本中的 53 个新功能盘点
原文:https://www.mirantis.com/blog/53-things-to-look-for-in-openstack-pike/ 作者:Mirantis Nick Chase 发 ...
- IIS6.0,Apache低版本,PHP CGI 解析漏洞
IIS6.0解析漏洞 在IIS6.0下存在这样的文件"名字.asp;名字.jpg" 代表了jpg文件可以以asp脚本类型的文件执行. 根据这个解析漏洞我们可以上传这种名字类型的图片 ...
- FineUIPro/Mvc/Core v5.4.0即将发布(Core基础版,新功能列表)!
FineUIPro/Mvc/Core v5.4.0 即将于 2019-03-04 发布,目前官网示例已更新,先睹为快:http://pro.fineui.com/http://mvc.fineui.c ...
- OpenStack Q版本新功能以及各核心组件功能对比
OpenStack Q版本已经发布了一段时间了.今天, 小编来总结一下OpenStack Q版本核心组件的各项主要新功能, 再来汇总一下最近2年来OpenStack N.O.P.Q各版本核心组件的主要 ...
- Apache Hudi 0.5.1版本重磅发布
历经大约3个月时间,Apache Hudi 社区终于发布了0.5.1版本,这是Apache Hudi发布的第二个Apache版本,该版本中一些关键点如下 版本升级 将Spark版本从2.1.0升级到2 ...
- 【Phylab2.0】Alpha版本项目展示
团队成员 冯炜韬(PM)http://www.cnblogs.com/toka 岳桐宇(后端)http://www.cnblogs.com/mycraftmw 杨子琛(测试&LaTeX)htt ...
- 微信小程序0.11.122100版本新功能解析
微信小程序0.11.122100版本新功能解析 新版本就不再吐槽了,整的自己跟个愤青似的.人老了,喷不动了,把机会留给年轻人吧.下午随着新版本开放,微信居然破天荒的开放了开发者论坛.我很是担心官方 ...
- 完美解决AutoCAD2012,AutoCAD2013本身电脑里有NET4.0或以上版本却装不上的问题
适用情况:电脑里本身有NET4.0或4.5版本,并且正确安装.或本身你就装有AutoCAD2013或AutoCAD2012要装AutoCAD2012或AutoCAD2013却装不上的情况 如图1所示. ...
随机推荐
- MySQL高可用之ProxySQL + MGR 实现读写分离实战
部署MGR 1.MGR 前置介绍 阿里云RDS集群方案用的就是MGR模式! 1.1.什么是 MGR MGR(MySQL Group Replication)是MySQL 5.7.17版本诞生的,是My ...
- vue3 基础-插件 plugin
前几篇我们介绍了 mixin 混入的的方式能实现对代码的复用, 而本篇将要介绍的 plugin 将会更加适合这种通用性功能的代码的复用和扩展. 最常用的场景, 比如轮播图就非常实用 plugin 来实 ...
- Seata源码—5.全局事务的创建与返回处理
大纲 1.Seata开启分布式事务的流程总结 2.Seata生成全局事务ID的雪花算法源码 3.生成xid以及对全局事务会话进行持久化的源码 4.全局事务会话数据持久化的实现源码 5.Seata Se ...
- SpringBoot3整合SpringSecurity6(四)添加用户、密码加密
写在前面 还记得在之前的文章中,我们在user表中手动插入了3条数据吗? 当时,大家就会有疑问.这一串密码是怎么来的呢,我们为啥要对密码进行加密? 带着这些疑问,我们继续上路.我们在开发一个应用系统, ...
- Kafka King 推荐一款漂亮、现代、实用的kafka客户端
Kafka King 一个漂亮.现代.实用的kafka客户端,使用python flet.flutter构建. Github主页:https://github.com/Bronya0/Kafka-Ki ...
- 通过JS模板引擎实现动态模块组件(Vite+JS+Handlebars)
1. 引言 在上一篇文章<实现一个前端动态模块组件(Vite+原生JS)>中,笔者通过原生的JavaScript实现了一个动态的模块组件.但是这个实现并不完善,最大的问题就是功能逻辑并没有 ...
- L1-1、Prompt 是什么?为什么它能“控制 AI”?
*--Prompt 入门 L1-1 想象一下,你只需输入一句话,AI 就能自动为你写一篇文案.生成一份报告.甚至规划你的创业计划.这种"对话即编程"的背后魔法,就是 Prompt ...
- 长短期记忆(LSTM)网络模型
一.概述 长短期记忆(Long Short-Term Memory,LSTM)网络是一种特殊的循环神经网络(RNN),专门设计用于解决传统 RNN 在处理长序列数据时面临的梯度消失 / 爆炸问题, ...
- Springboot笔记<13>单元测试
单元测试 Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库,使用@SpringBootTest注解会以springboot的环境启动 JUnit5常用注解 @Tes ...
- Unity Shader入门精要个人学习笔记
Unity Shader入门精要 渲染流水线 数学基础 1.点和矢量 类型 定义 表达 含义 性质 点(point) 点 (point) 是n 维空间(游戏中主要使用二维和三维空间)中的一个位置,它没 ...