@userdaoo/iflow-api-bridge 0.1.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 +195 -0
- package/dist/adapter.d.ts +59 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +171 -0
- package/dist/adapter.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +57 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +83 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/openai/transformer.d.ts +52 -0
- package/dist/openai/transformer.d.ts.map +1 -0
- package/dist/openai/transformer.js +120 -0
- package/dist/openai/transformer.js.map +1 -0
- package/dist/openai/types.d.ts +96 -0
- package/dist/openai/types.d.ts.map +1 -0
- package/dist/openai/types.js +6 -0
- package/dist/openai/types.js.map +1 -0
- package/dist/server.d.ts +49 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +278 -0
- package/dist/server.js.map +1 -0
- package/iflow-api.config.example.json +8 -0
- package/package.json +43 -0
- package/src/adapter.ts +227 -0
- package/src/cli.ts +64 -0
- package/src/config.ts +102 -0
- package/src/index.ts +27 -0
- package/src/openai/transformer.ts +142 -0
- package/src/openai/types.ts +102 -0
- package/src/server.ts +341 -0
- package/tsconfig.json +20 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* iFlow API Bridge
|
|
4
|
+
* 将 iFlow LLM 服务暴露为 OpenAI 兼容 API
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.getDefaultModel = exports.AVAILABLE_MODELS = exports.SSE_DONE = exports.formatSSE = exports.calculateUsage = exports.createStreamChunk = exports.createCompletionResponse = exports.messagesToIFlowPrompt = exports.getTimestamp = exports.generateId = exports.DEFAULT_CONFIG = exports.mergeConfig = exports.loadEnvConfig = exports.loadConfig = exports.IFlowAPIServer = exports.IFlowAdapter = void 0;
|
|
22
|
+
var adapter_js_1 = require("./adapter.js");
|
|
23
|
+
Object.defineProperty(exports, "IFlowAdapter", { enumerable: true, get: function () { return adapter_js_1.IFlowAdapter; } });
|
|
24
|
+
var server_js_1 = require("./server.js");
|
|
25
|
+
Object.defineProperty(exports, "IFlowAPIServer", { enumerable: true, get: function () { return server_js_1.IFlowAPIServer; } });
|
|
26
|
+
var config_js_1 = require("./config.js");
|
|
27
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_js_1.loadConfig; } });
|
|
28
|
+
Object.defineProperty(exports, "loadEnvConfig", { enumerable: true, get: function () { return config_js_1.loadEnvConfig; } });
|
|
29
|
+
Object.defineProperty(exports, "mergeConfig", { enumerable: true, get: function () { return config_js_1.mergeConfig; } });
|
|
30
|
+
Object.defineProperty(exports, "DEFAULT_CONFIG", { enumerable: true, get: function () { return config_js_1.DEFAULT_CONFIG; } });
|
|
31
|
+
__exportStar(require("./openai/types.js"), exports);
|
|
32
|
+
var transformer_js_1 = require("./openai/transformer.js");
|
|
33
|
+
Object.defineProperty(exports, "generateId", { enumerable: true, get: function () { return transformer_js_1.generateId; } });
|
|
34
|
+
Object.defineProperty(exports, "getTimestamp", { enumerable: true, get: function () { return transformer_js_1.getTimestamp; } });
|
|
35
|
+
Object.defineProperty(exports, "messagesToIFlowPrompt", { enumerable: true, get: function () { return transformer_js_1.messagesToIFlowPrompt; } });
|
|
36
|
+
Object.defineProperty(exports, "createCompletionResponse", { enumerable: true, get: function () { return transformer_js_1.createCompletionResponse; } });
|
|
37
|
+
Object.defineProperty(exports, "createStreamChunk", { enumerable: true, get: function () { return transformer_js_1.createStreamChunk; } });
|
|
38
|
+
Object.defineProperty(exports, "calculateUsage", { enumerable: true, get: function () { return transformer_js_1.calculateUsage; } });
|
|
39
|
+
Object.defineProperty(exports, "formatSSE", { enumerable: true, get: function () { return transformer_js_1.formatSSE; } });
|
|
40
|
+
Object.defineProperty(exports, "SSE_DONE", { enumerable: true, get: function () { return transformer_js_1.SSE_DONE; } });
|
|
41
|
+
Object.defineProperty(exports, "AVAILABLE_MODELS", { enumerable: true, get: function () { return transformer_js_1.AVAILABLE_MODELS; } });
|
|
42
|
+
Object.defineProperty(exports, "getDefaultModel", { enumerable: true, get: function () { return transformer_js_1.getDefaultModel; } });
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAEH,2CAAkF;AAAzE,0GAAA,YAAY,OAAA;AACrB,yCAAiE;AAAxD,2GAAA,cAAc,OAAA;AACvB,yCAMqB;AALnB,uGAAA,UAAU,OAAA;AACV,0GAAA,aAAa,OAAA;AACb,wGAAA,WAAW,OAAA;AACX,2GAAA,cAAc,OAAA;AAGhB,oDAAkC;AAClC,0DAWiC;AAV/B,4GAAA,UAAU,OAAA;AACV,8GAAA,YAAY,OAAA;AACZ,uHAAA,qBAAqB,OAAA;AACrB,0HAAA,wBAAwB,OAAA;AACxB,mHAAA,iBAAiB,OAAA;AACjB,gHAAA,cAAc,OAAA;AACd,2GAAA,SAAS,OAAA;AACT,0GAAA,QAAQ,OAAA;AACR,kHAAA,gBAAgB,OAAA;AAChB,iHAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI API 与 iFlow 消息格式转换器
|
|
3
|
+
*/
|
|
4
|
+
import type { ChatCompletionMessage, ChatCompletionResponse, ChatCompletionStreamResponse, UsageInfo } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* 生成唯一 ID
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateId(): string;
|
|
9
|
+
/**
|
|
10
|
+
* 获取当前时间戳
|
|
11
|
+
*/
|
|
12
|
+
export declare function getTimestamp(): number;
|
|
13
|
+
/**
|
|
14
|
+
* 将 OpenAI 消息格式转换为 iFlow 文本格式
|
|
15
|
+
*/
|
|
16
|
+
export declare function messagesToIFlowPrompt(messages: ChatCompletionMessage[]): string;
|
|
17
|
+
/**
|
|
18
|
+
* 创建流式响应块
|
|
19
|
+
*/
|
|
20
|
+
export declare function createStreamChunk(id: string, model: string, content: string, finishReason?: 'stop' | 'length' | null): ChatCompletionStreamResponse;
|
|
21
|
+
/**
|
|
22
|
+
* 创建完整响应
|
|
23
|
+
*/
|
|
24
|
+
export declare function createCompletionResponse(id: string, model: string, content: string, usage: UsageInfo, finishReason?: 'stop' | 'length'): ChatCompletionResponse;
|
|
25
|
+
/**
|
|
26
|
+
* 估算 token 数量(简化版)
|
|
27
|
+
*/
|
|
28
|
+
export declare function estimateTokens(text: string): number;
|
|
29
|
+
/**
|
|
30
|
+
* 计算使用量
|
|
31
|
+
*/
|
|
32
|
+
export declare function calculateUsage(prompt: string, completion: string): UsageInfo;
|
|
33
|
+
/**
|
|
34
|
+
* SSE 格式化
|
|
35
|
+
*/
|
|
36
|
+
export declare function formatSSE(data: unknown): string;
|
|
37
|
+
/**
|
|
38
|
+
* SSE 结束标记
|
|
39
|
+
*/
|
|
40
|
+
export declare const SSE_DONE = "data: [DONE]\n\n";
|
|
41
|
+
/**
|
|
42
|
+
* 支持的模型列表
|
|
43
|
+
*/
|
|
44
|
+
export declare const AVAILABLE_MODELS: {
|
|
45
|
+
id: string;
|
|
46
|
+
name: string;
|
|
47
|
+
}[];
|
|
48
|
+
/**
|
|
49
|
+
* 获取默认模型 ID
|
|
50
|
+
*/
|
|
51
|
+
export declare function getDefaultModel(): string;
|
|
52
|
+
//# sourceMappingURL=transformer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.d.ts","sourceRoot":"","sources":["../../src/openai/transformer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACtB,4BAA4B,EAG5B,SAAS,EAEV,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,GAAG,MAAM,CAO/E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,MAAM,GAAG,QAAQ,GAAG,IAAW,GAC5C,4BAA4B,CAc9B;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,EAChB,YAAY,GAAE,MAAM,GAAG,QAAiB,GACvC,sBAAsB,CAkBxB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,CAS5E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ,qBAAqB,CAAC;AAE3C;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;GAI5B,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenAI API 与 iFlow 消息格式转换器
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AVAILABLE_MODELS = exports.SSE_DONE = void 0;
|
|
7
|
+
exports.generateId = generateId;
|
|
8
|
+
exports.getTimestamp = getTimestamp;
|
|
9
|
+
exports.messagesToIFlowPrompt = messagesToIFlowPrompt;
|
|
10
|
+
exports.createStreamChunk = createStreamChunk;
|
|
11
|
+
exports.createCompletionResponse = createCompletionResponse;
|
|
12
|
+
exports.estimateTokens = estimateTokens;
|
|
13
|
+
exports.calculateUsage = calculateUsage;
|
|
14
|
+
exports.formatSSE = formatSSE;
|
|
15
|
+
exports.getDefaultModel = getDefaultModel;
|
|
16
|
+
/**
|
|
17
|
+
* 生成唯一 ID
|
|
18
|
+
*/
|
|
19
|
+
function generateId() {
|
|
20
|
+
return `chatcmpl-${Date.now().toString(36)}${Math.random().toString(36).substring(2, 10)}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 获取当前时间戳
|
|
24
|
+
*/
|
|
25
|
+
function getTimestamp() {
|
|
26
|
+
return Math.floor(Date.now() / 1000);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 将 OpenAI 消息格式转换为 iFlow 文本格式
|
|
30
|
+
*/
|
|
31
|
+
function messagesToIFlowPrompt(messages) {
|
|
32
|
+
return messages.map(msg => {
|
|
33
|
+
if (typeof msg.content === 'string') {
|
|
34
|
+
return `${msg.role}: ${msg.content}`;
|
|
35
|
+
}
|
|
36
|
+
return `${msg.role}: [复杂内容]`;
|
|
37
|
+
}).join('\n\n');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 创建流式响应块
|
|
41
|
+
*/
|
|
42
|
+
function createStreamChunk(id, model, content, finishReason = null) {
|
|
43
|
+
const choice = {
|
|
44
|
+
index: 0,
|
|
45
|
+
delta: content ? { content } : {},
|
|
46
|
+
finish_reason: finishReason,
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
id,
|
|
50
|
+
object: 'chat.completion.chunk',
|
|
51
|
+
created: getTimestamp(),
|
|
52
|
+
model,
|
|
53
|
+
choices: [choice],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 创建完整响应
|
|
58
|
+
*/
|
|
59
|
+
function createCompletionResponse(id, model, content, usage, finishReason = 'stop') {
|
|
60
|
+
const choice = {
|
|
61
|
+
index: 0,
|
|
62
|
+
message: {
|
|
63
|
+
role: 'assistant',
|
|
64
|
+
content,
|
|
65
|
+
},
|
|
66
|
+
finish_reason: finishReason,
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
id,
|
|
70
|
+
object: 'chat.completion',
|
|
71
|
+
created: getTimestamp(),
|
|
72
|
+
model,
|
|
73
|
+
choices: [choice],
|
|
74
|
+
usage,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 估算 token 数量(简化版)
|
|
79
|
+
*/
|
|
80
|
+
function estimateTokens(text) {
|
|
81
|
+
// 简化估算:假设平均每 4 个字符一个 token
|
|
82
|
+
return Math.ceil(text.length / 4);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 计算使用量
|
|
86
|
+
*/
|
|
87
|
+
function calculateUsage(prompt, completion) {
|
|
88
|
+
const promptTokens = estimateTokens(prompt);
|
|
89
|
+
const completionTokens = estimateTokens(completion);
|
|
90
|
+
return {
|
|
91
|
+
prompt_tokens: promptTokens,
|
|
92
|
+
completion_tokens: completionTokens,
|
|
93
|
+
total_tokens: promptTokens + completionTokens,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* SSE 格式化
|
|
98
|
+
*/
|
|
99
|
+
function formatSSE(data) {
|
|
100
|
+
return `data: ${JSON.stringify(data)}\n\n`;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* SSE 结束标记
|
|
104
|
+
*/
|
|
105
|
+
exports.SSE_DONE = 'data: [DONE]\n\n';
|
|
106
|
+
/**
|
|
107
|
+
* 支持的模型列表
|
|
108
|
+
*/
|
|
109
|
+
exports.AVAILABLE_MODELS = [
|
|
110
|
+
{ id: 'iflow-default', name: 'iFlow Default' },
|
|
111
|
+
{ id: 'iflow-claude', name: 'iFlow Claude' },
|
|
112
|
+
{ id: 'iflow-gpt-4', name: 'iFlow GPT-4' },
|
|
113
|
+
];
|
|
114
|
+
/**
|
|
115
|
+
* 获取默认模型 ID
|
|
116
|
+
*/
|
|
117
|
+
function getDefaultModel() {
|
|
118
|
+
return 'kimi-k2.5';
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=transformer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.js","sourceRoot":"","sources":["../../src/openai/transformer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI API 兼容类型定义
|
|
3
|
+
*/
|
|
4
|
+
export interface ChatCompletionMessage {
|
|
5
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
6
|
+
content: string | null;
|
|
7
|
+
name?: string;
|
|
8
|
+
tool_calls?: ToolCall[];
|
|
9
|
+
tool_call_id?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ToolCall {
|
|
12
|
+
id: string;
|
|
13
|
+
type: 'function';
|
|
14
|
+
function: {
|
|
15
|
+
name: string;
|
|
16
|
+
arguments: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface ChatCompletionRequest {
|
|
20
|
+
model: string;
|
|
21
|
+
messages: ChatCompletionMessage[];
|
|
22
|
+
stream?: boolean;
|
|
23
|
+
temperature?: number;
|
|
24
|
+
top_p?: number;
|
|
25
|
+
max_tokens?: number;
|
|
26
|
+
presence_penalty?: number;
|
|
27
|
+
frequency_penalty?: number;
|
|
28
|
+
stop?: string | string[];
|
|
29
|
+
tools?: ToolDefinition[];
|
|
30
|
+
tool_choice?: 'none' | 'auto' | {
|
|
31
|
+
type: 'function';
|
|
32
|
+
function: {
|
|
33
|
+
name: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface ToolDefinition {
|
|
38
|
+
type: 'function';
|
|
39
|
+
function: {
|
|
40
|
+
name: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
parameters?: Record<string, unknown>;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export interface ChatCompletionResponse {
|
|
46
|
+
id: string;
|
|
47
|
+
object: 'chat.completion';
|
|
48
|
+
created: number;
|
|
49
|
+
model: string;
|
|
50
|
+
choices: ChatCompletionChoice[];
|
|
51
|
+
usage: UsageInfo;
|
|
52
|
+
}
|
|
53
|
+
export interface ChatCompletionChoice {
|
|
54
|
+
index: number;
|
|
55
|
+
message: ChatCompletionMessage;
|
|
56
|
+
finish_reason: 'stop' | 'length' | 'tool_calls' | null;
|
|
57
|
+
}
|
|
58
|
+
export interface ChatCompletionStreamResponse {
|
|
59
|
+
id: string;
|
|
60
|
+
object: 'chat.completion.chunk';
|
|
61
|
+
created: number;
|
|
62
|
+
model: string;
|
|
63
|
+
choices: ChatCompletionStreamChoice[];
|
|
64
|
+
}
|
|
65
|
+
export interface ChatCompletionStreamChoice {
|
|
66
|
+
index: number;
|
|
67
|
+
delta: {
|
|
68
|
+
role?: string;
|
|
69
|
+
content?: string;
|
|
70
|
+
tool_calls?: ToolCall[];
|
|
71
|
+
};
|
|
72
|
+
finish_reason: 'stop' | 'length' | 'tool_calls' | null;
|
|
73
|
+
}
|
|
74
|
+
export interface UsageInfo {
|
|
75
|
+
prompt_tokens: number;
|
|
76
|
+
completion_tokens: number;
|
|
77
|
+
total_tokens: number;
|
|
78
|
+
}
|
|
79
|
+
export interface ModelInfo {
|
|
80
|
+
id: string;
|
|
81
|
+
object: 'model';
|
|
82
|
+
created: number;
|
|
83
|
+
owned_by: string;
|
|
84
|
+
}
|
|
85
|
+
export interface ModelsResponse {
|
|
86
|
+
object: 'list';
|
|
87
|
+
data: ModelInfo[];
|
|
88
|
+
}
|
|
89
|
+
export interface ErrorResponse {
|
|
90
|
+
error: {
|
|
91
|
+
message: string;
|
|
92
|
+
type: string;
|
|
93
|
+
code: string;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/openai/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/openai/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 服务器和 OpenAI API 路由
|
|
3
|
+
*/
|
|
4
|
+
export interface ServerOptions {
|
|
5
|
+
port: number;
|
|
6
|
+
host?: string;
|
|
7
|
+
cors?: boolean;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class IFlowAPIServer {
|
|
12
|
+
private app;
|
|
13
|
+
private adapter;
|
|
14
|
+
private options;
|
|
15
|
+
constructor(options: ServerOptions);
|
|
16
|
+
/**
|
|
17
|
+
* 设置中间件
|
|
18
|
+
*/
|
|
19
|
+
private setupMiddleware;
|
|
20
|
+
/**
|
|
21
|
+
* 设置路由
|
|
22
|
+
*/
|
|
23
|
+
private setupRoutes;
|
|
24
|
+
/**
|
|
25
|
+
* 处理流式响应
|
|
26
|
+
*/
|
|
27
|
+
private handleStreamResponse;
|
|
28
|
+
/**
|
|
29
|
+
* 处理非流式响应
|
|
30
|
+
*/
|
|
31
|
+
private handleNonStreamResponse;
|
|
32
|
+
/**
|
|
33
|
+
* 设置错误处理
|
|
34
|
+
*/
|
|
35
|
+
private setupErrorHandler;
|
|
36
|
+
/**
|
|
37
|
+
* 创建错误响应
|
|
38
|
+
*/
|
|
39
|
+
private createError;
|
|
40
|
+
/**
|
|
41
|
+
* 启动服务器
|
|
42
|
+
*/
|
|
43
|
+
start(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* 停止服务器
|
|
46
|
+
*/
|
|
47
|
+
stop(): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyBH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,OAAO,CAAgB;gBAEnB,OAAO,EAAE,aAAa;IAYlC;;OAEG;IACH,OAAO,CAAC,eAAe;IAoCvB;;OAEG;IACH,OAAO,CAAC,WAAW;IA6DnB;;OAEG;YACW,oBAAoB;IA+ElC;;OAEG;YACW,uBAAuB;IAqCrC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5B"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HTTP 服务器和 OpenAI API 路由
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.IFlowAPIServer = void 0;
|
|
10
|
+
const express_1 = __importDefault(require("express"));
|
|
11
|
+
const cors_1 = __importDefault(require("cors"));
|
|
12
|
+
const adapter_js_1 = require("./adapter.js");
|
|
13
|
+
const transformer_js_1 = require("./openai/transformer.js");
|
|
14
|
+
class IFlowAPIServer {
|
|
15
|
+
app;
|
|
16
|
+
adapter;
|
|
17
|
+
options;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.app = (0, express_1.default)();
|
|
21
|
+
this.adapter = new adapter_js_1.IFlowAdapter({
|
|
22
|
+
model: options.model,
|
|
23
|
+
});
|
|
24
|
+
this.setupMiddleware();
|
|
25
|
+
this.setupRoutes();
|
|
26
|
+
this.setupErrorHandler();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 设置中间件
|
|
30
|
+
*/
|
|
31
|
+
setupMiddleware() {
|
|
32
|
+
// JSON 解析
|
|
33
|
+
this.app.use(express_1.default.json({ limit: '10mb' }));
|
|
34
|
+
// CORS
|
|
35
|
+
if (this.options.cors !== false) {
|
|
36
|
+
this.app.use((0, cors_1.default)({
|
|
37
|
+
origin: '*',
|
|
38
|
+
methods: ['GET', 'POST', 'OPTIONS'],
|
|
39
|
+
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
// API Key 验证(如果配置了)
|
|
43
|
+
if (this.options.apiKey) {
|
|
44
|
+
this.app.use((req, res, next) => {
|
|
45
|
+
// 健康检查端点不需要认证
|
|
46
|
+
if (req.path === '/health') {
|
|
47
|
+
return next();
|
|
48
|
+
}
|
|
49
|
+
const authHeader = req.headers.authorization;
|
|
50
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
51
|
+
return res.status(401).json(this.createError('未提供 API Key', 'authentication_error'));
|
|
52
|
+
}
|
|
53
|
+
const token = authHeader.substring(7);
|
|
54
|
+
if (token !== this.options.apiKey) {
|
|
55
|
+
return res.status(401).json(this.createError('API Key 无效', 'authentication_error'));
|
|
56
|
+
}
|
|
57
|
+
next();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 设置路由
|
|
63
|
+
*/
|
|
64
|
+
setupRoutes() {
|
|
65
|
+
// 健康检查
|
|
66
|
+
this.app.get('/health', (req, res) => {
|
|
67
|
+
res.json({
|
|
68
|
+
status: 'ok',
|
|
69
|
+
connected: this.adapter.isConnected(),
|
|
70
|
+
timestamp: new Date().toISOString(),
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
// 模型列表
|
|
74
|
+
this.app.get('/v1/models', async (req, res) => {
|
|
75
|
+
const response = {
|
|
76
|
+
object: 'list',
|
|
77
|
+
data: transformer_js_1.AVAILABLE_MODELS.map(model => ({
|
|
78
|
+
id: model.id,
|
|
79
|
+
object: 'model',
|
|
80
|
+
created: (0, transformer_js_1.getTimestamp)(),
|
|
81
|
+
owned_by: 'iflow',
|
|
82
|
+
})),
|
|
83
|
+
};
|
|
84
|
+
res.json(response);
|
|
85
|
+
});
|
|
86
|
+
// 聊天完成
|
|
87
|
+
this.app.post('/v1/chat/completions', async (req, res) => {
|
|
88
|
+
console.log(`[${new Date().toISOString()}] 收到聊天请求`);
|
|
89
|
+
console.log('请求体:', JSON.stringify(req.body, null, 2));
|
|
90
|
+
try {
|
|
91
|
+
const body = req.body;
|
|
92
|
+
// 验证请求
|
|
93
|
+
if (!body.messages || !Array.isArray(body.messages) || body.messages.length === 0) {
|
|
94
|
+
console.log('请求验证失败: messages 为空');
|
|
95
|
+
return res.status(400).json(this.createError('messages 不能为空', 'invalid_request_error'));
|
|
96
|
+
}
|
|
97
|
+
const isStream = body.stream === true;
|
|
98
|
+
const model = body.model || (0, transformer_js_1.getDefaultModel)();
|
|
99
|
+
console.log(`流式: ${isStream}, 模型: ${model}`);
|
|
100
|
+
// 转换消息为 iFlow 提示词
|
|
101
|
+
const prompt = (0, transformer_js_1.messagesToIFlowPrompt)(body.messages);
|
|
102
|
+
console.log('提示词:', prompt.substring(0, 100) + '...');
|
|
103
|
+
if (isStream) {
|
|
104
|
+
console.log('处理流式响应...');
|
|
105
|
+
await this.handleStreamResponse(res, prompt, model);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.log('处理非流式响应...');
|
|
109
|
+
await this.handleNonStreamResponse(res, prompt, model);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.error('处理请求错误:', error);
|
|
114
|
+
res.status(500).json(this.createError('内部服务器错误', 'internal_error'));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 处理流式响应
|
|
120
|
+
*/
|
|
121
|
+
async handleStreamResponse(res, prompt, model) {
|
|
122
|
+
const id = (0, transformer_js_1.generateId)();
|
|
123
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
124
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
125
|
+
res.setHeader('Connection', 'keep-alive');
|
|
126
|
+
// 设置 60 秒超时
|
|
127
|
+
const timeout = setTimeout(() => {
|
|
128
|
+
res.write((0, transformer_js_1.formatSSE)(this.createError('请求超时', 'timeout_error')));
|
|
129
|
+
res.end();
|
|
130
|
+
}, 60000);
|
|
131
|
+
try {
|
|
132
|
+
// 发送开始标记(角色)
|
|
133
|
+
const startChunk = {
|
|
134
|
+
id,
|
|
135
|
+
object: 'chat.completion.chunk',
|
|
136
|
+
created: (0, transformer_js_1.getTimestamp)(),
|
|
137
|
+
model,
|
|
138
|
+
choices: [{
|
|
139
|
+
index: 0,
|
|
140
|
+
delta: { role: 'assistant' },
|
|
141
|
+
finish_reason: null,
|
|
142
|
+
}],
|
|
143
|
+
};
|
|
144
|
+
res.write((0, transformer_js_1.formatSSE)(startChunk));
|
|
145
|
+
// 流式发送内容
|
|
146
|
+
let fullContent = '';
|
|
147
|
+
let isDone = false;
|
|
148
|
+
for await (const chunk of this.adapter.sendMessageStream(prompt)) {
|
|
149
|
+
if (isDone)
|
|
150
|
+
break;
|
|
151
|
+
switch (chunk.type) {
|
|
152
|
+
case 'content':
|
|
153
|
+
if (chunk.content) {
|
|
154
|
+
fullContent += chunk.content;
|
|
155
|
+
const streamChunk = (0, transformer_js_1.createStreamChunk)(id, model, chunk.content);
|
|
156
|
+
res.write((0, transformer_js_1.formatSSE)(streamChunk));
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
case 'tool_call':
|
|
160
|
+
// 工具调用信息可以记录日志,但不发送到客户端
|
|
161
|
+
console.log(`工具调用: ${chunk.toolName} - ${chunk.toolStatus}`);
|
|
162
|
+
break;
|
|
163
|
+
case 'done':
|
|
164
|
+
// 收到完成信号,标记结束
|
|
165
|
+
isDone = true;
|
|
166
|
+
break;
|
|
167
|
+
case 'error':
|
|
168
|
+
console.error('流式处理错误:', chunk.error);
|
|
169
|
+
res.write((0, transformer_js_1.formatSSE)(this.createError(chunk.error || '流式响应错误', 'streaming_error', 'streaming_error')));
|
|
170
|
+
isDone = true;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// 发送结束标记
|
|
175
|
+
const endChunk = (0, transformer_js_1.createStreamChunk)(id, model, '', 'stop');
|
|
176
|
+
res.write((0, transformer_js_1.formatSSE)(endChunk));
|
|
177
|
+
res.write(transformer_js_1.SSE_DONE);
|
|
178
|
+
res.end();
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error('流式响应错误:', error);
|
|
182
|
+
res.write((0, transformer_js_1.formatSSE)(this.createError('流式响应失败', 'streaming_error')));
|
|
183
|
+
res.end();
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
clearTimeout(timeout);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 处理非流式响应
|
|
191
|
+
*/
|
|
192
|
+
async handleNonStreamResponse(res, prompt, model) {
|
|
193
|
+
const id = (0, transformer_js_1.generateId)();
|
|
194
|
+
// 设置 60 秒超时
|
|
195
|
+
const timeout = setTimeout(() => {
|
|
196
|
+
res.status(504).json(this.createError('请求超时', 'timeout_error'));
|
|
197
|
+
}, 60000);
|
|
198
|
+
try {
|
|
199
|
+
const response = await this.adapter.sendMessage(prompt);
|
|
200
|
+
clearTimeout(timeout);
|
|
201
|
+
const usage = (0, transformer_js_1.calculateUsage)(prompt, response.content);
|
|
202
|
+
const completionResponse = (0, transformer_js_1.createCompletionResponse)(id, model, response.content, usage, response.stopReason === 'max_tokens' ? 'length' : 'stop');
|
|
203
|
+
res.json(completionResponse);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
clearTimeout(timeout);
|
|
207
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
208
|
+
console.error('处理请求错误:', errorMsg);
|
|
209
|
+
// 区分超时错误和其他错误
|
|
210
|
+
if (errorMsg.includes('超时') || errorMsg.includes('timeout')) {
|
|
211
|
+
res.status(504).json(this.createError(errorMsg, 'timeout_error', 'timeout'));
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
res.status(500).json(this.createError(errorMsg, 'internal_error', 'internal_error'));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 设置错误处理
|
|
220
|
+
*/
|
|
221
|
+
setupErrorHandler() {
|
|
222
|
+
this.app.use((err, req, res, _next) => {
|
|
223
|
+
console.error('服务器错误:', err);
|
|
224
|
+
res.status(500).json(this.createError('服务器内部错误', 'internal_error'));
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 创建错误响应
|
|
229
|
+
*/
|
|
230
|
+
createError(message, type, code = 'unknown_error') {
|
|
231
|
+
return {
|
|
232
|
+
error: {
|
|
233
|
+
message,
|
|
234
|
+
type,
|
|
235
|
+
code,
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* 启动服务器
|
|
241
|
+
*/
|
|
242
|
+
async start() {
|
|
243
|
+
// 连接 iFlow
|
|
244
|
+
console.log('正在连接 iFlow...');
|
|
245
|
+
await this.adapter.connect();
|
|
246
|
+
console.log('iFlow 连接成功');
|
|
247
|
+
// 启动 HTTP 服务器
|
|
248
|
+
const host = this.options.host || '0.0.0.0';
|
|
249
|
+
const port = this.options.port;
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
this.app.listen(port, host, () => {
|
|
252
|
+
const model = this.options.model || 'iFlow 默认';
|
|
253
|
+
console.log(`\n🚀 iFlow API 桥接服务已启动`);
|
|
254
|
+
console.log(`📍 服务地址: http://${host}:${port}`);
|
|
255
|
+
console.log(`🤖 使用模型: ${model}`);
|
|
256
|
+
console.log(`🔗 OpenAI API: http://${host}:${port}/v1/chat/completions`);
|
|
257
|
+
console.log(`📋 模型列表: http://${host}:${port}/v1/models`);
|
|
258
|
+
console.log(`❤️ 健康检查: http://${host}:${port}/health`);
|
|
259
|
+
console.log(`\n💡 使用示例:`);
|
|
260
|
+
console.log(` export OPENAI_BASE_URL=http://${host}:${port}/v1`);
|
|
261
|
+
console.log(` export OPENAI_API_KEY=sk-iflow`);
|
|
262
|
+
console.log(` claude`);
|
|
263
|
+
console.log();
|
|
264
|
+
resolve();
|
|
265
|
+
}).on('error', (err) => {
|
|
266
|
+
reject(err);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 停止服务器
|
|
272
|
+
*/
|
|
273
|
+
async stop() {
|
|
274
|
+
await this.adapter.disconnect();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
exports.IFlowAPIServer = IFlowAPIServer;
|
|
278
|
+
//# sourceMappingURL=server.js.map
|