@lutery/vision-mcp 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/README.md +428 -0
- package/dist/adapters/base-adapter.d.ts +69 -0
- package/dist/adapters/base-adapter.d.ts.map +1 -0
- package/dist/adapters/base-adapter.js +143 -0
- package/dist/adapters/base-adapter.js.map +1 -0
- package/dist/adapters/claude-adapter.d.ts +38 -0
- package/dist/adapters/claude-adapter.d.ts.map +1 -0
- package/dist/adapters/claude-adapter.js +251 -0
- package/dist/adapters/claude-adapter.js.map +1 -0
- package/dist/adapters/glm-adapter.d.ts +15 -0
- package/dist/adapters/glm-adapter.d.ts.map +1 -0
- package/dist/adapters/glm-adapter.js +131 -0
- package/dist/adapters/glm-adapter.js.map +1 -0
- package/dist/adapters/modelscope-adapter.d.ts +20 -0
- package/dist/adapters/modelscope-adapter.d.ts.map +1 -0
- package/dist/adapters/modelscope-adapter.js +142 -0
- package/dist/adapters/modelscope-adapter.js.map +1 -0
- package/dist/adapters/openai-adapter.d.ts +20 -0
- package/dist/adapters/openai-adapter.d.ts.map +1 -0
- package/dist/adapters/openai-adapter.js +194 -0
- package/dist/adapters/openai-adapter.js.map +1 -0
- package/dist/adapters/siliconflow-adapter.d.ts +21 -0
- package/dist/adapters/siliconflow-adapter.d.ts.map +1 -0
- package/dist/adapters/siliconflow-adapter.js +145 -0
- package/dist/adapters/siliconflow-adapter.js.map +1 -0
- package/dist/config/model-config.d.ts +39 -0
- package/dist/config/model-config.d.ts.map +1 -0
- package/dist/config/model-config.js +115 -0
- package/dist/config/model-config.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/system.d.ts +75 -0
- package/dist/prompts/system.d.ts.map +1 -0
- package/dist/prompts/system.js +272 -0
- package/dist/prompts/system.js.map +1 -0
- package/dist/providers/provider-registry.d.ts +58 -0
- package/dist/providers/provider-registry.d.ts.map +1 -0
- package/dist/providers/provider-registry.js +173 -0
- package/dist/providers/provider-registry.js.map +1 -0
- package/dist/src/adapters/base-adapter.d.ts +59 -0
- package/dist/src/adapters/base-adapter.d.ts.map +1 -0
- package/dist/src/adapters/base-adapter.js +83 -0
- package/dist/src/adapters/base-adapter.js.map +1 -0
- package/dist/src/adapters/glm-adapter.d.ts +15 -0
- package/dist/src/adapters/glm-adapter.d.ts.map +1 -0
- package/dist/src/adapters/glm-adapter.js +116 -0
- package/dist/src/adapters/glm-adapter.js.map +1 -0
- package/dist/src/adapters/siliconflow-adapter.d.ts +21 -0
- package/dist/src/adapters/siliconflow-adapter.d.ts.map +1 -0
- package/dist/src/adapters/siliconflow-adapter.js +130 -0
- package/dist/src/adapters/siliconflow-adapter.js.map +1 -0
- package/dist/src/config/model-config.d.ts +40 -0
- package/dist/src/config/model-config.d.ts.map +1 -0
- package/dist/src/config/model-config.js +126 -0
- package/dist/src/config/model-config.js.map +1 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +188 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/prompts/system.d.ts +75 -0
- package/dist/src/prompts/system.d.ts.map +1 -0
- package/dist/src/prompts/system.js +272 -0
- package/dist/src/prompts/system.js.map +1 -0
- package/dist/src/tools/vision-tool.d.ts +91 -0
- package/dist/src/tools/vision-tool.d.ts.map +1 -0
- package/dist/src/tools/vision-tool.js +171 -0
- package/dist/src/tools/vision-tool.js.map +1 -0
- package/dist/src/utils/errors.d.ts +65 -0
- package/dist/src/utils/errors.d.ts.map +1 -0
- package/dist/src/utils/errors.js +146 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/image-input.d.ts +45 -0
- package/dist/src/utils/image-input.d.ts.map +1 -0
- package/dist/src/utils/image-input.js +226 -0
- package/dist/src/utils/image-input.js.map +1 -0
- package/dist/src/utils/logger.d.ts +63 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +157 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/test/integration.test.d.ts +10 -0
- package/dist/test/integration.test.d.ts.map +1 -0
- package/dist/test/integration.test.js +270 -0
- package/dist/test/integration.test.js.map +1 -0
- package/dist/test/test-utils.d.ts +45 -0
- package/dist/test/test-utils.d.ts.map +1 -0
- package/dist/test/test-utils.js +107 -0
- package/dist/test/test-utils.js.map +1 -0
- package/dist/test/vision-tool.test.d.ts +9 -0
- package/dist/test/vision-tool.test.d.ts.map +1 -0
- package/dist/test/vision-tool.test.js +167 -0
- package/dist/test/vision-tool.test.js.map +1 -0
- package/dist/tools/vision-tool.d.ts +91 -0
- package/dist/tools/vision-tool.d.ts.map +1 -0
- package/dist/tools/vision-tool.js +167 -0
- package/dist/tools/vision-tool.js.map +1 -0
- package/dist/utils/data-url-parser.d.ts +27 -0
- package/dist/utils/data-url-parser.d.ts.map +1 -0
- package/dist/utils/data-url-parser.js +53 -0
- package/dist/utils/data-url-parser.js.map +1 -0
- package/dist/utils/errors.d.ts +65 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +146 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/image-input.d.ts +45 -0
- package/dist/utils/image-input.d.ts.map +1 -0
- package/dist/utils/image-input.js +238 -0
- package/dist/utils/image-input.js.map +1 -0
- package/dist/utils/logger.d.ts +63 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +157 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/thinking-extractors.d.ts +34 -0
- package/dist/utils/thinking-extractors.d.ts.map +1 -0
- package/dist/utils/thinking-extractors.js +83 -0
- package/dist/utils/thinking-extractors.js.map +1 -0
- package/dist/utils/thinking-filter.d.ts +32 -0
- package/dist/utils/thinking-filter.d.ts.map +1 -0
- package/dist/utils/thinking-filter.js +147 -0
- package/dist/utils/thinking-filter.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Input Normalization Utility
|
|
3
|
+
*
|
|
4
|
+
* @description 统一处理图片输入:支持 URL、base64(data URL) 和本地路径
|
|
5
|
+
*/
|
|
6
|
+
import { readFile } from 'fs/promises';
|
|
7
|
+
import { InvalidInputError, ImageLoadError, VisionMCPError } from './errors.js';
|
|
8
|
+
import { logger } from './logger.js';
|
|
9
|
+
/**
|
|
10
|
+
* 支持的图片 MIME 类型
|
|
11
|
+
* 仅支持 GLM 和 SiliconFlow 明确支持的格式(保守列表)
|
|
12
|
+
* D-002: Only support image input types supported by GLM/SiliconFlow
|
|
13
|
+
*/
|
|
14
|
+
const SUPPORTED_MIME_TYPES = [
|
|
15
|
+
'image/jpeg',
|
|
16
|
+
'image/jpg',
|
|
17
|
+
'image/png',
|
|
18
|
+
'image/webp'
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* 文件扩展名到 MIME 类型的映射
|
|
22
|
+
* 仅支持 GLM 和 SiliconFlow 明确支持的格式
|
|
23
|
+
*/
|
|
24
|
+
const EXT_TO_MIME_TYPE = {
|
|
25
|
+
'.jpg': 'image/jpeg',
|
|
26
|
+
'.jpeg': 'image/jpeg',
|
|
27
|
+
'.png': 'image/png',
|
|
28
|
+
'.webp': 'image/webp'
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* 检测输入类型
|
|
32
|
+
*/
|
|
33
|
+
export function detectInputType(input) {
|
|
34
|
+
// 检测 base64 data URL
|
|
35
|
+
if (input.startsWith('data:image/') && input.includes(';base64,')) {
|
|
36
|
+
return 'base64';
|
|
37
|
+
}
|
|
38
|
+
// 检测 URL(http 或 https)
|
|
39
|
+
if (input.startsWith('http://') || input.startsWith('https://')) {
|
|
40
|
+
return 'url';
|
|
41
|
+
}
|
|
42
|
+
// 其他情况视为本地路径
|
|
43
|
+
return 'local';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 从文件扩展名获取 MIME 类型
|
|
47
|
+
*/
|
|
48
|
+
export function getMimeTypeFromFileName(filename) {
|
|
49
|
+
const ext = filename.toLowerCase().substring(filename.lastIndexOf('.'));
|
|
50
|
+
const mimeType = EXT_TO_MIME_TYPE[ext];
|
|
51
|
+
if (!mimeType) {
|
|
52
|
+
throw new InvalidInputError(`Unsupported file extension: ${ext}`, { filename, supportedExtensions: Object.keys(EXT_TO_MIME_TYPE) });
|
|
53
|
+
}
|
|
54
|
+
return mimeType;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 从 data URL 提取 MIME 类型
|
|
58
|
+
*/
|
|
59
|
+
export function getMimeTypeFromDataUrl(dataUrl) {
|
|
60
|
+
const match = dataUrl.match(/^data:(image\/[^;]+);base64,/i);
|
|
61
|
+
if (!match) {
|
|
62
|
+
throw new InvalidInputError('Invalid data URL format. Expected: data:image/*;base64,...', { dataUrl });
|
|
63
|
+
}
|
|
64
|
+
const mimeType = match[1].toLowerCase();
|
|
65
|
+
if (!SUPPORTED_MIME_TYPES.includes(mimeType)) {
|
|
66
|
+
throw new InvalidInputError(`Unsupported image MIME type: ${mimeType}`, { supportedTypes: SUPPORTED_MIME_TYPES });
|
|
67
|
+
}
|
|
68
|
+
return mimeType;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 检测 URL 是否指向图片文件
|
|
72
|
+
*/
|
|
73
|
+
export function isImageUrl(url) {
|
|
74
|
+
try {
|
|
75
|
+
const urlObj = new URL(url);
|
|
76
|
+
const pathname = urlObj.pathname.toLowerCase();
|
|
77
|
+
return Object.keys(EXT_TO_MIME_TYPE).some(ext => pathname.endsWith(ext));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// 无效的 URL
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 规范化图片输入为统一的 data URL 格式
|
|
86
|
+
*/
|
|
87
|
+
export async function normalizeImageInput(input) {
|
|
88
|
+
const type = detectInputType(input);
|
|
89
|
+
// 默认启用严格 URL 验证(符合 D-002),可通过 VISION_STRICT_URL_VALIDATION=false 禁用
|
|
90
|
+
const strictValidation = process.env.VISION_STRICT_URL_VALIDATION !== 'false';
|
|
91
|
+
logger.debug('Normalizing image input', {
|
|
92
|
+
type,
|
|
93
|
+
inputLength: input.length,
|
|
94
|
+
strictValidation
|
|
95
|
+
});
|
|
96
|
+
try {
|
|
97
|
+
switch (type) {
|
|
98
|
+
case 'base64':
|
|
99
|
+
return await normalizeBase64Input(input);
|
|
100
|
+
case 'url':
|
|
101
|
+
return await normalizeUrlInput(input, strictValidation);
|
|
102
|
+
case 'local':
|
|
103
|
+
return await normalizeLocalInput(input);
|
|
104
|
+
default:
|
|
105
|
+
throw new InvalidInputError(`Unsupported input type: ${type}`, { input, detectedType: type });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (error instanceof VisionMCPError) {
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
throw new ImageLoadError(`Failed to normalize image input`, { input, type }, error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 规范化 base64 data URL 输入
|
|
117
|
+
*/
|
|
118
|
+
async function normalizeBase64Input(input) {
|
|
119
|
+
try {
|
|
120
|
+
// 验证格式
|
|
121
|
+
if (!input.includes(',') || input.split(',').length !== 2) {
|
|
122
|
+
throw new InvalidInputError('Invalid data URL format. Expected: data:image/*;base64,DATA');
|
|
123
|
+
}
|
|
124
|
+
const mimeType = getMimeTypeFromDataUrl(input);
|
|
125
|
+
const dataPart = input.split(',')[1];
|
|
126
|
+
// 验证 base64 数据
|
|
127
|
+
if (!dataPart || dataPart.length === 0) {
|
|
128
|
+
throw new InvalidInputError('No base64 data found in data URL');
|
|
129
|
+
}
|
|
130
|
+
logger.debug('Base64 input validated', { mimeType, dataLength: dataPart.length });
|
|
131
|
+
return {
|
|
132
|
+
type: 'base64',
|
|
133
|
+
originalInput: input,
|
|
134
|
+
dataUrl: input,
|
|
135
|
+
mimeType
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
throw new ImageLoadError('Invalid base64 data URL', { input }, error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 规范化 URL 输入
|
|
144
|
+
*
|
|
145
|
+
* @description 当前直接返回 URL,实际场景中可以下载并转换为 base64
|
|
146
|
+
*/
|
|
147
|
+
async function normalizeUrlInput(input, strictValidation) {
|
|
148
|
+
try {
|
|
149
|
+
// 验证 URL 格式
|
|
150
|
+
const url = new URL(input);
|
|
151
|
+
if (!url.protocol.startsWith('http')) {
|
|
152
|
+
throw new InvalidInputError('Only HTTP/HTTPS URLs are supported', { protocol: url.protocol });
|
|
153
|
+
}
|
|
154
|
+
// 检测是否为图片 URL
|
|
155
|
+
if (!isImageUrl(input)) {
|
|
156
|
+
if (strictValidation) {
|
|
157
|
+
throw new InvalidInputError(`URL does not have a supported image extension (allowed: ${Object.keys(EXT_TO_MIME_TYPE).join(', ')})`, { url: input });
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
logger.warn('URL does not appear to point to an image file', { url: input });
|
|
161
|
+
logger.warn('Set VISION_STRICT_URL_VALIDATION=true to enforce strict validation', { url: input });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
logger.debug('URL input validated', { url: input, strictValidation });
|
|
165
|
+
// 注意:SiliconFlow 和 GLM 都直接支持 URL
|
|
166
|
+
// 这里我们保持为 URL 格式,适配器会处理
|
|
167
|
+
return {
|
|
168
|
+
type: 'url',
|
|
169
|
+
originalInput: input,
|
|
170
|
+
dataUrl: input, // 保持为 URL
|
|
171
|
+
mimeType: 'image/*' // 实际类型由模型确定
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (error instanceof TypeError) {
|
|
176
|
+
throw new InvalidInputError('Invalid URL format', { input }, error);
|
|
177
|
+
}
|
|
178
|
+
throw new ImageLoadError('Failed to process URL input', { input }, error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 规范化本地文件路径输入
|
|
183
|
+
*
|
|
184
|
+
* @description 读取本地文件并转换为 base64 data URL
|
|
185
|
+
*/
|
|
186
|
+
async function normalizeLocalInput(input) {
|
|
187
|
+
try {
|
|
188
|
+
logger.debug('Reading local file', { path: input });
|
|
189
|
+
// 读取文件
|
|
190
|
+
const fileBuffer = await readFile(input);
|
|
191
|
+
if (fileBuffer.length === 0) {
|
|
192
|
+
throw new ImageLoadError('File is empty', { path: input });
|
|
193
|
+
}
|
|
194
|
+
// 检测 MIME 类型
|
|
195
|
+
const mimeType = getMimeTypeFromFileName(input);
|
|
196
|
+
// 转换为 base64
|
|
197
|
+
const base64Data = fileBuffer.toString('base64');
|
|
198
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
199
|
+
logger.debug('Local file converted to base64', {
|
|
200
|
+
path: input,
|
|
201
|
+
mimeType,
|
|
202
|
+
size: fileBuffer.length,
|
|
203
|
+
dataUrlLength: dataUrl.length
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
type: 'local',
|
|
207
|
+
originalInput: input,
|
|
208
|
+
dataUrl,
|
|
209
|
+
mimeType
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
if (error.code === 'ENOENT') {
|
|
214
|
+
throw new ImageLoadError('File not found', { path: input }, error);
|
|
215
|
+
}
|
|
216
|
+
if (error.code === 'EACCES') {
|
|
217
|
+
throw new ImageLoadError('Permission denied reading file', { path: input }, error);
|
|
218
|
+
}
|
|
219
|
+
throw new ImageLoadError('Failed to read local file', { path: input }, error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 获取图片大小信息(用于日志)
|
|
224
|
+
*/
|
|
225
|
+
export function getImageSizeInfo(dataUrl) {
|
|
226
|
+
try {
|
|
227
|
+
const base64Data = dataUrl.includes(',')
|
|
228
|
+
? dataUrl.split(',')[1]
|
|
229
|
+
: dataUrl;
|
|
230
|
+
// 计算大小(base64 编码的原始数据)
|
|
231
|
+
const decodedSize = Math.floor(base64Data.length * 0.75);
|
|
232
|
+
return { size: decodedSize };
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return { size: 0 };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=image-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-input.js","sourceRoot":"","sources":["../../src/utils/image-input.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility
|
|
3
|
+
*
|
|
4
|
+
* @description 统一的日志工具,确保只使用 stdout 输出
|
|
5
|
+
* 符合 MCP 协议要求:stdout 用于 JSON-RPC 通信,日志使用 stderr
|
|
6
|
+
*/
|
|
7
|
+
export declare enum LogLevel {
|
|
8
|
+
DEBUG = 0,
|
|
9
|
+
INFO = 1,
|
|
10
|
+
WARN = 2,
|
|
11
|
+
ERROR = 3
|
|
12
|
+
}
|
|
13
|
+
export interface LogEntry {
|
|
14
|
+
timestamp: string;
|
|
15
|
+
level: keyof typeof LogLevel;
|
|
16
|
+
message: string;
|
|
17
|
+
requestId?: string;
|
|
18
|
+
modelType?: string;
|
|
19
|
+
latency?: number;
|
|
20
|
+
[key: string]: any;
|
|
21
|
+
}
|
|
22
|
+
export declare class Logger {
|
|
23
|
+
private static instance;
|
|
24
|
+
private logLevel;
|
|
25
|
+
private constructor();
|
|
26
|
+
static getInstance(): Logger;
|
|
27
|
+
/**
|
|
28
|
+
* 写入日志到 stderr
|
|
29
|
+
* @param entry 日志条目
|
|
30
|
+
*/
|
|
31
|
+
private write;
|
|
32
|
+
/**
|
|
33
|
+
* 记录 DEBUG 级别日志
|
|
34
|
+
*/
|
|
35
|
+
debug(message: string, meta?: Record<string, any>): void;
|
|
36
|
+
/**
|
|
37
|
+
* 记录 INFO 级别日志
|
|
38
|
+
*/
|
|
39
|
+
info(message: string, meta?: Record<string, any>): void;
|
|
40
|
+
/**
|
|
41
|
+
* 记录 WARN 级别日志
|
|
42
|
+
*/
|
|
43
|
+
warn(message: string, meta?: Record<string, any>): void;
|
|
44
|
+
/**
|
|
45
|
+
* 记录 ERROR 级别日志
|
|
46
|
+
*/
|
|
47
|
+
error(message: string, error?: Error | unknown, meta?: Record<string, any>): void;
|
|
48
|
+
/**
|
|
49
|
+
* 为请求添加上下文信息的日志记录
|
|
50
|
+
*/
|
|
51
|
+
logRequest(message: string, context: {
|
|
52
|
+
requestId?: string;
|
|
53
|
+
modelType?: string;
|
|
54
|
+
latency?: number;
|
|
55
|
+
[key: string]: any;
|
|
56
|
+
}): void;
|
|
57
|
+
}
|
|
58
|
+
export declare const logger: Logger;
|
|
59
|
+
/**
|
|
60
|
+
* 装饰器:记录函数执行时间和错误
|
|
61
|
+
*/
|
|
62
|
+
export declare function withLogging(target: any, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor;
|
|
63
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,OAAO,QAAQ,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAW;IAE3B,OAAO;WAkBO,WAAW,IAAI,MAAM;IAOnC;;;OAGG;IACH,OAAO,CAAC,KAAK;IAKb;;OAEG;IACI,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAW/D;;OAEG;IACI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAW9D;;OAEG;IACI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAW9D;;OAEG;IACI,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAyBxF;;OAEG;IACI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,GAAG,IAAI;CAUT;AAGD,eAAO,MAAM,MAAM,QAAuB,CAAC;AAE3C;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,sBAgC3F"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility
|
|
3
|
+
*
|
|
4
|
+
* @description 统一的日志工具,确保只使用 stdout 输出
|
|
5
|
+
* 符合 MCP 协议要求:stdout 用于 JSON-RPC 通信,日志使用 stderr
|
|
6
|
+
*/
|
|
7
|
+
export var LogLevel;
|
|
8
|
+
(function (LogLevel) {
|
|
9
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
10
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
11
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
12
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
13
|
+
})(LogLevel || (LogLevel = {}));
|
|
14
|
+
export class Logger {
|
|
15
|
+
static instance;
|
|
16
|
+
logLevel;
|
|
17
|
+
constructor() {
|
|
18
|
+
// 从环境变量读取日志级别,默认为 INFO
|
|
19
|
+
const envLevel = process.env.LOG_LEVEL?.toUpperCase();
|
|
20
|
+
switch (envLevel) {
|
|
21
|
+
case 'DEBUG':
|
|
22
|
+
this.logLevel = LogLevel.DEBUG;
|
|
23
|
+
break;
|
|
24
|
+
case 'WARN':
|
|
25
|
+
this.logLevel = LogLevel.WARN;
|
|
26
|
+
break;
|
|
27
|
+
case 'ERROR':
|
|
28
|
+
this.logLevel = LogLevel.ERROR;
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
this.logLevel = LogLevel.INFO;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
static getInstance() {
|
|
35
|
+
if (!Logger.instance) {
|
|
36
|
+
Logger.instance = new Logger();
|
|
37
|
+
}
|
|
38
|
+
return Logger.instance;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 写入日志到 stderr
|
|
42
|
+
* @param entry 日志条目
|
|
43
|
+
*/
|
|
44
|
+
write(entry) {
|
|
45
|
+
// MCP 协议要求:stdout 用于 JSON-RPC,日志必须使用 stderr
|
|
46
|
+
console.error(JSON.stringify(entry));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 记录 DEBUG 级别日志
|
|
50
|
+
*/
|
|
51
|
+
debug(message, meta) {
|
|
52
|
+
if (this.logLevel <= LogLevel.DEBUG) {
|
|
53
|
+
this.write({
|
|
54
|
+
timestamp: new Date().toISOString(),
|
|
55
|
+
level: 'DEBUG',
|
|
56
|
+
message,
|
|
57
|
+
...meta
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 记录 INFO 级别日志
|
|
63
|
+
*/
|
|
64
|
+
info(message, meta) {
|
|
65
|
+
if (this.logLevel <= LogLevel.INFO) {
|
|
66
|
+
this.write({
|
|
67
|
+
timestamp: new Date().toISOString(),
|
|
68
|
+
level: 'INFO',
|
|
69
|
+
message,
|
|
70
|
+
...meta
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 记录 WARN 级别日志
|
|
76
|
+
*/
|
|
77
|
+
warn(message, meta) {
|
|
78
|
+
if (this.logLevel <= LogLevel.WARN) {
|
|
79
|
+
this.write({
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
level: 'WARN',
|
|
82
|
+
message,
|
|
83
|
+
...meta
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 记录 ERROR 级别日志
|
|
89
|
+
*/
|
|
90
|
+
error(message, error, meta) {
|
|
91
|
+
if (this.logLevel <= LogLevel.ERROR) {
|
|
92
|
+
const logEntry = {
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
level: 'ERROR',
|
|
95
|
+
message,
|
|
96
|
+
...meta
|
|
97
|
+
};
|
|
98
|
+
if (error) {
|
|
99
|
+
if (error instanceof Error) {
|
|
100
|
+
logEntry.error = {
|
|
101
|
+
name: error.name,
|
|
102
|
+
message: error.message,
|
|
103
|
+
stack: error.stack
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
logEntry.error = String(error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.write(logEntry);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 为请求添加上下文信息的日志记录
|
|
115
|
+
*/
|
|
116
|
+
logRequest(message, context) {
|
|
117
|
+
const { requestId, modelType, latency, ...rest } = context;
|
|
118
|
+
this.info(message, {
|
|
119
|
+
...(requestId && { requestId }),
|
|
120
|
+
...(modelType && { modelType }),
|
|
121
|
+
...(latency !== undefined && { latency }),
|
|
122
|
+
...rest
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// 导出单例实例
|
|
127
|
+
export const logger = Logger.getInstance();
|
|
128
|
+
/**
|
|
129
|
+
* 装饰器:记录函数执行时间和错误
|
|
130
|
+
*/
|
|
131
|
+
export function withLogging(target, propertyKey, descriptor) {
|
|
132
|
+
const originalMethod = descriptor.value;
|
|
133
|
+
descriptor.value = async function (...args) {
|
|
134
|
+
const startTime = Date.now();
|
|
135
|
+
const requestId = Math.random().toString(36).substring(7);
|
|
136
|
+
logger.debug(`[${propertyKey}] Starting execution`, { requestId });
|
|
137
|
+
try {
|
|
138
|
+
const result = await originalMethod.apply(this, args);
|
|
139
|
+
const latency = Date.now() - startTime;
|
|
140
|
+
logger.logRequest(`[${propertyKey}] Completed successfully`, {
|
|
141
|
+
requestId,
|
|
142
|
+
latency
|
|
143
|
+
});
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
const latency = Date.now() - startTime;
|
|
148
|
+
logger.error(`[${propertyKey}] Execution failed`, error, {
|
|
149
|
+
requestId,
|
|
150
|
+
latency
|
|
151
|
+
});
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
return descriptor;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thinking Extractor Functions
|
|
3
|
+
*
|
|
4
|
+
* @description Independent module for thinking content extraction functions.
|
|
5
|
+
* Separated to avoid circular dependencies with provider-registry.ts.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Re-export the type for backward compatibility
|
|
9
|
+
* This type is defined here to avoid importing thinking-filter.ts
|
|
10
|
+
*/
|
|
11
|
+
export interface ModelResponseEnvelope {
|
|
12
|
+
content: string;
|
|
13
|
+
reasoning?: string;
|
|
14
|
+
thinking?: string;
|
|
15
|
+
rawResponse?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Type for extracting thinking content from raw model responses
|
|
19
|
+
*/
|
|
20
|
+
export type ThinkingExtractor = (rawResponse: unknown) => ModelResponseEnvelope;
|
|
21
|
+
/**
|
|
22
|
+
* Extracts thinking content from GLM-4.6V model responses
|
|
23
|
+
*/
|
|
24
|
+
export declare function extractGLMThinking(rawResponse: unknown): ModelResponseEnvelope;
|
|
25
|
+
/**
|
|
26
|
+
* Extracts thinking content from OpenAI-compatible responses (SiliconFlow)
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractOpenAIThinking(rawResponse: unknown): ModelResponseEnvelope;
|
|
29
|
+
/**
|
|
30
|
+
* Extracts thinking content from Claude Messages API responses
|
|
31
|
+
* Claude response format: { content: [{type: "text", text: "..."}], usage: {...} }
|
|
32
|
+
*/
|
|
33
|
+
export declare function extractClaudeThinking(rawResponse: unknown): ModelResponseEnvelope;
|
|
34
|
+
//# sourceMappingURL=thinking-extractors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thinking-extractors.d.ts","sourceRoot":"","sources":["../../src/utils/thinking-extractors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,WAAW,EAAE,OAAO,KAAK,qBAAqB,CAAC;AAEhF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,OAAO,GAAG,qBAAqB,CAkB9E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,OAAO,GAAG,qBAAqB,CAkBjF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,OAAO,GAAG,qBAAqB,CA4BjF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thinking Extractor Functions
|
|
3
|
+
*
|
|
4
|
+
* @description Independent module for thinking content extraction functions.
|
|
5
|
+
* Separated to avoid circular dependencies with provider-registry.ts.
|
|
6
|
+
*/
|
|
7
|
+
import { logger } from './logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* Extracts thinking content from GLM-4.6V model responses
|
|
10
|
+
*/
|
|
11
|
+
export function extractGLMThinking(rawResponse) {
|
|
12
|
+
try {
|
|
13
|
+
// @ts-ignore - Access response structure
|
|
14
|
+
const choice = rawResponse?.choices?.[0];
|
|
15
|
+
const message = choice?.message || {};
|
|
16
|
+
return {
|
|
17
|
+
content: message.content || '',
|
|
18
|
+
reasoning: message.reasoning || message.thinking || '',
|
|
19
|
+
rawResponse
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
logger.warn('Failed to extract GLM thinking content', { error });
|
|
24
|
+
return {
|
|
25
|
+
content: '',
|
|
26
|
+
rawResponse
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Extracts thinking content from OpenAI-compatible responses (SiliconFlow)
|
|
32
|
+
*/
|
|
33
|
+
export function extractOpenAIThinking(rawResponse) {
|
|
34
|
+
try {
|
|
35
|
+
// @ts-ignore - Access response structure
|
|
36
|
+
const choice = rawResponse?.choices?.[0];
|
|
37
|
+
const message = choice?.message || {};
|
|
38
|
+
return {
|
|
39
|
+
content: message.content || '',
|
|
40
|
+
reasoning: message.reasoning || '',
|
|
41
|
+
rawResponse
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
logger.warn('Failed to extract OpenAI thinking content', { error });
|
|
46
|
+
return {
|
|
47
|
+
content: '',
|
|
48
|
+
rawResponse
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extracts thinking content from Claude Messages API responses
|
|
54
|
+
* Claude response format: { content: [{type: "text", text: "..."}], usage: {...} }
|
|
55
|
+
*/
|
|
56
|
+
export function extractClaudeThinking(rawResponse) {
|
|
57
|
+
try {
|
|
58
|
+
// @ts-ignore - Access response structure
|
|
59
|
+
const contentBlocks = rawResponse?.content || [];
|
|
60
|
+
// 提取所有文本内容
|
|
61
|
+
const textParts = contentBlocks
|
|
62
|
+
.filter((block) => block?.type === 'text')
|
|
63
|
+
.map((block) => block?.text || '')
|
|
64
|
+
.filter(Boolean);
|
|
65
|
+
const content = textParts.join('\n\n');
|
|
66
|
+
// Claude 的 extended thinking 可能返回在 thinking 字段中
|
|
67
|
+
// 但大多数情况下不会单独返回,而是混在 content 中
|
|
68
|
+
// stripThinking 会处理这种情况
|
|
69
|
+
return {
|
|
70
|
+
content,
|
|
71
|
+
reasoning: rawResponse?.thinking || '',
|
|
72
|
+
rawResponse
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
logger.warn('Failed to extract Claude thinking content', { error });
|
|
77
|
+
return {
|
|
78
|
+
content: '',
|
|
79
|
+
rawResponse
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=thinking-extractors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thinking-extractors.js","sourceRoot":"","sources":["../../src/utils/thinking-extractors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAkBrC;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAoB;IACrD,IAAI,CAAC;QACH,yCAAyC;QACzC,MAAM,MAAM,GAAG,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC;QAEtC,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,QAAQ,IAAI,EAAE;YACtD,WAAW;SACZ,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACjE,OAAO;YACL,OAAO,EAAE,EAAE;YACX,WAAW;SACZ,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAoB;IACxD,IAAI,CAAC;QACH,yCAAyC;QACzC,MAAM,MAAM,GAAG,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC;QAEtC,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;YAClC,WAAW;SACZ,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,2CAA2C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,OAAO;YACL,OAAO,EAAE,EAAE;YACX,WAAW;SACZ,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAoB;IACxD,IAAI,CAAC;QACH,yCAAyC;QACzC,MAAM,aAAa,GAAG,WAAW,EAAE,OAAO,IAAI,EAAE,CAAC;QAEjD,WAAW;QACX,MAAM,SAAS,GAAG,aAAa;aAC5B,MAAM,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,CAAC;aAC9C,GAAG,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC;aACtC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEnB,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEvC,gDAAgD;QAChD,+BAA+B;QAC/B,wBAAwB;QACxB,OAAO;YACL,OAAO;YACP,SAAS,EAAG,WAAmB,EAAE,QAAQ,IAAI,EAAE;YAC/C,WAAW;SACZ,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,2CAA2C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,OAAO;YACL,OAAO,EAAE,EAAE;YACX,WAAW;SACZ,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thinking/Reasoning Content Filter
|
|
3
|
+
*
|
|
4
|
+
* @description Filters thinking/reasoning content from model responses to prevent
|
|
5
|
+
* exposing internal reasoning to MCP clients. Supports multiple models and response formats.
|
|
6
|
+
*
|
|
7
|
+
* Architecture Decision: Keep thinking enabled at model level but strip reasoning
|
|
8
|
+
* content before returning to MCP client.
|
|
9
|
+
*/
|
|
10
|
+
import { ModelResponseEnvelope, ThinkingExtractor } from './thinking-extractors.js';
|
|
11
|
+
export type { ModelResponseEnvelope, ThinkingExtractor };
|
|
12
|
+
/**
|
|
13
|
+
* Registry of thinking extractors by model type
|
|
14
|
+
* Uses imported extractor functions to avoid circular dependencies
|
|
15
|
+
*/
|
|
16
|
+
export declare const THINKING_EXTRACTORS: Record<string, ThinkingExtractor>;
|
|
17
|
+
/**
|
|
18
|
+
* Strips thinking content from model response envelope
|
|
19
|
+
* @param envelope - Response envelope that may contain thinking content
|
|
20
|
+
* @returns Cleaned content string with all thinking content removed
|
|
21
|
+
*/
|
|
22
|
+
export declare function stripThinking(envelope: ModelResponseEnvelope): string;
|
|
23
|
+
/**
|
|
24
|
+
* Filters thinking content from raw model response
|
|
25
|
+
* This is the main entry point for filtering thinking content
|
|
26
|
+
*
|
|
27
|
+
* @param rawResponse - The raw response from the model API
|
|
28
|
+
* @param modelType - The type of model (e.g., 'glm-4.6v', 'siliconflow')
|
|
29
|
+
* @returns Cleaned content string with thinking content removed
|
|
30
|
+
*/
|
|
31
|
+
export declare function filterThinkingContent(rawResponse: unknown, modelType: string): string;
|
|
32
|
+
//# sourceMappingURL=thinking-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thinking-filter.d.ts","sourceRoot":"","sources":["../../src/utils/thinking-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EAIlB,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,CAAC;AAsBzD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAOjE,CAAC;AAuCF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAiCrE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAgCrF"}
|