@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,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Adapter
|
|
3
|
+
*
|
|
4
|
+
* @description OpenAI API 适配器实现,支持 GPT-4o、GPT-4 Vision 等视觉模型
|
|
5
|
+
*/
|
|
6
|
+
import { BaseVisionModelAdapter } from './base-adapter.js';
|
|
7
|
+
import { ModelAPIError, ModelConfigError } from '../utils/errors.js';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
export class OpenAIAdapter extends BaseVisionModelAdapter {
|
|
10
|
+
options;
|
|
11
|
+
constructor(config, options = {}) {
|
|
12
|
+
super(config);
|
|
13
|
+
if (config.type !== 'openai') {
|
|
14
|
+
throw new ModelConfigError('Invalid model type. Expected: openai', { expected: 'openai', received: config.type });
|
|
15
|
+
}
|
|
16
|
+
this.options = {
|
|
17
|
+
maxTokens: 2048,
|
|
18
|
+
temperature: 0.7,
|
|
19
|
+
...options
|
|
20
|
+
};
|
|
21
|
+
logger.info('Initialized OpenAI adapter', {
|
|
22
|
+
model: config.name,
|
|
23
|
+
baseUrl: config.baseUrl,
|
|
24
|
+
options: this.options
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async analyze(imageData, prompt) {
|
|
28
|
+
logger.logRequest('Analyzing image with OpenAI', {
|
|
29
|
+
modelType: 'openai',
|
|
30
|
+
model: this.config.name,
|
|
31
|
+
imageLength: imageData.length
|
|
32
|
+
});
|
|
33
|
+
try {
|
|
34
|
+
return await this.withRetry(async (signal) => {
|
|
35
|
+
const response = await this.callOpenAIAPI(imageData, prompt, signal);
|
|
36
|
+
logger.logRequest('OpenAI analysis completed', {
|
|
37
|
+
modelType: 'openai',
|
|
38
|
+
model: this.config.name,
|
|
39
|
+
responseLength: response.content.length
|
|
40
|
+
});
|
|
41
|
+
return response.content;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
logger.error('OpenAI analysis failed', error);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async analyzeWithResponse(imageData, prompt) {
|
|
50
|
+
logger.logRequest('Analyzing image with OpenAI (full response)', {
|
|
51
|
+
modelType: 'openai',
|
|
52
|
+
model: this.config.name,
|
|
53
|
+
imageLength: imageData.length
|
|
54
|
+
});
|
|
55
|
+
try {
|
|
56
|
+
return await this.withRetry(async (signal) => {
|
|
57
|
+
const response = await this.callOpenAIAPI(imageData, prompt, signal);
|
|
58
|
+
logger.logRequest('OpenAI analysis completed', {
|
|
59
|
+
modelType: 'openai',
|
|
60
|
+
model: this.config.name,
|
|
61
|
+
responseLength: response.content.length,
|
|
62
|
+
usage: response.usage
|
|
63
|
+
});
|
|
64
|
+
return response;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
logger.error('OpenAI analysis failed', error);
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async callOpenAIAPI(imageData, prompt, signal) {
|
|
73
|
+
// 构建 API 端点(移除末尾斜杠后添加 /chat/completions)
|
|
74
|
+
const baseUrl = this.config.baseUrl.replace(/\/$/, '');
|
|
75
|
+
const apiUrl = `${baseUrl}/chat/completions`;
|
|
76
|
+
// 构建请求体(OpenAI Chat Completions 格式)
|
|
77
|
+
const requestBody = {
|
|
78
|
+
model: this.config.name,
|
|
79
|
+
messages: [
|
|
80
|
+
{
|
|
81
|
+
role: 'user',
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: 'text',
|
|
85
|
+
text: prompt
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: 'image_url',
|
|
89
|
+
image_url: {
|
|
90
|
+
url: imageData
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
stream: false, // 强制非流式响应
|
|
97
|
+
// 注意:使用 max_tokens 参数
|
|
98
|
+
// - 适用于 gpt-4o、gpt-4-vision-preview 等主流模型
|
|
99
|
+
// - 对于 reasoning/o 系列模型,可能需要使用 max_completion_tokens
|
|
100
|
+
// - 未来可根据模型名称动态选择参数
|
|
101
|
+
max_tokens: this.options.maxTokens,
|
|
102
|
+
temperature: this.options.temperature
|
|
103
|
+
};
|
|
104
|
+
logger.debug('Calling OpenAI API', {
|
|
105
|
+
apiUrl,
|
|
106
|
+
model: this.config.name,
|
|
107
|
+
maxTokens: this.options.maxTokens,
|
|
108
|
+
temperature: this.options.temperature
|
|
109
|
+
});
|
|
110
|
+
const response = await fetch(apiUrl, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: {
|
|
113
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
114
|
+
'Content-Type': 'application/json',
|
|
115
|
+
'Accept': 'application/json'
|
|
116
|
+
},
|
|
117
|
+
body: JSON.stringify(requestBody),
|
|
118
|
+
signal
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
const rawErrorText = await response.text().catch(() => 'Failed to get error details');
|
|
122
|
+
// 截断过长的错误信息(最多 500 字符)
|
|
123
|
+
const MAX_ERROR_LENGTH = 500;
|
|
124
|
+
const errorText = rawErrorText.length > MAX_ERROR_LENGTH
|
|
125
|
+
? `${rawErrorText.slice(0, MAX_ERROR_LENGTH)}... (truncated)`
|
|
126
|
+
: rawErrorText;
|
|
127
|
+
// 尝试解析 OpenAI 标准错误格式
|
|
128
|
+
let errorMessage = `OpenAI API request failed: ${response.status} ${response.statusText}`;
|
|
129
|
+
let errorCode;
|
|
130
|
+
let guidance;
|
|
131
|
+
try {
|
|
132
|
+
const errorJson = JSON.parse(rawErrorText);
|
|
133
|
+
if (errorJson.error) {
|
|
134
|
+
errorMessage = errorJson.error.message || errorMessage;
|
|
135
|
+
errorCode = errorJson.error.code;
|
|
136
|
+
guidance = errorJson.error.type;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// 不是 JSON 格式,使用原始文本
|
|
141
|
+
}
|
|
142
|
+
// 特殊处理 429 限流
|
|
143
|
+
if (response.status === 429) {
|
|
144
|
+
const retryAfter = response.headers.get('retry-after');
|
|
145
|
+
const retryAfterText = retryAfter ? `Retry after ${retryAfter}s. ` : '';
|
|
146
|
+
throw new ModelAPIError(`Rate limit exceeded (429). ${retryAfterText}Please check your API quota or wait before retrying.`, {
|
|
147
|
+
status: response.status,
|
|
148
|
+
statusText: response.statusText,
|
|
149
|
+
errorCode,
|
|
150
|
+
errorDetails: errorText,
|
|
151
|
+
endpoint: apiUrl,
|
|
152
|
+
retryAfter: retryAfter,
|
|
153
|
+
guidance: guidance || 'Check your API quota or wait before retrying'
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// 其他错误
|
|
157
|
+
throw new ModelAPIError(errorMessage, {
|
|
158
|
+
status: response.status,
|
|
159
|
+
statusText: response.statusText,
|
|
160
|
+
errorCode,
|
|
161
|
+
errorDetails: errorText,
|
|
162
|
+
endpoint: apiUrl,
|
|
163
|
+
guidance
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// JSON 解析带兜底处理
|
|
167
|
+
let responseData;
|
|
168
|
+
const rawText = await response.text();
|
|
169
|
+
try {
|
|
170
|
+
responseData = JSON.parse(rawText);
|
|
171
|
+
}
|
|
172
|
+
catch (parseError) {
|
|
173
|
+
// 截断原始响应体用于诊断(最多 500 字符)
|
|
174
|
+
const truncatedBody = rawText.slice(0, 500);
|
|
175
|
+
const bodyPreview = rawText.length > 500
|
|
176
|
+
? `${truncatedBody}... (truncated, total length: ${rawText.length})`
|
|
177
|
+
: rawText;
|
|
178
|
+
logger.error('Failed to parse successful response as JSON', parseError, {
|
|
179
|
+
contentType: response.headers.get('content-type'),
|
|
180
|
+
bodyLength: rawText.length,
|
|
181
|
+
bodyPreview
|
|
182
|
+
});
|
|
183
|
+
throw new ModelAPIError('Invalid JSON response from successful request', {
|
|
184
|
+
status: response.status,
|
|
185
|
+
statusText: response.statusText,
|
|
186
|
+
endpoint: apiUrl,
|
|
187
|
+
errorDetails: bodyPreview,
|
|
188
|
+
parseError: parseError instanceof Error ? parseError.message : String(parseError)
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return this.parseResponse(responseData, 'openai');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=openai-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-adapter.js","sourceRoot":"","sources":["../../src/adapters/openai-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SiliconFlow Adapter
|
|
3
|
+
*
|
|
4
|
+
* @description SiliconFlow 模型适配器实现,支持多种视觉模型(Qwen2-VL、InternVL 等)
|
|
5
|
+
*/
|
|
6
|
+
import { BaseVisionModelAdapter } from './base-adapter.js';
|
|
7
|
+
import { ModelConfig } from '../config/model-config.js';
|
|
8
|
+
import { VisionModelResponse } from './base-adapter.js';
|
|
9
|
+
export interface SiliconFlowAdapterOptions {
|
|
10
|
+
detail?: 'low' | 'high' | 'auto';
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
temperature?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class SiliconFlowAdapter extends BaseVisionModelAdapter {
|
|
15
|
+
private options;
|
|
16
|
+
constructor(config: ModelConfig, options?: SiliconFlowAdapterOptions);
|
|
17
|
+
analyze(imageData: string, prompt: string): Promise<string>;
|
|
18
|
+
analyzeWithResponse(imageData: string, prompt: string): Promise<VisionModelResponse>;
|
|
19
|
+
private callSiliconFlowAPI;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=siliconflow-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"siliconflow-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/siliconflow-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,kBAAmB,SAAQ,sBAAsB;IAC5D,OAAO,CAAC,OAAO,CAA4B;gBAE/B,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,yBAA8B;IAqBlE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAyB3D,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;YA0B5E,kBAAkB;CAsFjC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SiliconFlow Adapter
|
|
3
|
+
*
|
|
4
|
+
* @description SiliconFlow 模型适配器实现,支持多种视觉模型(Qwen2-VL、InternVL 等)
|
|
5
|
+
*/
|
|
6
|
+
import { BaseVisionModelAdapter } from './base-adapter.js';
|
|
7
|
+
import { ModelAPIError } from '../utils/errors.js';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
export class SiliconFlowAdapter extends BaseVisionModelAdapter {
|
|
10
|
+
options;
|
|
11
|
+
constructor(config, options = {}) {
|
|
12
|
+
super(config);
|
|
13
|
+
if (config.type !== 'siliconflow') {
|
|
14
|
+
throw new Error('Invalid model type. Expected: siliconflow');
|
|
15
|
+
}
|
|
16
|
+
this.options = {
|
|
17
|
+
detail: 'auto',
|
|
18
|
+
maxTokens: 2048,
|
|
19
|
+
temperature: 0.7,
|
|
20
|
+
...options
|
|
21
|
+
};
|
|
22
|
+
logger.info('Initialized SiliconFlow adapter', {
|
|
23
|
+
model: config.name,
|
|
24
|
+
baseUrl: config.baseUrl,
|
|
25
|
+
options: this.options
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async analyze(imageData, prompt) {
|
|
29
|
+
logger.logRequest('Analyzing image with SiliconFlow', {
|
|
30
|
+
modelType: 'siliconflow',
|
|
31
|
+
model: this.config.name,
|
|
32
|
+
imageLength: imageData.length
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
return await this.withRetry(async (signal) => {
|
|
36
|
+
const response = await this.callSiliconFlowAPI(imageData, prompt, signal);
|
|
37
|
+
logger.logRequest('SiliconFlow analysis completed', {
|
|
38
|
+
modelType: 'siliconflow',
|
|
39
|
+
model: this.config.name,
|
|
40
|
+
responseLength: response.content.length
|
|
41
|
+
});
|
|
42
|
+
return response.content;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
logger.error('SiliconFlow analysis failed', error);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async analyzeWithResponse(imageData, prompt) {
|
|
51
|
+
logger.logRequest('Analyzing image with SiliconFlow (full response)', {
|
|
52
|
+
modelType: 'siliconflow',
|
|
53
|
+
model: this.config.name,
|
|
54
|
+
imageLength: imageData.length
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
return await this.withRetry(async (signal) => {
|
|
58
|
+
const response = await this.callSiliconFlowAPI(imageData, prompt, signal);
|
|
59
|
+
logger.logRequest('SiliconFlow analysis completed', {
|
|
60
|
+
modelType: 'siliconflow',
|
|
61
|
+
model: this.config.name,
|
|
62
|
+
responseLength: response.content.length,
|
|
63
|
+
usage: response.usage
|
|
64
|
+
});
|
|
65
|
+
return response;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
logger.error('SiliconFlow analysis failed', error);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async callSiliconFlowAPI(imageData, prompt, signal) {
|
|
74
|
+
// 构建请求 URL
|
|
75
|
+
const apiUrl = `${this.config.baseUrl}/chat/completions`;
|
|
76
|
+
// 构建请求体(OpenAI 兼容格式)
|
|
77
|
+
const requestBody = {
|
|
78
|
+
model: this.config.name,
|
|
79
|
+
messages: [
|
|
80
|
+
{
|
|
81
|
+
role: 'user',
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: 'text',
|
|
85
|
+
text: prompt
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: 'image_url',
|
|
89
|
+
image_url: {
|
|
90
|
+
url: imageData,
|
|
91
|
+
detail: this.options.detail || 'auto'
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
stream: false, // 显式禁用流式响应
|
|
98
|
+
max_tokens: this.options.maxTokens,
|
|
99
|
+
temperature: this.options.temperature
|
|
100
|
+
};
|
|
101
|
+
logger.debug('Calling SiliconFlow API', {
|
|
102
|
+
apiUrl,
|
|
103
|
+
model: this.config.name,
|
|
104
|
+
detail: this.options.detail,
|
|
105
|
+
maxTokens: this.options.maxTokens,
|
|
106
|
+
temperature: this.options.temperature
|
|
107
|
+
});
|
|
108
|
+
const response = await fetch(apiUrl, {
|
|
109
|
+
method: 'POST',
|
|
110
|
+
headers: {
|
|
111
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
112
|
+
'Content-Type': 'application/json',
|
|
113
|
+
'Accept': 'application/json'
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify(requestBody),
|
|
116
|
+
signal
|
|
117
|
+
});
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
const errorText = await response.text().catch(() => 'Failed to get error details');
|
|
120
|
+
// Special handling for rate limiting
|
|
121
|
+
if (response.status === 429) {
|
|
122
|
+
const retryAfter = response.headers.get('retry-after');
|
|
123
|
+
const retryAfterText = retryAfter ? `Retry after ${retryAfter}s. ` : '';
|
|
124
|
+
throw new ModelAPIError(`Rate limit exceeded (429). ${retryAfterText}Please check your API quota or wait before retrying.`, {
|
|
125
|
+
status: response.status,
|
|
126
|
+
statusText: response.statusText,
|
|
127
|
+
errorDetails: errorText,
|
|
128
|
+
endpoint: apiUrl,
|
|
129
|
+
retryAfter: retryAfter,
|
|
130
|
+
guidance: 'Check your API quota or wait before retrying'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// Handle other errors normally
|
|
134
|
+
throw new ModelAPIError(`SiliconFlow API request failed: ${response.status} ${response.statusText}`, {
|
|
135
|
+
status: response.status,
|
|
136
|
+
statusText: response.statusText,
|
|
137
|
+
errorDetails: errorText,
|
|
138
|
+
endpoint: apiUrl
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const responseData = await response.json();
|
|
142
|
+
return this.parseResponse(responseData, 'siliconflow');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=siliconflow-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"siliconflow-adapter.js","sourceRoot":"","sources":["../../src/adapters/siliconflow-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Configuration Manager
|
|
3
|
+
*
|
|
4
|
+
* @description 管理视觉模型的配置,从环境变量加载
|
|
5
|
+
*/
|
|
6
|
+
export interface ModelConfig {
|
|
7
|
+
type: string;
|
|
8
|
+
name: string;
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
apiKey: string;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
maxRetries?: number;
|
|
13
|
+
thinking?: {
|
|
14
|
+
type: 'enabled' | 'disabled';
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface ModelDefaults {
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
modelName: string;
|
|
20
|
+
timeout: number;
|
|
21
|
+
maxRetries: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 从环境变量加载模型配置
|
|
25
|
+
*/
|
|
26
|
+
export declare function loadModelConfig(): ModelConfig;
|
|
27
|
+
/**
|
|
28
|
+
* 验证模型配置
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateModelConfig(config: ModelConfig): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* 脱敏显示 API Key
|
|
33
|
+
*/
|
|
34
|
+
export declare function maskApiKey(apiKey: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* 获取模型的显示信息(用于日志)
|
|
37
|
+
*/
|
|
38
|
+
export declare function getModelDisplayInfo(config: ModelConfig): string;
|
|
39
|
+
//# sourceMappingURL=model-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-config.d.ts","sourceRoot":"","sources":["../../src/config/model-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAsE7C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAiChE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE/D"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Configuration Manager
|
|
3
|
+
*
|
|
4
|
+
* @description 管理视觉模型的配置,从环境变量加载
|
|
5
|
+
*/
|
|
6
|
+
import { ModelConfigError } from '../utils/errors.js';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
import { registry } from '../providers/provider-registry.js';
|
|
9
|
+
/**
|
|
10
|
+
* 从环境变量加载模型配置
|
|
11
|
+
*/
|
|
12
|
+
export function loadModelConfig() {
|
|
13
|
+
try {
|
|
14
|
+
// 加载模型类型
|
|
15
|
+
const type = process.env.VISION_MODEL_TYPE;
|
|
16
|
+
if (!type) {
|
|
17
|
+
throw new ModelConfigError('Missing VISION_MODEL_TYPE environment variable');
|
|
18
|
+
}
|
|
19
|
+
if (!registry.has(type)) {
|
|
20
|
+
const supportedTypes = registry.getSupportedTypes().join(', ');
|
|
21
|
+
throw new ModelConfigError(`Unsupported model type: ${type}. Supported types: ${supportedTypes}`);
|
|
22
|
+
}
|
|
23
|
+
const provider = registry.get(type);
|
|
24
|
+
const defaults = provider.defaults;
|
|
25
|
+
// 加载必要配置
|
|
26
|
+
const name = process.env.VISION_MODEL_NAME || defaults.modelName;
|
|
27
|
+
const baseUrl = process.env.VISION_API_BASE_URL || defaults.baseUrl;
|
|
28
|
+
const apiKey = process.env.VISION_API_KEY;
|
|
29
|
+
if (!apiKey) {
|
|
30
|
+
throw new ModelConfigError('Missing VISION_API_KEY environment variable');
|
|
31
|
+
}
|
|
32
|
+
// 验证 API Key 格式
|
|
33
|
+
if (provider.validateApiKey) {
|
|
34
|
+
const validation = provider.validateApiKey(apiKey);
|
|
35
|
+
if (validation !== true) {
|
|
36
|
+
logger.warn(validation, { modelType: type });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// 加载可选配置
|
|
40
|
+
const timeout = process.env.VISION_API_TIMEOUT
|
|
41
|
+
? parseInt(process.env.VISION_API_TIMEOUT, 10)
|
|
42
|
+
: defaults.timeout;
|
|
43
|
+
const maxRetries = process.env.VISION_MAX_RETRIES
|
|
44
|
+
? parseInt(process.env.VISION_MAX_RETRIES, 10)
|
|
45
|
+
: defaults.maxRetries;
|
|
46
|
+
const config = {
|
|
47
|
+
type,
|
|
48
|
+
name,
|
|
49
|
+
baseUrl: baseUrl.replace(/\/$/, ''), // 移除末尾的 /
|
|
50
|
+
apiKey,
|
|
51
|
+
timeout,
|
|
52
|
+
maxRetries,
|
|
53
|
+
thinking: provider.enableThinking ? { type: 'enabled' } : undefined
|
|
54
|
+
};
|
|
55
|
+
logger.info('Model configuration loaded successfully', {
|
|
56
|
+
modelType: type,
|
|
57
|
+
modelName: name,
|
|
58
|
+
baseUrl: config.baseUrl,
|
|
59
|
+
provider: provider.displayName
|
|
60
|
+
});
|
|
61
|
+
return config;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
logger.error('Failed to load model configuration', error);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 验证模型配置
|
|
70
|
+
*/
|
|
71
|
+
export function validateModelConfig(config) {
|
|
72
|
+
try {
|
|
73
|
+
const errors = [];
|
|
74
|
+
if (!config.name || typeof config.name !== 'string') {
|
|
75
|
+
errors.push('Model name is required');
|
|
76
|
+
}
|
|
77
|
+
if (!config.baseUrl || typeof config.baseUrl !== 'string') {
|
|
78
|
+
errors.push('Base URL is required');
|
|
79
|
+
}
|
|
80
|
+
if (!config.apiKey || typeof config.apiKey !== 'string') {
|
|
81
|
+
errors.push('API Key is required');
|
|
82
|
+
}
|
|
83
|
+
if (typeof config.timeout !== 'number' || config.timeout <= 0) {
|
|
84
|
+
errors.push('Timeout must be a positive number');
|
|
85
|
+
}
|
|
86
|
+
if (typeof config.maxRetries !== 'number' || config.maxRetries < 0) {
|
|
87
|
+
errors.push('Max retries must be a non-negative number');
|
|
88
|
+
}
|
|
89
|
+
if (errors.length > 0) {
|
|
90
|
+
throw new ModelConfigError(errors.join('; '));
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
logger.error('Model configuration validation failed', error);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 脱敏显示 API Key
|
|
101
|
+
*/
|
|
102
|
+
export function maskApiKey(apiKey) {
|
|
103
|
+
if (!apiKey || apiKey.length <= 8) {
|
|
104
|
+
return '***';
|
|
105
|
+
}
|
|
106
|
+
const visible = Math.min(4, Math.floor(apiKey.length / 4));
|
|
107
|
+
return `${apiKey.substring(0, visible)}${'*'.repeat(Math.max(1, apiKey.length - visible * 2))}${apiKey.substring(apiKey.length - visible)}`;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 获取模型的显示信息(用于日志)
|
|
111
|
+
*/
|
|
112
|
+
export function getModelDisplayInfo(config) {
|
|
113
|
+
return `${config.type} - ${config.name} (API Key: ${maskApiKey(config.apiKey)})`;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=model-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-config.js","sourceRoot":"","sources":["../../src/config/model-config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Vision MCP Server
|
|
4
|
+
*
|
|
5
|
+
* @description MCP Server providing vision capabilities via GLM-4.6V and SiliconFlow
|
|
6
|
+
* This server implements the Model Context Protocol for LLM integration
|
|
7
|
+
*
|
|
8
|
+
* IMPORTANT: This server uses STDIO for MCP communication. Do not use console.log for debugging.
|
|
9
|
+
* Use console.error (stderr) for all logging as required by MCP protocol.
|
|
10
|
+
*/
|
|
11
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
|
+
/**
|
|
13
|
+
* 创建并配置 MCP Server
|
|
14
|
+
*/
|
|
15
|
+
declare function createMCPServer(): McpServer;
|
|
16
|
+
export { createMCPServer };
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWpE;;GAEG;AACH,iBAAS,eAAe,IAAI,SAAS,CAoJpC;AAqDD,OAAO,EAAE,eAAe,EAAE,CAAC"}
|