# 🦙 Metona Ollama Client 基于原生 JavaScript 的 [Ollama](https://ollama.com) AI 聊天客户端。零依赖、无构建步骤,开箱即用。 支持流式对话、多模态图片输入、Think 深度推理、历史记录管理,可安装为 PWA 离线使用。 ![版本](https://img.shields.io/badge/version-2.0.0-brightgreen) ![平台](https://img.shields.io/badge/platform-Web-blue) ![协议](https://img.shields.io/badge/license-MIT-green) --- ## ✨ 功能特性 | 功能 | 说明 | |------|------| | 流式对话 | 基于 ReadableStream 的实时打字机效果,支持强制停止 | | 多模型支持 | 自动加载 Ollama 已安装模型,一键切换 | | Think 推理模式 | 可展开/收起的思考过程展示(需模型支持) | | 多模态输入 | 支持图片上传,兼容视觉模型 | | 强制停止 | 真正中止 Ollama 接口请求,双重保险(fetch abort + reader cancel) | | 历史管理 | IndexedDB 持久化,支持搜索、分页、导出 | | 数据导出 | Markdown / HTML / TXT / JSON 多格式 | | PWA 离线 | Service Worker 缓存,可安装到桌面 | | 连接检测 | 实时状态指示,CORS 问题自动提示 | | 显存管理 | 一键卸载模型释放显存 | --- ## 🚀 快速开始 ### 前置条件 1. 安装 [Ollama](https://ollama.com) 并启动服务 2. 下载至少一个模型: ```bash ollama pull qwen2.5 ``` ### 启动 ```bash # 克隆仓库 git clone https://gitee.com/thzxx/metona-ollama.git cd metona-ollama # 方式一:直接用浏览器打开 open index.html # 方式二:用 HTTP 服务器托管 python3 -m http.server 8080 # 访问 http://localhost:8080 ``` ### 跨域配置 如果 Ollama 和页面不在同一域,需要设置环境变量: ```bash OLLAMA_ORIGINS="*" ollama serve ``` --- ## 🔌 Ollama API 调用清单 Metona 通过 `js/ollama-api.js` 封装了 Ollama REST API,以下是所有调用的接口: ### 模型管理 | 接口 | 方法 | 函数 | 调用位置 | 用途 | |------|------|------|----------|------| | `/api/tags` | `GET` | `listModels()` | model-bar.js | 获取已安装模型列表,填充下拉选择框 | | `/api/ps` | `GET` | `psModels()` | header.js | 获取运行中的模型列表,展示在设置面板 | | `/api/version` | `GET` | `getVersion()` | header.js | 获取 Ollama 版本号,用于连接检测和版本显示 | | `/api/show` | `POST` | `showModel(model)` | model-bar.js | 查询模型详情,检测 Think/Vision 等能力 | ### 聊天 | 接口 | 方法 | 函数 | 调用位置 | 用途 | |------|------|------|----------|------| | `/api/chat` | `POST` | `chatStream(params, onChunk, abortController)` | input-area.js | **核心接口**。流式聊天(`stream: true`),通过 NDJSON 流实时返回 AI 回复,支持 AbortController 强制中止 | | `/api/chat` | `POST` | `chat(params)` | settings-modal.js | 非流式聊天(`stream: false`)。用于释放显存:发送空消息并设置 `keep_alive: 0` 强制卸载模型 | | `/api/generate` | `POST` | `generateStream(params, onChunk)` | — (未使用) | 单轮流式生成,预留接口 | | `/api/embed` | `POST` | `embed(model, input)` | — (未使用) | 生成嵌入向量,预留接口 | ### 流式聊天数据格式 (NDJSON) `/api/chat` 流式响应为 NDJSON(每行一个 JSON): ```json {"model":"qwen2.5","message":{"role":"assistant","content":"Hello"},"done":false} {"model":"qwen2.5","message":{"role":"assistant","content":" world"},"done":false} {"model":"qwen2.5","message":{"role":"assistant","content":"!"},"done":true,"eval_count":42,"total_duration":1234567890} ``` ### 聊天请求参数 ```javascript { model: "qwen2.5", // 模型名称 messages: [ // 对话历史 { role: "user", content: "你好", images: ["base64..."] }, { role: "assistant", content: "你好!" } ], stream: true, // 是否流式 think: true, // 是否启用 Think 推理(需模型支持) system: "你是一个助手", // 系统提示词 keep_alive: "5m", // 模型在内存中保持时间 options: { num_ctx: 24576 // 上下文窗口大小(tokens) } } ``` ### 强制停止机制 用户点击停止按钮时的完整流程: ``` 用户点击 ■ → abortController.abort() ├── fetch signal → 浏览器取消 HTTP 连接 → Ollama 停止推理 ├── abort listener → reader.cancel() → 中断 ReadableStream └── catch AbortError → 保留已有内容 → 标记 [已停止] → 恢复 UI ``` --- ## 📁 项目结构 ``` metona-ollama/ ├── index.html # 入口页面(纯 HTML 模板) ├── manifest.json # PWA 清单 ├── sw.js # Service Worker (PWA 缓存) ├── LICENSE ├── README.md │ ├── assets/ │ └── icons/ │ ├── llama.ico # 网站图标 │ └── llama.png # PWA 图标 │ ├── css/ │ └── style.css # 全局样式(暗色主题 + 响应式) │ └── js/ ├── app.js # 主入口:初始化所有组件、事件绑定、生命周期管理 ├── ollama-api.js # Ollama REST API 封装(见上方 API 清单) ├── chat-db.js # IndexedDB 持久化层(会话 CRUD + 设置存储) ├── state.js # 响应式状态管理(发布-订阅模式) ├── utils.js # 工具函数(ID生成、格式化、防抖、Base64) ├── sanitizer.js # HTML XSS 净化器(白名单策略) ├── marked-config.js # Markdown 渲染器配置(自定义链接/图片安全检查) ├── lib/ │ └── marked.esm.js # marked.js v15 (Markdown 解析器) └── components/ ├── chat-area.js # 消息渲染、流式更新、导出功能 ├── input-area.js # 文本输入、图片上传、发送/停止逻辑 ├── history-modal.js # 历史记录面板、搜索、分页 ├── settings-modal.js # 设置面板、模型管理、数据管理 ├── header.js # 顶部导航、连接状态检测 ├── model-bar.js # 模型选择栏、能力检测 ├── toast.js # Toast 通知组件 └── lightbox.js # 图片预览灯箱 ``` --- ## ⚙️ 设置项 | 设置 | 说明 | 默认值 | API 参数 | |------|------|--------|----------| | Ollama 服务地址 | API 端点 | `http://127.0.0.1:11434` | — | | 系统提示词 | 全局 System Prompt | 关闭 | `system` | | 上下文长度 | 上下文窗口大小 | 24576 tokens | `options.num_ctx` | | Think 模式 | 启用深度推理 | 关闭 | `think` | --- ## 📦 数据导出 | 格式 | 说明 | 文件后缀 | |------|------|----------| | Markdown | 单会话导出,便于阅读和编辑 | `.md` | | HTML | 带暗色主题样式的离线页面 | `.html` | | TXT | 纯文本备份 | `.txt` | | JSON | 全量备份/迁移,可跨设备导入 | `.json` | --- ## 🛠️ 技术栈 - **纯原生 JavaScript (ES Modules)** — 零构建步骤,`