mm_llm 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 qww
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,201 @@
1
- # Temporary Holding Version
1
+ # mm_llm
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 一个适用于**多用户、多智能体、多会话、多工具**的大模型调用模块,方便打造多智能体系统。
4
+
5
+ - 基于 OpenAI 兼容接口,可对接任意兼容服务(OpenAI、DeepSeek 等)
6
+ - 消息、智能体、工具、用户、选项、配额全部经由可替换的存储层读写
7
+ - 内置内存存储(`Memory`)与 SQLite 存储(`Sqlite`),数据按 `(user_id, agent_id, session_id)` 三元组分区,多用户多智能体互不串台
8
+ - 内置自动工具调用循环、鉴权与配额准入、系统提示固化等能力
9
+ - 关键步骤均为可覆写方法,二次开发者可按需定制任意一环
10
+
11
+ ## 环境要求
12
+
13
+ - Node.js `>= 22.5.0`(`Sqlite` 依赖内置的 `node:sqlite`)
14
+ - 仅使用 `Memory` 时对 Node 版本无额外要求
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ npm install mm_llm
20
+ ```
21
+
22
+ ## 快速开始
23
+
24
+ ```js
25
+ const { LLM } = require('mm_llm');
26
+
27
+ const llm = new LLM({
28
+ api_key: 'sk-xxx',
29
+ base_url: 'https://api.deepseek.com/v1',
30
+ model: 'deepseek-chat'
31
+ });
32
+
33
+ // 配置智能体与用户
34
+ llm.getStore().setAgent(1, { system_prompt: '你是一个简洁的助手' });
35
+ llm.getStore().setQuota(9, 100); // 默认按配额准入,未设置配额的用户会被拒绝,需先开户
36
+
37
+ // 一轮对话
38
+ const reply = await llm.main(
39
+ { user_id: 9 },
40
+ { agent_id: 1, session_id: 100, content: '你好' }
41
+ );
42
+ console.log(reply.content);
43
+ ```
44
+
45
+ ## 核心概念
46
+
47
+ ### LLM
48
+
49
+ 大模型调用类。直接实例化即可使用:默认从内存存储读取会话消息、智能体与用户配置,经 OpenAI 兼容接口完成对话,并把助手回复写回存储。
50
+
51
+ 调用链路:`main` → 鉴权 → 配额准入 → 写入用户输入 → `send` → 装配消息 / 工具 / 选项 → `post` → 写回助手回复。
52
+
53
+ ### 存储(Store)
54
+
55
+ 存储负责六类数据,`Memory` 与 `Sqlite` 实现同一契约、方法一一对应。
56
+
57
+ | 数据 | 键 | 说明 |
58
+ | --- | --- | --- |
59
+ | 会话消息 | `(user_id, agent_id, session_id)` | 三元组分区,避免串台 |
60
+ | 智能体配置 | `agent_id` | `{ system_prompt, tools, users }` |
61
+ | 工具定义 | `tool_name` | 提交给模型的函数声明,供智能体按名引用 |
62
+ | 用户信息 | `user_id` | `{ name, profile }`,用于组装系统提示词 |
63
+ | 智能体选项 | `(user_id, agent_id)` | 同一用户对不同智能体可各有选项 |
64
+ | 用户配额 | `user_id` | 剩余配额,未设置的用户返回 `undefined`,视为无配额而被拒绝 |
65
+
66
+ **LLM 依赖的存储契约**(实现以下方法即可自定义存储):
67
+
68
+ ```js
69
+ getMessages(user_id, agent_id, session_id) -> Array
70
+ setMessages(user_id, agent_id, session_id, messages) -> store
71
+ addMessage(user_id, agent_id, session_id, message) -> store
72
+ delSession(user_id, agent_id, session_id) -> Boolean
73
+ getAgent(agent_id) -> { system_prompt, tools, users }
74
+ getToolSchema(tool_name) -> Object
75
+ setToolSchema(tool_name, schema) -> store
76
+ getUser(user_id) -> { name, profile }
77
+ getOptions(user_id, agent_id) -> Object
78
+ getQuota(user_id) -> Number // 未设置时返回 undefined,视为无配额
79
+ ```
80
+
81
+ `Memory` / `Sqlite` 在此之上还提供配置写入方法:`addMessage` / `addMessages`、`setMessages`、`setAgent` / `delAgent`、`setToolSchema` / `getToolSchema` / `delToolSchema` / `listToolSchemas` / `clearToolSchemas`、`setUser` / `delUser`、`setOptions` / `delOptions`、`setQuota` / `gainQuota` / `delQuota`、`countMessages` / `hasSession` / `listSessions`、`clear`(`Sqlite` 另有 `close`)。
82
+
83
+ > `setAgent` / `setUser` / `setOptions` 均按字段合并,重复设置只覆盖传入的字段。
84
+
85
+ ### 使用 SQLite 持久化
86
+
87
+ ```js
88
+ const { LLM, Sqlite } = require('mm_llm');
89
+
90
+ const llm = new LLM({
91
+ api_key: 'sk-xxx',
92
+ store: new Sqlite('llm.db') // 缺省为 ':memory:',进程退出即丢失
93
+ });
94
+ ```
95
+
96
+ ## 配置项
97
+
98
+ `new LLM(config)` 可传字段如下(`LLM.config` 为默认值):
99
+
100
+ | 字段 | 类型 | 默认值 | 说明 |
101
+ | --- | --- | --- | --- |
102
+ | `api_key` | String | `''` | 服务密钥,缺失时调用抛异常 |
103
+ | `base_url` | String | `https://api.openai.com/v1` | 兼容接口地址 |
104
+ | `model` | String | `gpt-4o` | 模型名 |
105
+ | `temperature` | Number | `0.7` | 采样温度 |
106
+ | `max_tokens` | Number | `8192` | 单次最大生成 token |
107
+ | `stream` | Boolean | `false` | 是否流式返回 |
108
+ | `system_prompt` | String | `''` | 兜底系统提示词 |
109
+ | `max_rounds` | Number | `8` | 工具调用最大轮数 |
110
+ | `auth_default` | String | `allow` | 智能体未限定 `users` 时的默认准入策略 |
111
+ | `store` | Object | `null` | 存储实例,缺省使用 `Memory` |
112
+
113
+ ## API
114
+
115
+ ### 实例方法
116
+
117
+ | 方法 | 说明 |
118
+ | --- | --- |
119
+ | `main(params, ctx, options)` | 执行一次对话。`ctx` 含 `user_id`;`params` 含 `agent_id` / `session_id` / `content`;`options` 为本次调用选项,优先级最高 |
120
+ | `send(user_id, agent_id, session_id)` | 装配上下文后调用模型,`main` 内部使用 |
121
+ | `setStore(store)` / `getStore()` | 设置 / 获取存储实例 |
122
+ | `resetSession(user_id, agent_id, session_id)` | 清空该会话全部消息,下次调用按最新配置重建 system 消息 |
123
+ | `setTool(name, handler)` / `getTool` / `delTool` / `listTools` / `clearTools` | 注册 / 查询 / 删除 / 列举 / 清空工具执行函数 |
124
+ | `checkAuth(user_id, agent_id)` | 鉴权,可覆写 |
125
+ | `checkQuota(user_id, agent_id)` | 配额准入,可覆写 |
126
+ | `getSystemPrompt` / `getBasePrompt` / `getHistory` / `getMessages` / `getOptions` / `getTools` | 数据装配,可覆写或经存储替换 |
127
+ | `useTool(tool_name, params)` / `post(messages, tools, options)` | 工具执行与模型调用,可覆写 |
128
+
129
+ ### 选项优先级
130
+
131
+ 由低到高:实例默认配置 < `(user_id, agent_id)` 级选项 < 本次调用选项。
132
+
133
+ 其中 `(user_id, agent_id)` 级选项的 `system_prompt` 字段存在时,即代替智能体自身的系统提示词。本次调用选项借助 `AsyncLocalStorage` 承载,同一实例并发处理多用户时互不影响。
134
+
135
+ ### 工具调用
136
+
137
+ 工具的执行函数注册在 LLM 实例上(`setTool`),工具定义存于存储(`setToolSchema`),两者按同名关联;智能体通过 `agent.tools`(工具名数组)按名引用,引用的工具名未在存储中定义时抛异常。模型请求工具时由框架自动执行、回填结果并继续对话,直到模型不再请求工具或达到 `max_rounds`。
138
+
139
+ ```js
140
+ llm.setTool('get_weather', (params) => ({ city: params.city, weather: '晴' }));
141
+ llm.getStore().setToolSchema('get_weather', {
142
+ type: 'function',
143
+ function: {
144
+ name: 'get_weather',
145
+ description: '查询指定城市的天气',
146
+ parameters: {
147
+ type: 'object',
148
+ properties: { city: { type: 'string' } },
149
+ required: ['city']
150
+ }
151
+ }
152
+ });
153
+ llm.getStore().setAgent(2, { system_prompt: '你会按需调用工具', tools: ['get_weather'] });
154
+ ```
155
+
156
+ ### 鉴权与配额
157
+
158
+ 调用前先鉴权、再准入,任一不通过即抛异常,既不发送请求也不写入消息。
159
+
160
+ **鉴权**:按智能体配置中的 `users`(允许调用的用户ID数组)判定:
161
+
162
+ - `users` 存在:仅列表内用户可调用,空数组表示谁都不能用;
163
+ - `users` 缺省:由 `auth_default` 决定,`'allow'` 放行(默认)、`'deny'` 拒绝。
164
+
165
+ **配额**:默认可覆写的 `checkQuota` 按用户配额判定,剩余配额大于 0 才放行;未设置配额(返回 `undefined`)视为无配额,同样拒绝。配额扣减等结算动作由二次开发者自行处理。
166
+
167
+ ### 系统提示固化
168
+
169
+ 系统提示(基础提示 + 用户信息段)只在会话首次调用时固化为一条 `system` 消息并置于最前,之后每轮直接从历史读取,使历史前缀保持稳定以命中缓存。需要让新配置生效时调用 `resetSession` 重建会话。
170
+
171
+ ## 扩展方式
172
+
173
+ 1. **注入存储**:`new LLM({ store })`,传入实现上述契约的存储,消息、选项、提示、工具即改由该存储提供;
174
+ 2. **覆写方法**:子类覆写 `getHistory` / `getOptions` / `getSystemPrompt` / `getTools` / `useTool` / `post` / `checkAuth` / `checkQuota`,即可自定义任意一步的数据来源与行为;
175
+ 3. **替换工具实现**:通过 `setTool` 注册的 `handler` 按名覆盖。
176
+
177
+ ## 示例
178
+
179
+ `example` 目录提供九个可独立运行的示例(`demo1.js` ~ `demo9.js`),由 `example/demo.js` 依次调用,均请求真实接口:
180
+
181
+ ```bash
182
+ npm run demo
183
+ ```
184
+
185
+ | 文件 | 主题 |
186
+ | --- | --- |
187
+ | [demo1.js](example/demo1.js) | 基础对话 |
188
+ | [demo2.js](example/demo2.js) | 多用户 / 多智能体隔离 |
189
+ | [demo3.js](example/demo3.js) | 系统提示固化与重置会话 |
190
+ | [demo4.js](example/demo4.js) | 工具调用 |
191
+ | [demo5.js](example/demo5.js) | 用户选项与优先级 |
192
+ | [demo6.js](example/demo6.js) | 替换存储 |
193
+ | [demo7.js](example/demo7.js) | 用户配额准入 |
194
+ | [demo8.js](example/demo8.js) | 连续多轮对话 |
195
+ | [demo9.js](example/demo9.js) | 智能体鉴权 |
196
+
197
+ 运行前请在 `example/helpers.js` 中确认 `DEMO_API_KEY` 可用,并按需调整 `DEMO_BASE_URL` 与 `DEMO_MODEL`(默认对接 DeepSeek)。
198
+
199
+ ## License
200
+
201
+ [ISC](LICENSE)
package/index.js ADDED
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const { LLM } = require('./lib/llm');
4
+ const { Memory } = require('./lib/memory');
5
+ const { Sqlite } = require('./lib/sqlite');
6
+ const Admin = require('mm_corejs').Admin;
7
+
8
+ let admin = new Admin();
9
+ admin.setClass(LLM);
10
+
11
+ /**
12
+ * 获取大模型实例
13
+ * @param {string} scope 作用域
14
+ * @param {Object} config 配置参数
15
+ * @returns {LLM} 大模型实例
16
+ */
17
+ function llmAdmin(scope, config) {
18
+ if (!scope) {
19
+ scope = 'os';
20
+ }
21
+ return admin.get(scope, config);
22
+ }
23
+
24
+ module.exports = {
25
+ LLM,
26
+ Memory,
27
+ Sqlite,
28
+ admin,
29
+ llmAdmin
30
+ };