@xtalpi/agentic-lab-agent-sdk 0.0.17
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.md +20 -0
- package/README.md +172 -0
- package/dist/agent/index.d.ts +21 -0
- package/dist/agent/index.js +30 -0
- package/dist/agent/tracer.d.ts +37 -0
- package/dist/agent/tracer.js +217 -0
- package/dist/context/context.d.ts +6 -0
- package/dist/context/context.js +9 -0
- package/dist/file/file.d.ts +25 -0
- package/dist/file/file.js +95 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +53 -0
- package/dist/message/message.d.ts +12 -0
- package/dist/message/message.js +35 -0
- package/dist/message/normalize.d.ts +4 -0
- package/dist/message/normalize.js +11 -0
- package/dist/middleware/createToolErrorCatchMiddleware.d.ts +7 -0
- package/dist/middleware/createToolErrorCatchMiddleware.js +45 -0
- package/dist/middleware/createToolMiddleware.d.ts +8 -0
- package/dist/middleware/createToolMiddleware.js +32 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.js +11 -0
- package/dist/prompt/prompt.d.ts +11 -0
- package/dist/prompt/prompt.js +19 -0
- package/dist/protocal/protocal.d.ts +22 -0
- package/dist/protocal/protocal.js +118 -0
- package/dist/skill/skill.d.ts +14 -0
- package/dist/skill/skill.js +18 -0
- package/dist/stream/stream.d.ts +15 -0
- package/dist/stream/stream.js +38 -0
- package/dist/types/message/enum.d.ts +7 -0
- package/dist/types/message/enum.js +9 -0
- package/dist/types/message/index.d.ts +20 -0
- package/dist/types/message/index.js +1 -0
- package/dist/types/protocal/content/echarts.d.ts +7 -0
- package/dist/types/protocal/content/echarts.js +1 -0
- package/dist/types/protocal/content/fileLink.d.ts +14 -0
- package/dist/types/protocal/content/fileLink.js +9 -0
- package/dist/types/protocal/content/form.d.ts +49 -0
- package/dist/types/protocal/content/form.js +10 -0
- package/dist/types/protocal/content/progress.d.ts +16 -0
- package/dist/types/protocal/content/progress.js +9 -0
- package/dist/types/protocal/content/smiles.d.ts +21 -0
- package/dist/types/protocal/content/smiles.js +15 -0
- package/dist/types/protocal/content/toolCall.d.ts +16 -0
- package/dist/types/protocal/content/toolCall.js +11 -0
- package/dist/types/protocal/enum.d.ts +12 -0
- package/dist/types/protocal/enum.js +14 -0
- package/dist/types/protocal/index.d.ts +21 -0
- package/dist/types/protocal/index.js +1 -0
- package/dist/types/stream/index.d.ts +7 -0
- package/dist/types/stream/index.js +1 -0
- package/dist/types/tracer/index.d.ts +33 -0
- package/dist/types/tracer/index.js +14 -0
- package/dist/utils/error.d.ts +4 -0
- package/dist/utils/error.js +15 -0
- package/dist/utils/utils.d.ts +15 -0
- package/dist/utils/utils.js +42 -0
- package/package.json +72 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# @xtalpi/agentic-lab-agent-sdk
|
|
2
|
+
|
|
3
|
+
用于构建 AI Agent 服务的 Node.js / NestJS 工具包,提供消息处理、SSE 流式响应、协议封装、Prompt 管理、Skill 加载、调用链追踪等开箱即用的能力。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- **消息处理** — 解析聊天参数、加载 & 归一化历史消息
|
|
8
|
+
- **SSE 流式响应** — 一行代码设置 SSE Headers,流式写入数据块
|
|
9
|
+
- **协议层** — 统一的数据协议,内置 FileLink / ToolCall / Progress / SMILES / ECharts / Form 等富内容类型
|
|
10
|
+
- **Prompt 管理** — 从文件系统加载 System Prompt
|
|
11
|
+
- **Skill 管理** — 自动发现 `storage/skills` 目录下的技能
|
|
12
|
+
- **文件工具** — 路径解析、JSON 读写、文件拷贝等常用文件操作
|
|
13
|
+
- **Agent 模块** — 一步完成 Tracer 创建 + onChunk 包装,简化 Agent 运行时初始化
|
|
14
|
+
- **中间件** — Tool Call 状态通知中间件,自动向流中写入工具调用进度
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @xtalpi/agentic-lab-agent-sdk
|
|
20
|
+
# 或
|
|
21
|
+
pnpm add @xtalpi/agentic-lab-agent-sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 环境要求
|
|
25
|
+
|
|
26
|
+
- Node.js >= 22.22.0
|
|
27
|
+
- 可选 peer dependencies:`@langchain/core` >= 1.1.39, `langchain` >= 1.3.0(使用 agent / middleware 时需要)
|
|
28
|
+
|
|
29
|
+
## 快速开始
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { curve, UIEnv } from '@xtalpi/agentic-lab-agent-sdk';
|
|
33
|
+
|
|
34
|
+
// 设置 UI 环境(默认为 Curve)
|
|
35
|
+
curve.setUIEnv(UIEnv.Common); // 所有协议输出自动降级为纯 Markdown
|
|
36
|
+
|
|
37
|
+
const parsed = curve.message.parseChatParams({
|
|
38
|
+
message_id: 'msg-1',
|
|
39
|
+
context_link: 'ctx-1',
|
|
40
|
+
messages: [{ role: 'user', content: 'hello' }],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log(parsed.query); // "hello"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 模块概览
|
|
47
|
+
|
|
48
|
+
所有功能通过唯一的 `curve` 实例访问:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { curve } from '@xtalpi/agentic-lab-agent-sdk';
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### `curve.message`
|
|
55
|
+
|
|
56
|
+
| 方法 | 说明 |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `parseChatParams(params)` | 校验并解析聊天请求参数,自动提取 `query` |
|
|
59
|
+
| `loadMessages(messages, options?)` | 加载 & 归一化历史消息,支持 `maxHistoryMessages` 限制 |
|
|
60
|
+
|
|
61
|
+
### `curve.stream`
|
|
62
|
+
|
|
63
|
+
| 方法 | 说明 |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `setupSSEHeaders(res)` | 设置 Express Response 的 SSE 响应头 |
|
|
66
|
+
| `writeChunk(res, chunk)` | 写入一个 SSE 数据块 |
|
|
67
|
+
| `writeDone(res)` | 发送 `[DONE]` 并结束响应 |
|
|
68
|
+
| `writeError(res, error)` | 写入错误信息 |
|
|
69
|
+
|
|
70
|
+
### `curve.setUIEnv(env)` / `curve.getUIEnv()`
|
|
71
|
+
|
|
72
|
+
切换 UI 环境,影响 `curve.protocol` 所有输出格式:
|
|
73
|
+
|
|
74
|
+
| UIEnv | `output()` 返回 | `toXxxChunk()` 返回 |
|
|
75
|
+
| --- | --- | --- |
|
|
76
|
+
| `UIEnv.Curve` | `CurveChunk` 对象(含 data_type, metadata) | `` ```data JSON``` `` 协议格式 |
|
|
77
|
+
| `UIEnv.Common` | 纯 `string`(Markdown 文本) | 可读的 Markdown 描述文本 |
|
|
78
|
+
|
|
79
|
+
### `curve.protocol`
|
|
80
|
+
|
|
81
|
+
| 方法 | 说明 |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `output(chunk)` | 将 StreamChunk 或 string 转换为 CurveChunk(Curve 模式)或纯 string(Common 模式) |
|
|
84
|
+
| `toFileLinkChunk(...)` | 生成文件链接协议块 |
|
|
85
|
+
| `toToolCallChunk(...)` | 生成工具调用状态协议块 |
|
|
86
|
+
| `toProgressChunk(...)` | 生成进度条协议块 |
|
|
87
|
+
| `toSmilesChunk(...)` | 生成 SMILES 分子式协议块 |
|
|
88
|
+
| `toEChartsChunk(options)` | 生成 ECharts 图表协议块 |
|
|
89
|
+
| `toFormChunk(schema, action?, title?)` | 生成表单协议块 |
|
|
90
|
+
|
|
91
|
+
### `curve.prompt`
|
|
92
|
+
|
|
93
|
+
| 方法 | 说明 |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| `loadPrompt(filePath)` | 从指定路径加载 Prompt 文件 |
|
|
96
|
+
| `systemPrompt()` | 加载 `storage/prompts/system.md` |
|
|
97
|
+
|
|
98
|
+
### `curve.skill`
|
|
99
|
+
|
|
100
|
+
| 方法 | 说明 |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `getSkillDirs()` | 返回 `storage/skills` 下的技能目录路径 |
|
|
103
|
+
|
|
104
|
+
### `curve.file`
|
|
105
|
+
|
|
106
|
+
文件系统常用操作:`isExist`, `isFile`, `readJsonSync`, `writeJson`, `readFileString`, `resolvePath`, `copyFileSync`, `ensureDir` 等。
|
|
107
|
+
|
|
108
|
+
### `curve.utils`
|
|
109
|
+
|
|
110
|
+
通用工具方法:`uuidv4`, `delay`, `parseJsonSilent`, `paramsException`, `commonException` 等。
|
|
111
|
+
|
|
112
|
+
### `curve.agent`
|
|
113
|
+
|
|
114
|
+
Agent 模块,通过 `init()` 一步完成运行时初始化。
|
|
115
|
+
|
|
116
|
+
| 方法 / 属性 | 说明 |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `init(onChunk?, options?)` | 创建 tracer 并包装 onChunk,返回 `{ tracer, onChunk }` |
|
|
119
|
+
| `loadTrace(traceId)` | 加载指定 traceId 的持久化追踪数据 |
|
|
120
|
+
| `Tracer` | `AgentTracer` 类引用 |
|
|
121
|
+
| `traceEventType` | `TraceEventType` 枚举 |
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { curve } from '@xtalpi/agentic-lab-agent-sdk';
|
|
125
|
+
|
|
126
|
+
// 流式场景:一步创建 tracer + 包装 onChunk
|
|
127
|
+
const { tracer, onChunk } = curve.agent.init(_onChunk, { query: 'user question' });
|
|
128
|
+
|
|
129
|
+
// 非流式场景:只需 tracer
|
|
130
|
+
const { tracer } = curve.agent.init(undefined, { query: 'user question' });
|
|
131
|
+
|
|
132
|
+
// 加载历史追踪数据
|
|
133
|
+
const traceData = await curve.agent.loadTrace(traceId);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
追踪数据自动持久化到 `storage/cache/traces/` 目录。
|
|
137
|
+
|
|
138
|
+
### `curve.middleware`
|
|
139
|
+
|
|
140
|
+
中间件模块,提供 Agent 运行时中间件。
|
|
141
|
+
|
|
142
|
+
| 方法 | 说明 |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `createToolMiddleware(tools)` | 创建工具调用通知中间件,自动将 Tool Call 状态写入 SSE 流 |
|
|
145
|
+
| `createErrorCatch()` | 创建错误捕获中间件,拦截工具错误并回传给 LLM |
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { curve } from '@xtalpi/agentic-lab-agent-sdk';
|
|
149
|
+
|
|
150
|
+
const middleware = [
|
|
151
|
+
curve.middleware.createToolMiddleware(tools),
|
|
152
|
+
curve.middleware.createErrorCatch(),
|
|
153
|
+
];
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
## 项目约定
|
|
158
|
+
|
|
159
|
+
SDK 默认从 `process.cwd()` 下读取以下目录结构:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
storage/
|
|
163
|
+
prompts/
|
|
164
|
+
system.md
|
|
165
|
+
skills/
|
|
166
|
+
cache/
|
|
167
|
+
traces/
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
[MIT](./LICENSE.md)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TraceData, TraceEventType } from "../types/tracer/index.js";
|
|
2
|
+
import { AgentTracer } from "./tracer.js";
|
|
3
|
+
|
|
4
|
+
//#region src/agent/index.d.ts
|
|
5
|
+
interface AgentInitResult {
|
|
6
|
+
tracer: AgentTracer;
|
|
7
|
+
onChunk: (chunk: any) => void;
|
|
8
|
+
}
|
|
9
|
+
declare class AgentModule {
|
|
10
|
+
readonly Tracer: typeof AgentTracer;
|
|
11
|
+
readonly traceEventType: typeof TraceEventType;
|
|
12
|
+
private storagePath?;
|
|
13
|
+
init(onChunk?: (chunk: any) => void, options?: {
|
|
14
|
+
query?: string;
|
|
15
|
+
noPrint?: boolean;
|
|
16
|
+
storagePath?: string;
|
|
17
|
+
}): AgentInitResult;
|
|
18
|
+
loadTrace(traceId: string): Promise<TraceData | null>;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { AgentInitResult, AgentModule };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { TraceEventType } from "../types/tracer/index.js";
|
|
2
|
+
import { AgentTracer } from "./tracer.js";
|
|
3
|
+
|
|
4
|
+
//#region src/agent/index.ts
|
|
5
|
+
var AgentModule = class {
|
|
6
|
+
Tracer = AgentTracer;
|
|
7
|
+
traceEventType = TraceEventType;
|
|
8
|
+
storagePath;
|
|
9
|
+
init(onChunk, options) {
|
|
10
|
+
if (options?.storagePath) this.storagePath = options.storagePath;
|
|
11
|
+
const tracer = new AgentTracer(options);
|
|
12
|
+
const wrappedOnChunk = (chunk) => {
|
|
13
|
+
if (chunk && typeof chunk === "object") chunk.metadata = {
|
|
14
|
+
...chunk.metadata || {},
|
|
15
|
+
traceId: tracer.traceId
|
|
16
|
+
};
|
|
17
|
+
onChunk?.(chunk);
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
tracer,
|
|
21
|
+
onChunk: wrappedOnChunk
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async loadTrace(traceId) {
|
|
25
|
+
return AgentTracer.loadTrace(traceId, this.storagePath);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { AgentModule };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { TraceData } from "../types/tracer/index.js";
|
|
2
|
+
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
3
|
+
import { LLMResult } from "@langchain/core/outputs";
|
|
4
|
+
import { BaseMessage } from "@langchain/core/messages";
|
|
5
|
+
|
|
6
|
+
//#region src/agent/tracer.d.ts
|
|
7
|
+
declare class AgentTracer extends BaseCallbackHandler {
|
|
8
|
+
name: string;
|
|
9
|
+
readonly traceId: string;
|
|
10
|
+
private traceData;
|
|
11
|
+
private tracesDir;
|
|
12
|
+
private pendingLLM;
|
|
13
|
+
private pendingTool;
|
|
14
|
+
private noPrint;
|
|
15
|
+
private storagePath?;
|
|
16
|
+
constructor(options?: {
|
|
17
|
+
query?: string;
|
|
18
|
+
noPrint?: boolean;
|
|
19
|
+
storagePath?: string;
|
|
20
|
+
});
|
|
21
|
+
private addEvent;
|
|
22
|
+
private persist;
|
|
23
|
+
private safeStringify;
|
|
24
|
+
private truncate;
|
|
25
|
+
handleChainError(err: any): Promise<void>;
|
|
26
|
+
handleLLMStart(_llm: any, _prompts: string[], runId: string): Promise<void>;
|
|
27
|
+
handleChatModelStart(_llm: any, messages: BaseMessage[][], runId: string): Promise<void>;
|
|
28
|
+
handleLLMEnd(output: LLMResult, runId: string): Promise<void>;
|
|
29
|
+
handleLLMError(err: any, runId: string): Promise<void>;
|
|
30
|
+
handleToolStart(tool: any, input: string, runId: string, _parentRunId?: string, _tags?: string[], _metadata?: Record<string, unknown>, runName?: string): Promise<void>;
|
|
31
|
+
handleToolEnd(output: any, runId: string): Promise<void>;
|
|
32
|
+
handleToolError(err: any, runId: string): Promise<void>;
|
|
33
|
+
getTraceData(): TraceData;
|
|
34
|
+
static loadTrace(traceId: string, storagePath?: string): Promise<TraceData | null>;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { AgentTracer };
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { FileModule } from "../file/file.js";
|
|
2
|
+
import { TraceEventType } from "../types/tracer/index.js";
|
|
3
|
+
import fse from "fs-extra";
|
|
4
|
+
import { v4 } from "uuid";
|
|
5
|
+
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
6
|
+
|
|
7
|
+
//#region src/agent/tracer.ts
|
|
8
|
+
const file = new FileModule();
|
|
9
|
+
function getTracesDir(storagePath) {
|
|
10
|
+
const base = storagePath || file.resolvePath(process.cwd(), "storage");
|
|
11
|
+
return file.resolvePath(base, "cache", "traces");
|
|
12
|
+
}
|
|
13
|
+
var AgentTracer = class extends BaseCallbackHandler {
|
|
14
|
+
name = "AgentTracer";
|
|
15
|
+
traceId;
|
|
16
|
+
traceData;
|
|
17
|
+
tracesDir;
|
|
18
|
+
pendingLLM = /* @__PURE__ */ new Map();
|
|
19
|
+
pendingTool = /* @__PURE__ */ new Map();
|
|
20
|
+
noPrint;
|
|
21
|
+
storagePath;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
super();
|
|
24
|
+
this.traceId = v4();
|
|
25
|
+
this.storagePath = options?.storagePath;
|
|
26
|
+
this.tracesDir = getTracesDir(this.storagePath);
|
|
27
|
+
this.noPrint = options?.noPrint ?? false;
|
|
28
|
+
this.traceData = {
|
|
29
|
+
traceId: this.traceId,
|
|
30
|
+
query: options?.query,
|
|
31
|
+
startTime: Date.now(),
|
|
32
|
+
events: [],
|
|
33
|
+
totalTokens: {
|
|
34
|
+
promptTokens: 0,
|
|
35
|
+
completionTokens: 0,
|
|
36
|
+
totalTokens: 0
|
|
37
|
+
},
|
|
38
|
+
llmCalls: 0,
|
|
39
|
+
toolCalls: 0
|
|
40
|
+
};
|
|
41
|
+
fse.ensureDirSync(this.tracesDir);
|
|
42
|
+
}
|
|
43
|
+
addEvent(event) {
|
|
44
|
+
this.traceData.events.push(event);
|
|
45
|
+
this.persist();
|
|
46
|
+
}
|
|
47
|
+
persist() {
|
|
48
|
+
try {
|
|
49
|
+
if (!fse.existsSync(this.tracesDir)) fse.ensureDirSync(this.tracesDir);
|
|
50
|
+
this.traceData.endTime = Date.now();
|
|
51
|
+
const filePath = file.resolvePath(this.tracesDir, `${this.traceId}.json`);
|
|
52
|
+
fse.writeJsonSync(filePath, this.traceData, { spaces: 2 });
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.error(`[AgentTracer] persist failed: ${err.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
safeStringify(value) {
|
|
58
|
+
try {
|
|
59
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
60
|
+
} catch {
|
|
61
|
+
return String(value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
truncate(text, maxLen = 300) {
|
|
65
|
+
return text.length > maxLen ? text.slice(0, maxLen) + "..." : text;
|
|
66
|
+
}
|
|
67
|
+
async handleChainError(err) {
|
|
68
|
+
!this.noPrint && console.log(`[Tracer] ❌ Chain Error: ${this.truncate(err.message)}\n`);
|
|
69
|
+
this.addEvent({
|
|
70
|
+
type: TraceEventType.ChainError,
|
|
71
|
+
timestamp: Date.now(),
|
|
72
|
+
runId: "",
|
|
73
|
+
data: { error: err.message }
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async handleLLMStart(_llm, _prompts, runId) {
|
|
77
|
+
this.pendingLLM.set(runId, { startTime: Date.now() });
|
|
78
|
+
const inputPreview = _prompts?.[0] ? this.truncate(this.safeStringify(_prompts[0])) : "";
|
|
79
|
+
!this.noPrint && console.log(`[Tracer] 🚀 LLM Start (handleLLMStart)\n input: ${inputPreview}\n`);
|
|
80
|
+
}
|
|
81
|
+
async handleChatModelStart(_llm, messages, runId) {
|
|
82
|
+
this.pendingLLM.set(runId, { startTime: Date.now() });
|
|
83
|
+
this.traceData.llmCalls++;
|
|
84
|
+
const last = messages?.[0]?.[messages[0].length - 1];
|
|
85
|
+
const inputStr = this.safeStringify(last);
|
|
86
|
+
!this.noPrint && console.log(`[Tracer] 🚀 LLM *${this.traceData.llmCalls} Start\n input: ${this.truncate(inputStr)}\n`);
|
|
87
|
+
this.addEvent({
|
|
88
|
+
type: TraceEventType.LlmStart,
|
|
89
|
+
timestamp: Date.now(),
|
|
90
|
+
runId,
|
|
91
|
+
data: {
|
|
92
|
+
llmIndex: this.traceData.llmCalls,
|
|
93
|
+
input: inputStr
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async handleLLMEnd(output, runId) {
|
|
98
|
+
const pending = this.pendingLLM.get(runId);
|
|
99
|
+
if (!pending) return;
|
|
100
|
+
this.pendingLLM.delete(runId);
|
|
101
|
+
const duration = Date.now() - pending.startTime;
|
|
102
|
+
const usage = output?.llmOutput?.tokenUsage || {};
|
|
103
|
+
const gen = output?.generations?.[0]?.[0];
|
|
104
|
+
const msg = gen?.message ?? gen;
|
|
105
|
+
const promptTokens = usage.promptTokens || 0;
|
|
106
|
+
const completionTokens = usage.completionTokens || 0;
|
|
107
|
+
this.traceData.totalTokens.promptTokens += promptTokens;
|
|
108
|
+
this.traceData.totalTokens.completionTokens += completionTokens;
|
|
109
|
+
this.traceData.totalTokens.totalTokens += promptTokens + completionTokens;
|
|
110
|
+
const outputStr = this.safeStringify(msg);
|
|
111
|
+
const durationSec = (duration / 1e3).toFixed(2);
|
|
112
|
+
!this.noPrint && console.log(`[Tracer] ✅ LLM *${this.traceData.llmCalls} End\n duration: ${durationSec}s (${duration}ms)\n tokens: ${promptTokens}+${completionTokens}=${promptTokens + completionTokens}\n output: ${this.truncate(outputStr)}\n`);
|
|
113
|
+
this.addEvent({
|
|
114
|
+
type: TraceEventType.LlmEnd,
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
runId,
|
|
117
|
+
duration,
|
|
118
|
+
data: {
|
|
119
|
+
llmIndex: this.traceData.llmCalls,
|
|
120
|
+
tokenUsage: {
|
|
121
|
+
promptTokens,
|
|
122
|
+
completionTokens
|
|
123
|
+
},
|
|
124
|
+
output: outputStr
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async handleLLMError(err, runId) {
|
|
129
|
+
const p = this.pendingLLM.get(runId);
|
|
130
|
+
if (!p) return;
|
|
131
|
+
this.pendingLLM.delete(runId);
|
|
132
|
+
const duration = Date.now() - p.startTime;
|
|
133
|
+
const durationSec = (duration / 1e3).toFixed(2);
|
|
134
|
+
!this.noPrint && console.log(`[Tracer] ❌ LLM *${this.traceData.llmCalls} Error\n duration: ${durationSec}s (${duration}ms)\n error: ${this.truncate(err.message)}\n`);
|
|
135
|
+
this.addEvent({
|
|
136
|
+
type: TraceEventType.LlmError,
|
|
137
|
+
timestamp: Date.now(),
|
|
138
|
+
runId,
|
|
139
|
+
duration,
|
|
140
|
+
data: { error: err.message }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async handleToolStart(tool, input, runId, _parentRunId, _tags, _metadata, runName) {
|
|
144
|
+
const toolName = runName || tool?.name || tool?.id?.[tool.id?.length - 1] || "unknown";
|
|
145
|
+
this.pendingTool.set(runId, {
|
|
146
|
+
startTime: Date.now(),
|
|
147
|
+
toolName
|
|
148
|
+
});
|
|
149
|
+
this.traceData.toolCalls++;
|
|
150
|
+
!this.noPrint && console.log(`[Tracer][${toolName}] 🔧 Tool Start\n input: ${this.truncate(this.safeStringify(input))}\n`);
|
|
151
|
+
this.addEvent({
|
|
152
|
+
type: TraceEventType.ToolStart,
|
|
153
|
+
timestamp: Date.now(),
|
|
154
|
+
runId,
|
|
155
|
+
data: {
|
|
156
|
+
toolName,
|
|
157
|
+
input: this.safeStringify(input)
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async handleToolEnd(output, runId) {
|
|
162
|
+
const t = this.pendingTool.get(runId);
|
|
163
|
+
if (!t) return;
|
|
164
|
+
this.pendingTool.delete(runId);
|
|
165
|
+
const duration = Date.now() - t.startTime;
|
|
166
|
+
const durationSec = (duration / 1e3).toFixed(2);
|
|
167
|
+
!this.noPrint && console.log(`[Tracer][${t.toolName}] ✅ Tool End\n duration: ${durationSec}s (${duration}ms)\n output: ${this.truncate(this.safeStringify(output))}\n`);
|
|
168
|
+
this.addEvent({
|
|
169
|
+
type: TraceEventType.ToolEnd,
|
|
170
|
+
timestamp: Date.now(),
|
|
171
|
+
runId,
|
|
172
|
+
duration,
|
|
173
|
+
data: {
|
|
174
|
+
toolName: t.toolName,
|
|
175
|
+
output: this.safeStringify(output)
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async handleToolError(err, runId) {
|
|
180
|
+
const t = this.pendingTool.get(runId);
|
|
181
|
+
if (!t) return;
|
|
182
|
+
this.pendingTool.delete(runId);
|
|
183
|
+
const duration = Date.now() - t.startTime;
|
|
184
|
+
const durationSec = (duration / 1e3).toFixed(2);
|
|
185
|
+
!this.noPrint && console.log(`[Tracer][${t.toolName}] ❌ Tool Error\n duration: ${durationSec}s (${duration}ms)\n error: ${this.truncate(err.message)}\n`);
|
|
186
|
+
this.addEvent({
|
|
187
|
+
type: TraceEventType.ToolError,
|
|
188
|
+
timestamp: Date.now(),
|
|
189
|
+
runId,
|
|
190
|
+
duration,
|
|
191
|
+
data: {
|
|
192
|
+
toolName: t.toolName,
|
|
193
|
+
error: err.message
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
getTraceData() {
|
|
198
|
+
return { ...this.traceData };
|
|
199
|
+
}
|
|
200
|
+
static async loadTrace(traceId, storagePath) {
|
|
201
|
+
try {
|
|
202
|
+
const tracesDir = getTracesDir(storagePath);
|
|
203
|
+
if (!fse.existsSync(tracesDir)) {
|
|
204
|
+
fse.ensureDirSync(tracesDir);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
const filePath = file.resolvePath(tracesDir, `${traceId}.json`);
|
|
208
|
+
if (!file.isExist(filePath)) return null;
|
|
209
|
+
return file.readJsonSync(filePath, true);
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
//#endregion
|
|
217
|
+
export { AgentTracer };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/file/file.d.ts
|
|
2
|
+
declare class FileModule {
|
|
3
|
+
isExist(filePath: string): boolean;
|
|
4
|
+
isFile(filePath: string): boolean;
|
|
5
|
+
isWritable(filePath: string): boolean;
|
|
6
|
+
readJsonSync(filePath: string, silent?: boolean): any;
|
|
7
|
+
writeJson(filePath: string, data: any, spaces?: number): Promise<void>;
|
|
8
|
+
readFileString(filePath: string): Promise<string>;
|
|
9
|
+
readFileStringSync(filePath: string): string;
|
|
10
|
+
readFileBufferSync(filePath: string): Buffer;
|
|
11
|
+
resolvePath(...args: any[]): string;
|
|
12
|
+
joinPath(...args: string[]): string;
|
|
13
|
+
getFilename(filePath: string, ext?: string): string;
|
|
14
|
+
dirname(filePath: string): string;
|
|
15
|
+
extname(filePath: string): string;
|
|
16
|
+
isAbsolute(filePath: string): boolean;
|
|
17
|
+
mkdirSync(dir: string): void;
|
|
18
|
+
copy(src: string, dst: string): Promise<void>;
|
|
19
|
+
ensureDir(dir: string): Promise<void>;
|
|
20
|
+
writeFile(filePath: string, data: Buffer | string): Promise<void>;
|
|
21
|
+
readFileBuffer(filePath: string): Promise<Buffer>;
|
|
22
|
+
remove(filePath: string): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { FileModule };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import fse from "fs-extra";
|
|
4
|
+
|
|
5
|
+
//#region src/file/file.ts
|
|
6
|
+
var FileModule = class {
|
|
7
|
+
isExist(filePath) {
|
|
8
|
+
return fs.existsSync(path.resolve(filePath));
|
|
9
|
+
}
|
|
10
|
+
isFile(filePath) {
|
|
11
|
+
if (!this.isExist(filePath)) return false;
|
|
12
|
+
return fs.statSync(filePath).isFile();
|
|
13
|
+
}
|
|
14
|
+
isWritable(filePath) {
|
|
15
|
+
try {
|
|
16
|
+
fs.accessSync(path.resolve(filePath), fs.constants.R_OK | fs.constants.W_OK);
|
|
17
|
+
return true;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
readJsonSync(filePath, silent = false) {
|
|
23
|
+
try {
|
|
24
|
+
if (silent && !this.isFile(path.resolve(filePath))) {
|
|
25
|
+
console.warn(`Not found ${filePath}`);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const jsonObj = fse.readJsonSync(path.resolve(filePath));
|
|
29
|
+
if (!jsonObj) throw new Error("Invalid JSON.");
|
|
30
|
+
return jsonObj;
|
|
31
|
+
} catch (err) {
|
|
32
|
+
if (silent) {
|
|
33
|
+
console.error(err);
|
|
34
|
+
return null;
|
|
35
|
+
} else throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async writeJson(filePath, data, spaces = 2) {
|
|
39
|
+
await fse.writeJson(path.resolve(filePath), data, { spaces });
|
|
40
|
+
}
|
|
41
|
+
readFileString(filePath) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const cb = (err, data) => {
|
|
44
|
+
if (err) reject(err);
|
|
45
|
+
else resolve(data);
|
|
46
|
+
};
|
|
47
|
+
fs.readFile(path.resolve(filePath), "utf8", cb);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
readFileStringSync(filePath) {
|
|
51
|
+
return fs.readFileSync(path.resolve(filePath), "utf-8");
|
|
52
|
+
}
|
|
53
|
+
readFileBufferSync(filePath) {
|
|
54
|
+
return fs.readFileSync(path.resolve(filePath));
|
|
55
|
+
}
|
|
56
|
+
resolvePath(...args) {
|
|
57
|
+
return path.resolve(...args);
|
|
58
|
+
}
|
|
59
|
+
joinPath(...args) {
|
|
60
|
+
return path.join(...args);
|
|
61
|
+
}
|
|
62
|
+
getFilename(filePath, ext) {
|
|
63
|
+
return ext ? path.basename(filePath, ext) : path.basename(filePath);
|
|
64
|
+
}
|
|
65
|
+
dirname(filePath) {
|
|
66
|
+
return path.dirname(filePath);
|
|
67
|
+
}
|
|
68
|
+
extname(filePath) {
|
|
69
|
+
return path.extname(filePath);
|
|
70
|
+
}
|
|
71
|
+
isAbsolute(filePath) {
|
|
72
|
+
return path.isAbsolute(filePath);
|
|
73
|
+
}
|
|
74
|
+
mkdirSync(dir) {
|
|
75
|
+
fs.mkdirSync(path.resolve(dir), { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
async copy(src, dst) {
|
|
78
|
+
await fse.copy(src, dst);
|
|
79
|
+
}
|
|
80
|
+
async ensureDir(dir) {
|
|
81
|
+
await fse.ensureDir(dir);
|
|
82
|
+
}
|
|
83
|
+
async writeFile(filePath, data) {
|
|
84
|
+
await fs.promises.writeFile(path.resolve(filePath), data);
|
|
85
|
+
}
|
|
86
|
+
async readFileBuffer(filePath) {
|
|
87
|
+
return fs.promises.readFile(path.resolve(filePath));
|
|
88
|
+
}
|
|
89
|
+
async remove(filePath) {
|
|
90
|
+
await fse.remove(filePath);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { FileModule };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TraceData, TraceEvent, TraceEventType } from "./types/tracer/index.js";
|
|
2
|
+
import { AgentModule } from "./agent/index.js";
|
|
3
|
+
import { ContextModule } from "./context/context.js";
|
|
4
|
+
import { FileModule } from "./file/file.js";
|
|
5
|
+
import { StreamModule } from "./stream/stream.js";
|
|
6
|
+
import { ProtocolModule } from "./protocal/protocal.js";
|
|
7
|
+
import { MessageModule } from "./message/message.js";
|
|
8
|
+
import { PromptModule } from "./prompt/prompt.js";
|
|
9
|
+
import { SkillModule } from "./skill/skill.js";
|
|
10
|
+
import { UtilsModule } from "./utils/utils.js";
|
|
11
|
+
import { MiddlewareModule } from "./middleware/index.js";
|
|
12
|
+
|
|
13
|
+
//#region src/index.d.ts
|
|
14
|
+
declare enum UIEnv {
|
|
15
|
+
Curve = "Curve",
|
|
16
|
+
Common = "Common",
|
|
17
|
+
}
|
|
18
|
+
declare class Curve {
|
|
19
|
+
readonly stream: StreamModule;
|
|
20
|
+
readonly protocol: ProtocolModule;
|
|
21
|
+
readonly context: ContextModule;
|
|
22
|
+
readonly message: MessageModule;
|
|
23
|
+
readonly prompt: PromptModule;
|
|
24
|
+
readonly skill: SkillModule;
|
|
25
|
+
readonly file: FileModule;
|
|
26
|
+
readonly utils: UtilsModule;
|
|
27
|
+
readonly agent: AgentModule;
|
|
28
|
+
readonly middleware: MiddlewareModule;
|
|
29
|
+
private _uiEnv;
|
|
30
|
+
constructor();
|
|
31
|
+
setUIEnv(env: UIEnv): void;
|
|
32
|
+
getUIEnv(): UIEnv;
|
|
33
|
+
}
|
|
34
|
+
declare const curve: Curve;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { Curve, type TraceData, type TraceEvent, TraceEventType, UIEnv, curve };
|