p-api-agent 0.0.3 → 0.0.5
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/README.md +204 -208
- package/package.json +18 -2
package/README.md
CHANGED
|
@@ -1,283 +1,289 @@
|
|
|
1
|
-
#
|
|
1
|
+
# p-api-agent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一个轻量、高效、模型无关的 LLM Agent 框架。
|
|
4
|
+
|
|
5
|
+
采用 **ReAct 单调用循环**架构:每轮推理只发起一次 LLM 调用,通过将完整工具 Schema 注入系统提示词,让模型在单次响应中完成「是否用工具 + 选哪个工具 + 填什么参数」三步决策,相比传统多步判断方案减少 60% 以上的 API 调用次数。
|
|
4
6
|
|
|
5
7
|
## 特性
|
|
6
8
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
9
|
+
- ⚡ **ReAct 单调用循环** — 每轮只调用一次 LLM,无冗余中间步骤
|
|
10
|
+
- 🔌 **模型无关** — 只需注册一个 `async (messages) => string` 函数,兼容任意 LLM(OpenAI / Claude / 豆包 / 通义 / DeepSeek …)
|
|
11
|
+
- 🔧 **动态工具注册** — 工具以独立文件方式组织,按目录自动加载
|
|
12
|
+
- 🔗 **链式 API** — 注册方法均返回 `this`,支持链式调用
|
|
13
|
+
- 🛡️ **完善容错** — LLM 重试、工具失败兜底、JSON 解析保护、循环上限防护
|
|
14
|
+
- 📦 **零侵入** — 不绑定任何 LLM SDK,按需引入
|
|
15
|
+
|
|
16
|
+
---
|
|
13
17
|
|
|
14
18
|
## 安装
|
|
15
19
|
|
|
16
20
|
```bash
|
|
17
21
|
npm install p-api-agent
|
|
22
|
+
# 或
|
|
23
|
+
pnpm add p-api-agent
|
|
24
|
+
# 或
|
|
25
|
+
yarn add p-api-agent
|
|
18
26
|
```
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
---
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
## 快速开始
|
|
23
31
|
|
|
24
32
|
```typescript
|
|
25
33
|
import { Agent, FunctionCall } from 'p-api-agent';
|
|
34
|
+
import path from 'path';
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
const agent = new Agent({
|
|
29
|
-
maxLoop: 20, // 最大循环次数
|
|
30
|
-
debug: false // 调试模式
|
|
31
|
-
});
|
|
36
|
+
const agent = new Agent({ maxLoop: 10 });
|
|
32
37
|
|
|
33
|
-
// 注册 LLM
|
|
38
|
+
// 1. 注册 LLM 能力(替换成你自己的 LLM 调用)
|
|
34
39
|
agent.register_llm_text_ability(async (messages) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return response;
|
|
40
|
+
const res = await yourLLMClient.chat(messages);
|
|
41
|
+
return res.content;
|
|
38
42
|
});
|
|
39
43
|
|
|
40
|
-
//
|
|
41
|
-
const
|
|
42
|
-
agent.register_function_call(
|
|
43
|
-
|
|
44
|
-
// 开始对话
|
|
45
|
-
const result = await agent.create_chat('你好,请帮我完成任务');
|
|
46
|
-
console.log(result);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### 创建工具函数
|
|
50
|
-
|
|
51
|
-
在工具目录下创建 `.js` 或 `.ts` 文件:
|
|
44
|
+
// 2. 注册工具函数目录(可选)
|
|
45
|
+
const fc = new FunctionCall(path.join(__dirname, 'tools'));
|
|
46
|
+
agent.register_function_call(fc);
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
name: 'get_weather',
|
|
58
|
-
description: '获取指定城市的天气信息',
|
|
59
|
-
input_schema: {
|
|
60
|
-
type: 'object',
|
|
61
|
-
properties: {
|
|
62
|
-
city: {
|
|
63
|
-
type: 'string',
|
|
64
|
-
description: '城市名称',
|
|
65
|
-
examples: ['北京', '上海', '深圳']
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
required: ['city']
|
|
69
|
-
},
|
|
70
|
-
register_func: async (params: { city: string }) => {
|
|
71
|
-
// 实现您的工具逻辑
|
|
72
|
-
return {
|
|
73
|
-
city: params.city,
|
|
74
|
-
temperature: 25,
|
|
75
|
-
weather: '晴天'
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
}
|
|
48
|
+
// 3. 发起对话
|
|
49
|
+
const result = await agent.create_chat('帮我查一下订单 12345 的收货地址');
|
|
50
|
+
console.log(result.result); // 最终答案
|
|
51
|
+
console.log(result.use_tools); // 调用过的工具记录
|
|
80
52
|
```
|
|
81
53
|
|
|
82
|
-
|
|
54
|
+
支持链式注册:
|
|
83
55
|
|
|
84
56
|
```typescript
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
`
|
|
90
|
-
});
|
|
57
|
+
const result = await new Agent({ maxLoop: 10 })
|
|
58
|
+
.register_llm_text_ability(llmFunc)
|
|
59
|
+
.register_function_call(fc)
|
|
60
|
+
.create_chat('你好');
|
|
91
61
|
```
|
|
92
62
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
### Agent 类
|
|
63
|
+
---
|
|
96
64
|
|
|
97
|
-
|
|
65
|
+
## 工作原理
|
|
98
66
|
|
|
99
|
-
```
|
|
100
|
-
|
|
67
|
+
```
|
|
68
|
+
用户输入
|
|
69
|
+
│
|
|
70
|
+
▼
|
|
71
|
+
构建系统提示词(含所有工具完整 Schema)
|
|
72
|
+
│
|
|
73
|
+
▼ ◄────────────────────────────────────────────────────────┐
|
|
74
|
+
单次 LLM 调用 │
|
|
75
|
+
│ │
|
|
76
|
+
├─ { command: "use_tool", tool_name, params } │
|
|
77
|
+
│ └─ 执行工具 → 结果作为 observation 注入历史 ──────────┘
|
|
78
|
+
│
|
|
79
|
+
├─ { command: "end", result }
|
|
80
|
+
│ └─ 返回最终答案 ✓
|
|
81
|
+
│
|
|
82
|
+
└─ { command: "no_ability", result }
|
|
83
|
+
└─ 返回无法处理说明 ✓
|
|
101
84
|
```
|
|
102
85
|
|
|
103
|
-
|
|
104
|
-
- `maxLoop?: number` - 最大循环次数(默认:20)
|
|
105
|
-
- `debug?: boolean` - 是否启用调试模式(默认:false)
|
|
106
|
-
- `customPresetPrompt?: string` - 自定义预置提示词
|
|
86
|
+
每轮推理固定 **1 次** LLM 调用。工具结果以 `[工具调用结果]` 前缀注入对话历史,模型可在下一轮基于结果继续推理、链式调用多个工具,最终输出 `end` 指令结束。
|
|
107
87
|
|
|
108
|
-
|
|
88
|
+
---
|
|
109
89
|
|
|
110
|
-
|
|
90
|
+
## 创建工具函数
|
|
111
91
|
|
|
112
|
-
|
|
92
|
+
在工具目录下为每个工具创建一个独立文件,导出 `register()` 函数:
|
|
113
93
|
|
|
114
94
|
```typescript
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
95
|
+
// tools/get_weather.ts
|
|
96
|
+
import type { RegisterInfo } from 'p-api-agent';
|
|
97
|
+
|
|
98
|
+
export const register = (): RegisterInfo => ({
|
|
99
|
+
name: 'get_weather',
|
|
100
|
+
description: '获取指定城市的实时天气信息',
|
|
101
|
+
input_schema: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {
|
|
104
|
+
city: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: '城市名称',
|
|
107
|
+
examples: ['北京', '上海', '深圳'],
|
|
108
|
+
},
|
|
109
|
+
date: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
description: '日期,格式 YYYY-MM-DD,不传则默认今天',
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
required: ['city'],
|
|
115
|
+
},
|
|
116
|
+
register_func: async (params: { city: string; date?: string }) => {
|
|
117
|
+
// 实现你的业务逻辑
|
|
118
|
+
return { city: params.city, temperature: 25, weather: '晴' };
|
|
119
|
+
},
|
|
118
120
|
});
|
|
119
121
|
```
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
`register_func` 接收 LLM 生成的参数对象,返回值会被序列化后注入对话上下文供模型读取。
|
|
122
124
|
|
|
123
|
-
|
|
125
|
+
---
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
const functionCall = new FunctionCall('/path/to/tools');
|
|
127
|
-
agent.register_function_call(functionCall);
|
|
128
|
-
```
|
|
127
|
+
## API 文档
|
|
129
128
|
|
|
130
|
-
|
|
129
|
+
### `new Agent(options?)`
|
|
131
130
|
|
|
132
|
-
|
|
131
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
132
|
+
|---|---|---|---|
|
|
133
|
+
| `maxLoop` | `number` | `20` | 最大推理循环轮次,防止无限循环 |
|
|
134
|
+
| `retryTimes` | `number` | `2` | LLM 调用失败时的重试次数 |
|
|
135
|
+
| `customSystemPrompt` | `string` | — | 完全覆盖内置系统提示词 |
|
|
133
136
|
|
|
134
|
-
|
|
135
|
-
const result = await agent.create_chat('用户输入');
|
|
136
|
-
// 或使用消息数组
|
|
137
|
-
const result = await agent.create_chat([
|
|
138
|
-
{
|
|
139
|
-
role: 'user',
|
|
140
|
-
content: [{ type: 'text', text: '用户输入' }]
|
|
141
|
-
}
|
|
142
|
-
]);
|
|
143
|
-
```
|
|
137
|
+
---
|
|
144
138
|
|
|
145
|
-
|
|
139
|
+
### `agent.register_llm_text_ability(func)`
|
|
146
140
|
|
|
147
|
-
|
|
141
|
+
注册 LLM 文字调用函数,返回 `this`。
|
|
148
142
|
|
|
149
143
|
```typescript
|
|
150
|
-
agent.
|
|
144
|
+
agent.register_llm_text_ability(
|
|
145
|
+
async (messages: Message[]) => Promise<string>
|
|
146
|
+
)
|
|
151
147
|
```
|
|
152
148
|
|
|
153
|
-
|
|
149
|
+
`messages` 格式遵循 OpenAI Chat Completions 标准,兼容绝大多数主流 LLM。
|
|
154
150
|
|
|
155
|
-
|
|
151
|
+
---
|
|
156
152
|
|
|
157
|
-
|
|
158
|
-
agent.set_preset_prompt('自定义提示词内容');
|
|
159
|
-
```
|
|
153
|
+
### `agent.register_function_call(fc)`
|
|
160
154
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
#### 构造函数
|
|
155
|
+
注册工具函数集合,返回 `this`。`FunctionCall` 会自动扫描目录下所有文件并调用 `register()` 加载。
|
|
164
156
|
|
|
165
157
|
```typescript
|
|
166
|
-
new FunctionCall(
|
|
158
|
+
const fc = new FunctionCall('/absolute/path/to/tools');
|
|
159
|
+
agent.register_function_call(fc);
|
|
167
160
|
```
|
|
168
161
|
|
|
169
|
-
|
|
170
|
-
- `toolPath` - 工具函数所在目录的绝对路径
|
|
171
|
-
|
|
172
|
-
#### 方法
|
|
162
|
+
---
|
|
173
163
|
|
|
174
|
-
|
|
164
|
+
### `agent.create_chat(input)`
|
|
175
165
|
|
|
176
|
-
|
|
166
|
+
发起一轮对话,返回 `Promise<CreateChatResult>`。
|
|
177
167
|
|
|
178
168
|
```typescript
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
```
|
|
169
|
+
// 字符串输入
|
|
170
|
+
const result = await agent.create_chat('查询用户 123 的信息');
|
|
182
171
|
|
|
183
|
-
|
|
172
|
+
// 消息数组输入(携带多轮历史)
|
|
173
|
+
const result = await agent.create_chat([
|
|
174
|
+
{ role: 'user', content: [{ type: 'text', text: '你好' }] },
|
|
175
|
+
{ role: 'assistant', content: [{ type: 'text', text: '你好!' }] },
|
|
176
|
+
{ role: 'user', content: [{ type: 'text', text: '帮我查一下...' }] },
|
|
177
|
+
]);
|
|
178
|
+
```
|
|
184
179
|
|
|
185
|
-
|
|
180
|
+
**返回值:**
|
|
186
181
|
|
|
187
182
|
```typescript
|
|
188
|
-
|
|
183
|
+
interface CreateChatResult {
|
|
184
|
+
result: string // 最终返回给用户的文本答案
|
|
185
|
+
use_tools: { // 本次对话中调用过的工具记录
|
|
186
|
+
tool_name: string
|
|
187
|
+
params: any
|
|
188
|
+
exec_result: any
|
|
189
|
+
}[]
|
|
190
|
+
}
|
|
189
191
|
```
|
|
190
192
|
|
|
191
|
-
|
|
193
|
+
---
|
|
192
194
|
|
|
193
|
-
|
|
195
|
+
### `agent.set_system_prompt(prompt)`
|
|
194
196
|
|
|
195
|
-
|
|
196
|
-
const result = await functionCall.exec_function('get_weather', { city: '北京' });
|
|
197
|
-
```
|
|
197
|
+
运行时覆盖系统提示词,返回 `this`。**注意:** 覆盖后工具列表不再自动注入,需在自定义提示词中手动声明。
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
---
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
import { Logger, LogLevel } from 'p-api-agent';
|
|
201
|
+
### `agent.set_max_loop(n)`
|
|
203
202
|
|
|
204
|
-
|
|
205
|
-
Logger.setLevel(LogLevel.DEBUG);
|
|
203
|
+
动态调整最大循环轮次,返回 `this`。
|
|
206
204
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### `new FunctionCall(toolPath)`
|
|
208
|
+
|
|
209
|
+
| 方法 | 说明 |
|
|
210
|
+
|---|---|
|
|
211
|
+
| `get_tools_list()` | 返回工具名称 + 描述列表 |
|
|
212
|
+
| `get_tools_with_schema()` | 返回工具完整 Schema(含参数定义) |
|
|
213
|
+
| `gen_tool_doc(name)` | 生成单个工具的可读说明文档字符串 |
|
|
214
|
+
| `exec_function(name, params)` | 执行指定工具,返回工具执行结果 |
|
|
215
|
+
|
|
216
|
+
---
|
|
213
217
|
|
|
214
|
-
|
|
218
|
+
## 类型定义
|
|
215
219
|
|
|
216
220
|
```typescript
|
|
217
|
-
import {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
import type {
|
|
222
|
+
AgentOptions,
|
|
223
|
+
UserChatInput,
|
|
224
|
+
Message,
|
|
225
|
+
CreateChatResult,
|
|
226
|
+
ToolRecord,
|
|
227
|
+
RegisterInfo,
|
|
224
228
|
} from 'p-api-agent';
|
|
225
|
-
|
|
226
|
-
try {
|
|
227
|
-
await agent.create_chat('...');
|
|
228
|
-
} catch (error) {
|
|
229
|
-
if (error instanceof ToolNotFoundError) {
|
|
230
|
-
console.error('工具函数不存在');
|
|
231
|
-
} else if (error instanceof MaxLoopExceededError) {
|
|
232
|
-
console.error('超出最大循环次数');
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
229
|
```
|
|
236
230
|
|
|
237
|
-
|
|
231
|
+
---
|
|
238
232
|
|
|
239
|
-
|
|
240
|
-
2. **LLM 分析** → LLM 判断是否需要调用工具
|
|
241
|
-
3. **工具调用** → 如需要,先获取工具文档,再执行工具
|
|
242
|
-
4. **结果处理** → 整合工具结果,返回给用户
|
|
243
|
-
5. **循环控制** → 最多循环 maxLoop 次,防止无限循环
|
|
233
|
+
## 完整示例(OpenAI)
|
|
244
234
|
|
|
245
|
-
|
|
235
|
+
```typescript
|
|
236
|
+
import { Agent, FunctionCall } from 'p-api-agent';
|
|
237
|
+
import OpenAI from 'openai';
|
|
238
|
+
import path from 'path';
|
|
239
|
+
|
|
240
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
241
|
+
|
|
242
|
+
const agent = new Agent({ maxLoop: 15, retryTimes: 3 })
|
|
243
|
+
.register_llm_text_ability(async (messages) => {
|
|
244
|
+
const res = await openai.chat.completions.create({
|
|
245
|
+
model: 'gpt-4o',
|
|
246
|
+
messages: messages as any,
|
|
247
|
+
});
|
|
248
|
+
return res.choices[0].message.content ?? '';
|
|
249
|
+
})
|
|
250
|
+
.register_function_call(
|
|
251
|
+
new FunctionCall(path.join(__dirname, 'tools'))
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const { result, use_tools } = await agent.create_chat(
|
|
255
|
+
'帮我查询订单 12345 的购买者信息和商品价格'
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
console.log('答案:', result);
|
|
259
|
+
console.log('调用了工具:', use_tools.map(t => t.tool_name));
|
|
260
|
+
```
|
|
246
261
|
|
|
247
|
-
|
|
262
|
+
---
|
|
248
263
|
|
|
249
|
-
|
|
250
|
-
```json
|
|
251
|
-
{
|
|
252
|
-
"command": "end",
|
|
253
|
-
"result": "最终返回给用户的结果"
|
|
254
|
-
}
|
|
255
|
-
```
|
|
264
|
+
## 自定义系统提示词
|
|
256
265
|
|
|
257
|
-
|
|
258
|
-
```json
|
|
259
|
-
{
|
|
260
|
-
"command": "get_tool_doc",
|
|
261
|
-
"tool_name": "工具函数名称"
|
|
262
|
-
}
|
|
263
|
-
```
|
|
266
|
+
如需完全控制 Agent 人设,使用 `customSystemPrompt` 选项:
|
|
264
267
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
```typescript
|
|
269
|
+
const agent = new Agent({
|
|
270
|
+
customSystemPrompt: `
|
|
271
|
+
你是一个专业的电商客服助手,负责处理订单查询和退换货问题。
|
|
272
|
+
|
|
273
|
+
## 指令格式
|
|
274
|
+
每次只能输出以下 JSON 之一,不能附带多余文字:
|
|
275
|
+
- 调用工具: {"command":"use_tool","tool_name":"工具名","params":{...}}
|
|
276
|
+
- 任务完成: {"command":"end","result":"最终答案"}
|
|
277
|
+
- 无法处理: {"command":"no_ability","result":"原因说明"}
|
|
278
|
+
|
|
279
|
+
## 可用工具
|
|
280
|
+
- get_order_info: 根据订单号查询订单详情
|
|
281
|
+
- get_user_info: 根据用户 ID 查询用户信息
|
|
282
|
+
`.trim(),
|
|
283
|
+
});
|
|
272
284
|
```
|
|
273
285
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
1. **错误处理**: 始终使用 try-catch 包裹 Agent 调用
|
|
277
|
-
2. **日志记录**: 生产环境建议设置为 INFO 或 WARN 级别
|
|
278
|
-
3. **循环次数**: 根据实际需求调整 maxLoop,避免过多循环
|
|
279
|
-
4. **工具设计**: 工具函数应该功能单一、职责明确
|
|
280
|
-
5. **参数验证**: 在工具函数中进行充分的参数验证
|
|
286
|
+
---
|
|
281
287
|
|
|
282
288
|
## 开发
|
|
283
289
|
|
|
@@ -288,22 +294,12 @@ npm install
|
|
|
288
294
|
# 构建
|
|
289
295
|
npm run build
|
|
290
296
|
|
|
291
|
-
#
|
|
292
|
-
|
|
297
|
+
# 运行示例
|
|
298
|
+
npx tsx examples/test.ts
|
|
293
299
|
```
|
|
294
300
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
ISC
|
|
298
|
-
|
|
299
|
-
## 贡献
|
|
301
|
+
---
|
|
300
302
|
|
|
301
|
-
|
|
303
|
+
## License
|
|
302
304
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
### v0.0.1
|
|
306
|
-
- 初始版本发布
|
|
307
|
-
- 支持基本的 Agent 功能
|
|
308
|
-
- 工具函数调用
|
|
309
|
-
- 完善的类型定义和错误处理
|
|
305
|
+
ISC
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "p-api-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"main": "./dist/index.cjs",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"files": [
|
|
@@ -18,7 +18,23 @@
|
|
|
18
18
|
},
|
|
19
19
|
"author": "",
|
|
20
20
|
"license": "ISC",
|
|
21
|
-
"description": "",
|
|
21
|
+
"description": "一个轻量、高效、模型无关的 LLM Agent 框架,采用 ReAct 单调用循环架构",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/p-shoko/p-api-agent.git"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/p-shoko/p-api-agent#readme",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/p-shoko/p-api-agent/issues"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"agent",
|
|
32
|
+
"llm",
|
|
33
|
+
"react",
|
|
34
|
+
"function-call",
|
|
35
|
+
"openai",
|
|
36
|
+
"ai"
|
|
37
|
+
],
|
|
22
38
|
"devDependencies": {
|
|
23
39
|
"@types/lodash": "4.17.16",
|
|
24
40
|
"@types/node": "^25.3.5",
|