markpdfdown 0.1.8-beta.6 → 0.2.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/dist/main/index.js +398 -14
- package/dist/preload/index.js +20 -1
- package/dist/renderer/assets/{index-yTU2skrN.css → index-B_JfRqwM.css} +54 -0
- package/dist/renderer/assets/{index-iEK8qT5k.js → index-wHB9i2kW.js} +623 -320
- package/dist/renderer/index.html +2 -2
- package/package.json +10 -3
- package/dist/app/app.js +0 -49
- package/dist/app/controllers/completionController.js +0 -19
- package/dist/app/controllers/modelController.js +0 -53
- package/dist/app/controllers/providerController.js +0 -120
- package/dist/app/dal/modelDal.js +0 -44
- package/dist/app/dal/providerDal.js +0 -78
- package/dist/app/db/index.js +0 -56
- package/dist/app/db/migration.js +0 -157
- package/dist/app/logic/llm/AnthropicClient.js +0 -219
- package/dist/app/logic/llm/AzureOpenAIClient.js +0 -239
- package/dist/app/logic/llm/GeminiClient.js +0 -212
- package/dist/app/logic/llm/LLMClient.js +0 -80
- package/dist/app/logic/llm/OpenAIClient.js +0 -235
- package/dist/app/logic/llm/example-advanced.js +0 -232
- package/dist/app/logic/llm/index.js +0 -14
- package/dist/app/logic/model.js +0 -27
- package/dist/app/middleware/logger.js +0 -23
- package/dist/app/routes/routes.js +0 -16
- package/dist/app/types/Provider.js +0 -1
- package/dist/server/controllers/FileController.js +0 -64
- package/dist/server/controllers/TaskController.js +0 -57
- package/dist/server/controllers/completionController.js +0 -64
- package/dist/server/controllers/modelController.js +0 -74
- package/dist/server/controllers/providerController.js +0 -120
- package/dist/server/dal/TaskDal.js +0 -67
- package/dist/server/dal/modelDal.js +0 -44
- package/dist/server/dal/providerDal.js +0 -83
- package/dist/server/db/index.js +0 -57
- package/dist/server/db/migration.js +0 -157
- package/dist/server/index.js +0 -49
- package/dist/server/logic/File.js +0 -34
- package/dist/server/logic/Task.js +0 -21
- package/dist/server/logic/llm/AnthropicClient.js +0 -220
- package/dist/server/logic/llm/AzureOpenAIClient.js +0 -239
- package/dist/server/logic/llm/GeminiClient.js +0 -213
- package/dist/server/logic/llm/LLMClient.js +0 -83
- package/dist/server/logic/llm/OllamaClient.js +0 -220
- package/dist/server/logic/llm/OpenAIClient.js +0 -235
- package/dist/server/logic/llm/example-advanced.js +0 -231
- package/dist/server/logic/llm/index.js +0 -15
- package/dist/server/logic/model.js +0 -59
- package/dist/server/middleware/logger.js +0 -23
- package/dist/server/routes/routes.js +0 -30
- package/dist/server/types/Provider.js +0 -1
- package/dist/server/types/Task.js +0 -1
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM客户端接口定义
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* LLM客户端基类
|
|
6
|
-
*/
|
|
7
|
-
export class LLMClient {
|
|
8
|
-
constructor(apiKey, baseUrl = '') {
|
|
9
|
-
this.apiKey = apiKey;
|
|
10
|
-
this.baseUrl = baseUrl;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* 向后兼容处理单一prompt
|
|
14
|
-
* @param options 可能包含旧格式prompt的选项
|
|
15
|
-
* @returns 标准化后的选项
|
|
16
|
-
*/
|
|
17
|
-
normalizeOptions(options) {
|
|
18
|
-
const normalizedOptions = { ...options };
|
|
19
|
-
// 如果存在旧的prompt字段,将其转换为消息格式
|
|
20
|
-
if ('prompt' in normalizedOptions && normalizedOptions.prompt) {
|
|
21
|
-
// 如果没有messages字段或为空数组,则使用prompt创建一个用户消息
|
|
22
|
-
if (!normalizedOptions.messages || normalizedOptions.messages.length === 0) {
|
|
23
|
-
normalizedOptions.messages = [{
|
|
24
|
-
role: 'user',
|
|
25
|
-
content: {
|
|
26
|
-
type: 'text',
|
|
27
|
-
text: normalizedOptions.prompt
|
|
28
|
-
}
|
|
29
|
-
}];
|
|
30
|
-
}
|
|
31
|
-
// 删除原始prompt字段
|
|
32
|
-
delete normalizedOptions.prompt;
|
|
33
|
-
}
|
|
34
|
-
// 如果有systemPrompt但没有system角色消息,添加一个system角色消息
|
|
35
|
-
if (normalizedOptions.systemPrompt &&
|
|
36
|
-
(!normalizedOptions.messages || !normalizedOptions.messages.some(m => m.role === 'system'))) {
|
|
37
|
-
const systemMessage = {
|
|
38
|
-
role: 'system',
|
|
39
|
-
content: {
|
|
40
|
-
type: 'text',
|
|
41
|
-
text: normalizedOptions.systemPrompt
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
normalizedOptions.messages = normalizedOptions.messages || [];
|
|
45
|
-
normalizedOptions.messages.unshift(systemMessage);
|
|
46
|
-
// 删除原始systemPrompt字段
|
|
47
|
-
delete normalizedOptions.systemPrompt;
|
|
48
|
-
}
|
|
49
|
-
return normalizedOptions;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* LLM客户端工厂类,用于创建不同的LLM客户端实例
|
|
54
|
-
*/
|
|
55
|
-
export class LLMClientFactory {
|
|
56
|
-
/**
|
|
57
|
-
* 创建LLM客户端实例
|
|
58
|
-
* @param type LLM客户端类型
|
|
59
|
-
* @param apiKey API密钥
|
|
60
|
-
* @param baseUrl 基础URL,某些服务需要自定义
|
|
61
|
-
*/
|
|
62
|
-
static async createClient(type, apiKey, baseUrl) {
|
|
63
|
-
switch (type) {
|
|
64
|
-
case 'openai':
|
|
65
|
-
const OpenAIModule = await import('./OpenAIClient.js');
|
|
66
|
-
return new OpenAIModule.OpenAIClient(apiKey, baseUrl || '');
|
|
67
|
-
case 'azure-openai':
|
|
68
|
-
const AzureOpenAIModule = await import('./AzureOpenAIClient.js');
|
|
69
|
-
return new AzureOpenAIModule.AzureOpenAIClient(apiKey, baseUrl || '');
|
|
70
|
-
case 'gemini':
|
|
71
|
-
const GeminiModule = await import('./GeminiClient.js');
|
|
72
|
-
return new GeminiModule.GeminiClient(apiKey, baseUrl || '');
|
|
73
|
-
case 'anthropic':
|
|
74
|
-
const AnthropicModule = await import('./AnthropicClient.js');
|
|
75
|
-
return new AnthropicModule.AnthropicClient(apiKey, baseUrl || '');
|
|
76
|
-
default:
|
|
77
|
-
throw new Error(`不支持的LLM客户端类型: ${type}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
import { LLMClient } from './LLMClient.js';
|
|
2
|
-
/**
|
|
3
|
-
* OpenAI客户端实现
|
|
4
|
-
*/
|
|
5
|
-
export class OpenAIClient extends LLMClient {
|
|
6
|
-
constructor(apiKey, baseUrl) {
|
|
7
|
-
super(apiKey, baseUrl || 'https://api.openai.com/v1');
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* 执行OpenAI文本补全
|
|
11
|
-
*/
|
|
12
|
-
async completion(options) {
|
|
13
|
-
try {
|
|
14
|
-
// 标准化选项,处理向后兼容
|
|
15
|
-
const normalizedOptions = this.normalizeOptions(options);
|
|
16
|
-
// 转换消息格式为OpenAI格式
|
|
17
|
-
const openaiMessages = this.convertMessagesToOpenAIFormat(normalizedOptions.messages);
|
|
18
|
-
const requestBody = {
|
|
19
|
-
model: normalizedOptions.model || 'gpt-3.5-turbo',
|
|
20
|
-
messages: openaiMessages,
|
|
21
|
-
temperature: normalizedOptions.temperature ?? 0.7,
|
|
22
|
-
max_tokens: normalizedOptions.maxTokens,
|
|
23
|
-
stream: normalizedOptions.stream || false
|
|
24
|
-
};
|
|
25
|
-
// 添加工具配置(如果有)
|
|
26
|
-
if (normalizedOptions.tools && normalizedOptions.tools.length > 0) {
|
|
27
|
-
requestBody.tools = normalizedOptions.tools;
|
|
28
|
-
if (normalizedOptions.tool_choice) {
|
|
29
|
-
requestBody.tool_choice = normalizedOptions.tool_choice;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// 添加响应格式(如果指定)
|
|
33
|
-
if (normalizedOptions.response_format) {
|
|
34
|
-
requestBody.response_format = normalizedOptions.response_format;
|
|
35
|
-
}
|
|
36
|
-
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
37
|
-
method: 'POST',
|
|
38
|
-
headers: {
|
|
39
|
-
'Content-Type': 'application/json',
|
|
40
|
-
'Authorization': `Bearer ${normalizedOptions.apiKey || this.apiKey}`
|
|
41
|
-
},
|
|
42
|
-
body: JSON.stringify(requestBody)
|
|
43
|
-
});
|
|
44
|
-
if (!response.ok) {
|
|
45
|
-
const error = await response.json();
|
|
46
|
-
throw new Error(`OpenAI API错误: ${error.error?.message || response.statusText}`);
|
|
47
|
-
}
|
|
48
|
-
if (normalizedOptions.stream && response.body && normalizedOptions.onUpdate) {
|
|
49
|
-
// 处理流式响应
|
|
50
|
-
const reader = response.body.getReader();
|
|
51
|
-
const decoder = new TextDecoder('utf-8');
|
|
52
|
-
let content = '';
|
|
53
|
-
let toolCalls = [];
|
|
54
|
-
const processStream = async () => {
|
|
55
|
-
const { done, value } = await reader.read();
|
|
56
|
-
if (done) {
|
|
57
|
-
return {
|
|
58
|
-
content,
|
|
59
|
-
model: normalizedOptions.model || 'gpt-3.5-turbo',
|
|
60
|
-
finishReason: 'stop',
|
|
61
|
-
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
62
|
-
responseFormat: normalizedOptions.response_format?.type
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
// 解析流式响应数据
|
|
66
|
-
const chunk = decoder.decode(value);
|
|
67
|
-
const lines = chunk
|
|
68
|
-
.split('\n')
|
|
69
|
-
.filter(line => line.trim() !== '' && line.trim() !== 'data: [DONE]');
|
|
70
|
-
for (const line of lines) {
|
|
71
|
-
if (line.startsWith('data: ')) {
|
|
72
|
-
try {
|
|
73
|
-
const data = JSON.parse(line.slice(6));
|
|
74
|
-
// 处理常规文本内容
|
|
75
|
-
if (data.choices && data.choices[0]?.delta?.content) {
|
|
76
|
-
const newContent = data.choices[0].delta.content;
|
|
77
|
-
content += newContent;
|
|
78
|
-
if (normalizedOptions.onUpdate) {
|
|
79
|
-
normalizedOptions.onUpdate(content);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// 处理工具调用
|
|
83
|
-
if (data.choices && data.choices[0]?.delta?.tool_calls) {
|
|
84
|
-
const deltaToolCalls = data.choices[0].delta.tool_calls;
|
|
85
|
-
for (const deltaTool of deltaToolCalls) {
|
|
86
|
-
// 查找现有工具调用或创建新的
|
|
87
|
-
let toolCall = toolCalls.find(tc => tc.id === deltaTool.id);
|
|
88
|
-
if (!toolCall && deltaTool.id) {
|
|
89
|
-
toolCall = {
|
|
90
|
-
id: deltaTool.id,
|
|
91
|
-
type: 'function',
|
|
92
|
-
function: {
|
|
93
|
-
name: '',
|
|
94
|
-
arguments: ''
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
toolCalls.push(toolCall);
|
|
98
|
-
}
|
|
99
|
-
if (toolCall && deltaTool.function) {
|
|
100
|
-
if (deltaTool.function.name) {
|
|
101
|
-
toolCall.function.name = deltaTool.function.name;
|
|
102
|
-
}
|
|
103
|
-
if (deltaTool.function.arguments) {
|
|
104
|
-
toolCall.function.arguments += deltaTool.function.arguments;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// 如果有工具调用,同时更新内容
|
|
109
|
-
if (normalizedOptions.onUpdate) {
|
|
110
|
-
normalizedOptions.onUpdate(content);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
catch (e) {
|
|
115
|
-
// 忽略解析错误
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return processStream();
|
|
120
|
-
};
|
|
121
|
-
return processStream();
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
// 处理普通响应
|
|
125
|
-
const data = await response.json();
|
|
126
|
-
// 提取响应内容
|
|
127
|
-
let responseContent = '';
|
|
128
|
-
const toolCalls = [];
|
|
129
|
-
if (data.choices && data.choices[0]?.message) {
|
|
130
|
-
const message = data.choices[0].message;
|
|
131
|
-
// 提取文本内容
|
|
132
|
-
if (typeof message.content === 'string') {
|
|
133
|
-
responseContent = message.content;
|
|
134
|
-
}
|
|
135
|
-
// 提取工具调用
|
|
136
|
-
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
137
|
-
for (const toolCall of message.tool_calls) {
|
|
138
|
-
toolCalls.push({
|
|
139
|
-
id: toolCall.id,
|
|
140
|
-
type: toolCall.type,
|
|
141
|
-
function: {
|
|
142
|
-
name: toolCall.function.name,
|
|
143
|
-
arguments: toolCall.function.arguments
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return {
|
|
150
|
-
content: responseContent,
|
|
151
|
-
model: data.model,
|
|
152
|
-
finishReason: data.choices[0]?.finish_reason,
|
|
153
|
-
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
154
|
-
responseFormat: normalizedOptions.response_format?.type,
|
|
155
|
-
rawResponse: data // 保留原始响应以便调试
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
161
|
-
throw new Error(`OpenAI补全请求失败: ${errorMessage}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* 将消息转换为OpenAI API格式
|
|
166
|
-
*/
|
|
167
|
-
convertMessagesToOpenAIFormat(messages) {
|
|
168
|
-
return messages.map(message => {
|
|
169
|
-
const openaiMessage = {
|
|
170
|
-
role: message.role
|
|
171
|
-
};
|
|
172
|
-
// 处理名称字段(如果存在)
|
|
173
|
-
if (message.name) {
|
|
174
|
-
openaiMessage.name = message.name;
|
|
175
|
-
}
|
|
176
|
-
// 处理内容
|
|
177
|
-
if (Array.isArray(message.content)) {
|
|
178
|
-
// 处理多部分内容
|
|
179
|
-
openaiMessage.content = message.content.map(content => this.convertContentToOpenAIFormat(content));
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
// 处理单一内容
|
|
183
|
-
const content = this.convertContentToOpenAIFormat(message.content);
|
|
184
|
-
// 如果是简单的文本内容,则直接使用字符串
|
|
185
|
-
if (content.type === 'text') {
|
|
186
|
-
openaiMessage.content = content.text;
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
openaiMessage.content = [content];
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return openaiMessage;
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* 将内容对象转换为OpenAI API格式
|
|
197
|
-
*/
|
|
198
|
-
convertContentToOpenAIFormat(content) {
|
|
199
|
-
switch (content.type) {
|
|
200
|
-
case 'text':
|
|
201
|
-
return {
|
|
202
|
-
type: 'text',
|
|
203
|
-
text: content.text
|
|
204
|
-
};
|
|
205
|
-
case 'image':
|
|
206
|
-
const imageContent = content;
|
|
207
|
-
return {
|
|
208
|
-
type: 'image_url',
|
|
209
|
-
image_url: {
|
|
210
|
-
url: imageContent.image_url,
|
|
211
|
-
detail: imageContent.detail || 'auto'
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
case 'tool_call':
|
|
215
|
-
const toolCallContent = content;
|
|
216
|
-
return {
|
|
217
|
-
type: 'tool_call',
|
|
218
|
-
tool_call_id: toolCallContent.tool_call_id,
|
|
219
|
-
function: {
|
|
220
|
-
name: toolCallContent.function.name,
|
|
221
|
-
arguments: toolCallContent.function.arguments
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
case 'tool_result':
|
|
225
|
-
const toolResultContent = content;
|
|
226
|
-
return {
|
|
227
|
-
type: 'tool_result',
|
|
228
|
-
tool_call_id: toolResultContent.tool_call_id,
|
|
229
|
-
content: toolResultContent.content
|
|
230
|
-
};
|
|
231
|
-
default:
|
|
232
|
-
throw new Error(`不支持的内容类型: ${content.type}`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import { LLMClientFactory } from './LLMClient.js';
|
|
2
|
-
/**
|
|
3
|
-
* 高级功能示例:多轮对话、多角色和图片支持
|
|
4
|
-
*/
|
|
5
|
-
async function advancedExample() {
|
|
6
|
-
try {
|
|
7
|
-
// 创建OpenAI客户端
|
|
8
|
-
const openaiClient = await LLMClientFactory.createClient('openai', 'your-openai-api-key');
|
|
9
|
-
// 1. 基本多轮对话示例
|
|
10
|
-
console.log('=== 多轮对话示例 ===');
|
|
11
|
-
const chatMessages = [
|
|
12
|
-
{
|
|
13
|
-
role: 'system',
|
|
14
|
-
content: {
|
|
15
|
-
type: 'text',
|
|
16
|
-
text: '你是一个专业的AI助手,善于回答用户的问题。请简短、直接地回答。'
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
role: 'user',
|
|
21
|
-
content: {
|
|
22
|
-
type: 'text',
|
|
23
|
-
text: '你好,我想了解一下人工智能。'
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
role: 'assistant',
|
|
28
|
-
content: {
|
|
29
|
-
type: 'text',
|
|
30
|
-
text: '人工智能是计算机科学的一个分支,旨在创建能够像人类一样思考和学习的智能机器。它包括机器学习、深度学习、自然语言处理等领域。有什么具体方面你想了解的吗?'
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
role: 'user',
|
|
35
|
-
content: {
|
|
36
|
-
type: 'text',
|
|
37
|
-
text: '自然语言处理是什么?'
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
];
|
|
41
|
-
const chatResponse = await openaiClient.completion({
|
|
42
|
-
messages: chatMessages,
|
|
43
|
-
model: 'gpt-4'
|
|
44
|
-
});
|
|
45
|
-
console.log('对话响应:', chatResponse.content);
|
|
46
|
-
// 2. 图片分析示例
|
|
47
|
-
console.log('\n=== 图片分析示例 ===');
|
|
48
|
-
const imageMessages = [
|
|
49
|
-
{
|
|
50
|
-
role: 'user',
|
|
51
|
-
content: [
|
|
52
|
-
{
|
|
53
|
-
type: 'text',
|
|
54
|
-
text: '这张图片是什么?请详细描述一下。'
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
type: 'image',
|
|
58
|
-
image_url: 'https://example.com/sample-image.jpg', // 请替换为实际图片URL
|
|
59
|
-
detail: 'high'
|
|
60
|
-
}
|
|
61
|
-
]
|
|
62
|
-
}
|
|
63
|
-
];
|
|
64
|
-
const imageResponse = await openaiClient.completion({
|
|
65
|
-
messages: imageMessages,
|
|
66
|
-
model: 'gpt-4-vision-preview' // 支持图像的模型
|
|
67
|
-
});
|
|
68
|
-
console.log('图片分析响应:', imageResponse.content);
|
|
69
|
-
// 3. 工具调用示例
|
|
70
|
-
console.log('\n=== 工具调用示例 ===');
|
|
71
|
-
// 定义一个简单的计算器工具
|
|
72
|
-
const calculatorTool = {
|
|
73
|
-
type: 'function',
|
|
74
|
-
function: {
|
|
75
|
-
name: 'calculate',
|
|
76
|
-
description: '执行基本数学计算',
|
|
77
|
-
parameters: {
|
|
78
|
-
type: 'object',
|
|
79
|
-
properties: {
|
|
80
|
-
operation: {
|
|
81
|
-
type: 'string',
|
|
82
|
-
enum: ['add', 'subtract', 'multiply', 'divide'],
|
|
83
|
-
description: '要执行的操作'
|
|
84
|
-
},
|
|
85
|
-
a: {
|
|
86
|
-
type: 'number',
|
|
87
|
-
description: '第一个操作数'
|
|
88
|
-
},
|
|
89
|
-
b: {
|
|
90
|
-
type: 'number',
|
|
91
|
-
description: '第二个操作数'
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
required: ['operation', 'a', 'b']
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
const toolMessages = [
|
|
99
|
-
{
|
|
100
|
-
role: 'user',
|
|
101
|
-
content: {
|
|
102
|
-
type: 'text',
|
|
103
|
-
text: '我需要计算123乘以456是多少'
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
];
|
|
107
|
-
const toolResponse = await openaiClient.completion({
|
|
108
|
-
messages: toolMessages,
|
|
109
|
-
model: 'gpt-4',
|
|
110
|
-
tools: [calculatorTool],
|
|
111
|
-
tool_choice: 'auto'
|
|
112
|
-
});
|
|
113
|
-
console.log('工具调用响应:', toolResponse);
|
|
114
|
-
// 如果有工具调用,处理它并继续对话
|
|
115
|
-
if (toolResponse.toolCalls && toolResponse.toolCalls.length > 0) {
|
|
116
|
-
const toolCall = toolResponse.toolCalls[0];
|
|
117
|
-
console.log('工具调用:', toolCall.function.name);
|
|
118
|
-
console.log('参数:', toolCall.function.arguments);
|
|
119
|
-
// 解析参数
|
|
120
|
-
const args = JSON.parse(toolCall.function.arguments);
|
|
121
|
-
// 执行计算
|
|
122
|
-
let result;
|
|
123
|
-
switch (args.operation) {
|
|
124
|
-
case 'add':
|
|
125
|
-
result = args.a + args.b;
|
|
126
|
-
break;
|
|
127
|
-
case 'subtract':
|
|
128
|
-
result = args.a - args.b;
|
|
129
|
-
break;
|
|
130
|
-
case 'multiply':
|
|
131
|
-
result = args.a * args.b;
|
|
132
|
-
break;
|
|
133
|
-
case 'divide':
|
|
134
|
-
result = args.a / args.b;
|
|
135
|
-
break;
|
|
136
|
-
default:
|
|
137
|
-
throw new Error(`不支持的操作: ${args.operation}`);
|
|
138
|
-
}
|
|
139
|
-
// 继续对话,添加工具响应
|
|
140
|
-
const toolResultMessages = [
|
|
141
|
-
...toolMessages,
|
|
142
|
-
{
|
|
143
|
-
role: 'assistant',
|
|
144
|
-
content: {
|
|
145
|
-
type: 'text',
|
|
146
|
-
text: toolResponse.content
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
role: 'tool',
|
|
151
|
-
content: {
|
|
152
|
-
type: 'tool_result',
|
|
153
|
-
tool_call_id: toolCall.id,
|
|
154
|
-
content: JSON.stringify({ result })
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
];
|
|
158
|
-
const finalResponse = await openaiClient.completion({
|
|
159
|
-
messages: toolResultMessages,
|
|
160
|
-
model: 'gpt-4'
|
|
161
|
-
});
|
|
162
|
-
console.log('最终响应:', finalResponse.content);
|
|
163
|
-
}
|
|
164
|
-
// 4. JSON 响应格式示例
|
|
165
|
-
console.log('\n=== JSON响应格式示例 ===');
|
|
166
|
-
const jsonMessages = [
|
|
167
|
-
{
|
|
168
|
-
role: 'system',
|
|
169
|
-
content: {
|
|
170
|
-
type: 'text',
|
|
171
|
-
text: '你是一个API服务,返回结构化的JSON数据。'
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
role: 'user',
|
|
176
|
-
content: {
|
|
177
|
-
type: 'text',
|
|
178
|
-
text: '给我三本经典科幻小说的信息,包括书名、作者和出版年份。'
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
];
|
|
182
|
-
const jsonResponse = await openaiClient.completion({
|
|
183
|
-
messages: jsonMessages,
|
|
184
|
-
model: 'gpt-4',
|
|
185
|
-
response_format: { type: 'json_object' }
|
|
186
|
-
});
|
|
187
|
-
console.log('JSON响应:', jsonResponse.content);
|
|
188
|
-
console.log('解析后的JSON:', JSON.parse(jsonResponse.content));
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
console.error('示例运行出错:', error);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
// 辅助函数:将图片文件转换为base64数据URI(用于本地文件)
|
|
195
|
-
export function imageToDataURI(filePath) {
|
|
196
|
-
return new Promise((resolve, reject) => {
|
|
197
|
-
const fs = require('fs');
|
|
198
|
-
fs.readFile(filePath, (err, data) => {
|
|
199
|
-
if (err) {
|
|
200
|
-
reject(err);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
// 获取文件扩展名
|
|
204
|
-
const extension = filePath.split('.').pop()?.toLowerCase();
|
|
205
|
-
let mimeType;
|
|
206
|
-
switch (extension) {
|
|
207
|
-
case 'jpg':
|
|
208
|
-
case 'jpeg':
|
|
209
|
-
mimeType = 'image/jpeg';
|
|
210
|
-
break;
|
|
211
|
-
case 'png':
|
|
212
|
-
mimeType = 'image/png';
|
|
213
|
-
break;
|
|
214
|
-
case 'gif':
|
|
215
|
-
mimeType = 'image/gif';
|
|
216
|
-
break;
|
|
217
|
-
case 'webp':
|
|
218
|
-
mimeType = 'image/webp';
|
|
219
|
-
break;
|
|
220
|
-
default:
|
|
221
|
-
mimeType = 'application/octet-stream';
|
|
222
|
-
}
|
|
223
|
-
const base64Data = data.toString('base64');
|
|
224
|
-
const dataURI = `data:${mimeType};base64,${base64Data}`;
|
|
225
|
-
resolve(dataURI);
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
// 运行示例
|
|
230
|
-
// advancedExample().catch(console.error);
|
|
231
|
-
// 导出函数
|
|
232
|
-
export { advancedExample };
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM客户端模块导出
|
|
3
|
-
*/
|
|
4
|
-
// 导出接口和基类
|
|
5
|
-
export { LLMClient, LLMClientFactory } from './LLMClient.js';
|
|
6
|
-
// 导出具体实现类
|
|
7
|
-
export { OpenAIClient } from './OpenAIClient.js';
|
|
8
|
-
export { AzureOpenAIClient } from './AzureOpenAIClient.js';
|
|
9
|
-
export { GeminiClient } from './GeminiClient.js';
|
|
10
|
-
export { AnthropicClient } from './AnthropicClient.js';
|
|
11
|
-
// 导入工厂类以供默认导出
|
|
12
|
-
import { LLMClientFactory } from './LLMClient.js';
|
|
13
|
-
// 默认导出工厂类
|
|
14
|
-
export default LLMClientFactory;
|
package/dist/app/logic/model.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// @ts-ignore
|
|
2
|
-
import providerDal from '../dal/providerDal.js';
|
|
3
|
-
import { LLMClientFactory } from './llm/LLMClient.js';
|
|
4
|
-
// 根据服务商ID获取LLM客户端
|
|
5
|
-
const getLLMClient = async (providerId) => {
|
|
6
|
-
const provider = await providerDal.findById(providerId);
|
|
7
|
-
if (!provider) {
|
|
8
|
-
throw new Error('服务商不存在');
|
|
9
|
-
}
|
|
10
|
-
return LLMClientFactory.createClient(provider.type || '', provider.api_key || '', `${provider.base_url || ''}${provider.suffix || ''}`);
|
|
11
|
-
};
|
|
12
|
-
// 补全请求
|
|
13
|
-
const completion = async (providerId, modelId, messages, options) => {
|
|
14
|
-
const llmClient = await getLLMClient(providerId);
|
|
15
|
-
// 从options中提取除messages外的其他选项
|
|
16
|
-
const { messages: _messages, model: _model, ...otherOptions } = options || {};
|
|
17
|
-
// 按CompletionOptions格式转换messages
|
|
18
|
-
const completionOptions = {
|
|
19
|
-
messages: messages,
|
|
20
|
-
model: modelId,
|
|
21
|
-
...otherOptions
|
|
22
|
-
};
|
|
23
|
-
return llmClient.completion(completionOptions);
|
|
24
|
-
};
|
|
25
|
-
export default {
|
|
26
|
-
completion
|
|
27
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 请求日志中间件
|
|
3
|
-
* 记录所有API请求的信息
|
|
4
|
-
*/
|
|
5
|
-
const requestLogger = (req, res, next) => {
|
|
6
|
-
const start = Date.now();
|
|
7
|
-
// 请求完成后记录日志信息
|
|
8
|
-
res.on('finish', () => {
|
|
9
|
-
const duration = Date.now() - start;
|
|
10
|
-
console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`);
|
|
11
|
-
});
|
|
12
|
-
next();
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* 错误日志中间件
|
|
16
|
-
* 记录API错误信息
|
|
17
|
-
*/
|
|
18
|
-
const errorLogger = (err, _req, _res, next) => {
|
|
19
|
-
console.error(`[${new Date().toISOString()}] 错误: ${err.message}`);
|
|
20
|
-
console.error(err.stack);
|
|
21
|
-
next(err);
|
|
22
|
-
};
|
|
23
|
-
export { requestLogger, errorLogger };
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import providerController from '../controllers/providerController.js';
|
|
3
|
-
import modelController from '../controllers/modelController.js';
|
|
4
|
-
const router = express.Router();
|
|
5
|
-
// 服务商路由
|
|
6
|
-
router.get('/providers', providerController.getAllProviders);
|
|
7
|
-
router.get('/providers/:id', providerController.getProviderById);
|
|
8
|
-
router.post('/providers', providerController.createProvider);
|
|
9
|
-
router.put('/providers/:id', providerController.updateProvider);
|
|
10
|
-
router.delete('/providers/:id', providerController.deleteProvider);
|
|
11
|
-
router.put('/providers/:id/status', providerController.updateProviderStatus);
|
|
12
|
-
// 模型路由
|
|
13
|
-
router.get('/models/:provider', modelController.getModelsByProviderId);
|
|
14
|
-
router.post('/models', modelController.createModel);
|
|
15
|
-
router.delete('/models/:id/:provider', modelController.deleteModel);
|
|
16
|
-
export default router;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|