huxy-llm-api 1.1.1 → 1.1.2

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.
Files changed (3) hide show
  1. package/README.md +39 -6
  2. package/package.json +3 -3
  3. package/src/index.js +183 -136
package/README.md CHANGED
@@ -32,7 +32,6 @@ import startApi from 'huxy-llm-api';
32
32
  const ollamaApi = startApi('ollama', {
33
33
  apiKey: 'your-api-key',
34
34
  host: 'http://localhost:11434',
35
- // undici dispatcher
36
35
  dispatcher: {
37
36
  headersTimeout: 10 * 60 * 1000,
38
37
  },
@@ -84,9 +83,45 @@ const response = await openaiApi.chat('你是谁', {
84
83
  console.log('对话结果:', response);
85
84
  ```
86
85
 
86
+ #### 图片处理函数
87
+
88
+ - `saveImage(base64String, outputDir = './images', name)`: 保存 base64 图片到本地。
89
+ - `imageToBase64(imagePath, includeMimeType = false)`: 将本地图片转为 base64 字符串。
90
+
91
+ 使用:
92
+
93
+ ```
94
+ // imageToBase64
95
+ const image = await ollamaApi.imageToBase64('./example.png');
96
+ const result = await ollamaApi.generate('你好', {
97
+ model: 'qwen3-vl',
98
+ stream: false,
99
+ image,
100
+ options: {
101
+ temperature: 0.15,
102
+ top_p: 0.9,
103
+ },
104
+ }, (message) => {
105
+ console.log('实时响应:', message);
106
+ });
107
+
108
+ // saveImage
109
+ const result = await ollamaApi.generate('你好', {
110
+ model: 'qwen3-vl',
111
+ stream: false,
112
+ options: {
113
+ temperature: 0.15,
114
+ top_p: 0.9,
115
+ },
116
+ }, (message) => {
117
+ console.log('实时响应:', message);
118
+ });
119
+ saveImage(result.image);
120
+ ```
121
+
87
122
  ## API 文档
88
123
 
89
- ### `startApi(apiType, userConfig, userOption)`
124
+ ### `startApi(apiType, userConfig)`
90
125
 
91
126
  初始化 LLM API 客户端。
92
127
 
@@ -114,13 +149,11 @@ console.log('对话结果:', response);
114
149
  - `prompt`: 字符串或消息数组 - 输入提示
115
150
  - `configs`: 对象 - 模型参数配置
116
151
  - `model`: 模型名称
117
- - `system`: 系统提示词
118
152
  - `stream`: 是否流式响应(默认: false)
119
- - `think`: 是否开启思考模式(需模型支持)(Boolean 或 'high | medium | low'。默认: false)
120
- - `options`: 其他模型参数(OpenAI 可使用 `extra_body`)[详细参数配置 parameter](https://docs.ollama.com/modelfile#parameter)
153
+ - `system`: 系统提示(聊天模式)
154
+ - `options`: 其他模型参数(OpenAI 可使用 `extra_body`)
121
155
  - `temperature`: 生成温度(0-1)
122
156
  - `top_p`: 核采样概率
123
- - ...
124
157
  - `callback`: 函数 - 流式响应回调
125
158
 
126
159
  ## 配置
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huxy-llm-api",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "一个简洁、易用的用于简化 Ollama 和 OpenAI API 调用的 Node.js 库。",
5
5
  "type": "module",
6
6
  "module": "./src/index.js",
@@ -35,8 +35,8 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "ollama": "^0.6.3",
38
- "openai": "^6.16.0",
39
- "undici": "^7.18.2"
38
+ "openai": "^6.24.0",
39
+ "undici": "^7.22.0"
40
40
  },
41
41
  "engines": {
42
42
  "node": ">=20.0.0"
package/src/index.js CHANGED
@@ -1,171 +1,218 @@
1
- import {Ollama as $} from 'ollama';
2
- import q from 'openai';
3
- import {fetch as v, Agent as b} from 'undici';
4
- var E = 300 * 60 * 1e3,
5
- k = t => (i, o) => v(i, {...o, dispatcher: new b({headersTimeout: E, ...t})}),
6
- l = k;
7
- var U = {config: {apiKey: process.env.OLLM_API_KEY || 'ollm_key', host: process.env.OLLM_API_HOST}, params: {}, options: {}},
8
- g = U;
9
- var M = {config: {apiKey: process.env.LLM_API_KEY || 'llm_key', baseURL: process.env.LLM_API_BASEURL}, params: {}, options: {}},
10
- h = M;
11
- var B = ['temperature', 'seed', 'stop', 'top_p'],
12
- F = t => {
13
- let {max_tokens: i, options: o = {}, ...r} = t,
14
- {num_ctx: s, ...n} = o;
1
+ var B = Object.defineProperty;
2
+ var U = (t, o) => {
3
+ for (var e in o) B(t, e, {get: o[e], enumerable: !0});
4
+ };
5
+ import {Ollama as rt} from 'ollama';
6
+ import at from 'openai';
7
+ import {fetch as F, Agent as M} from 'undici';
8
+ var T = 1.5 * 60 * 60 * 1e3,
9
+ z = t => (o, e) => F(o, {...e, dispatcher: new M({headersTimeout: T, ...t})}),
10
+ y = z;
11
+ var N = {config: {apiKey: process.env.OLLM_API_KEY || 'ollm_key', host: process.env.OLLM_API_HOST}, params: {}, options: {}},
12
+ w = N;
13
+ var S = {config: {apiKey: process.env.LLM_API_KEY || 'llm_key', baseURL: process.env.LLM_API_BASEURL}, params: {}, options: {}},
14
+ _ = S;
15
+ var Y = ['temperature', 'seed', 'stop', 'top_p'],
16
+ H = t => {
17
+ let {max_tokens: o, options: e = {}, ...s} = t,
18
+ {num_ctx: a, ...n} = e;
15
19
  return (
16
- (r.max_tokens = i ?? s),
17
- Object.keys(n).map(e => {
18
- B.includes(e) ? (r[e] = n[e]) : (r.extra_body || (r.extra_body = {}), (r.extra_body[e] = n[e]));
20
+ (s.max_tokens = o ?? a),
21
+ Object.keys(n).map(r => {
22
+ Y.includes(r) ? (s[r] = n[r]) : (s.extra_body || (s.extra_body = {}), (s.extra_body[r] = n[r]));
19
23
  }),
20
- r
24
+ s
21
25
  );
22
26
  },
23
- S = (t, i = {}, o = 'chat') => {
27
+ V = (t, o = {}, e = 'chat') => {
24
28
  if (!t) throw Error('\u8BF7\u4F20\u5165\u4F60\u7684 prompt !');
25
- if (!i.model) throw Error('\u8BF7\u914D\u7F6E\u8981\u4F7F\u7528\u7684\u5927\u6A21\u578B model !');
26
- if (o === 'chat') {
27
- let s = Array.isArray(t) ? t : [{role: 'user', content: t}],
28
- {system: n, ...e} = i;
29
- return (n && (s = [{role: 'system', content: n}, ...s]), {messages: s, ...e});
29
+ if (!o.model) throw Error('\u8BF7\u914D\u7F6E\u8981\u4F7F\u7528\u7684\u5927\u6A21\u578B model !');
30
+ if (e === 'chat') {
31
+ let a = Array.isArray(t) ? t : [{role: 'user', content: t}],
32
+ {system: n, ...r} = o;
33
+ return (n && (a = [{role: 'system', content: n}, ...a]), {messages: a, ...r});
30
34
  }
31
- if (o === 'responses') {
32
- let {instructions: s, system: n, ...e} = i;
33
- return (s || (e.instructions = n), {input: t, ...e});
35
+ if (e === 'responses') {
36
+ let {instructions: a, system: n, ...r} = o;
37
+ return (a || (r.instructions = n), {input: t, ...r});
34
38
  }
35
- return {prompt: Array.isArray(t) ? t.slice(-1)[0]?.content : t, ...i};
39
+ return {prompt: Array.isArray(t) ? t.slice(-1)[0]?.content : t, ...o};
36
40
  };
37
- var x =
38
- ({params: t, options: i} = {}, o) =>
39
- (r, s = {}, n) => {
40
- let {options: e, extra_body: a, ...c} = s,
41
- p = S(r, {...t, ...c}, n);
42
- return ((p.options = {...i, ...a, ...e}), o === 'openai' ? F(p) : p);
41
+ var R =
42
+ ({params: t, options: o} = {}, e) =>
43
+ (s, a = {}, n) => {
44
+ let {options: r, extra_body: i, ...c} = a,
45
+ p = V(s, {...t, ...c}, n);
46
+ return ((p.options = {...o, ...i, ...r}), e === 'openai' ? H(p) : p);
43
47
  };
44
- var Y = ['response.reasoning_text.delta', 'response.reasoning_summary_text.delta'],
45
- d = async (t, i, o) => {
46
- if (i) {
47
- let s = '',
48
+ var W = ['response.reasoning_text.delta', 'response.reasoning_summary_text.delta'],
49
+ A = async (t, o, e) => {
50
+ if (o) {
51
+ let a = '',
48
52
  n = '';
49
- for await (let e of t) {
50
- let {type: a, delta: c} = e;
51
- (Y.includes(a) && (n += c), a === 'response.output_text.delta' && (s += c), o?.({content: s, reasoning: n}, e));
53
+ for await (let r of t) {
54
+ let {type: i, delta: c} = r;
55
+ (W.includes(i) && (n += c), i === 'response.output_text.delta' && (a += c), e?.({content: a, reasoning: n}, r));
52
56
  }
53
- return {content: s, reasoning: n};
57
+ return {content: a, reasoning: n};
54
58
  }
55
- return (o?.(t), {reasoning: (t.output?.[0]?.content ?? t.output?.[0]?.summary)?.[0]?.text, content: t.output_text});
59
+ return (e?.(t), {reasoning: (t.output?.[0]?.content ?? t.output?.[0]?.summary)?.[0]?.text, content: t.output_text});
56
60
  },
57
- y = async (t, i, o) => {
58
- if (i) {
59
- let e = '',
60
- a = '';
61
+ b = async (t, o, e) => {
62
+ if (o) {
63
+ let r = '',
64
+ i = '';
61
65
  for await (let c of t) {
62
66
  let {delta: p} = c.choices?.[0] ?? {},
63
- {reasoning: u, content: f} = p ?? {};
64
- (u && (a += u), f && (e += f), o?.({content: e, reasoning: a}, c));
67
+ {reasoning: m, content: f} = p ?? {};
68
+ (m && (i += m), f && (r += f), e?.({content: r, reasoning: i}, c));
65
69
  }
66
- return {content: e, reasoning: a};
70
+ return {content: r, reasoning: i};
67
71
  }
68
- o?.(t);
69
- let {message: r} = t.choices?.[0] ?? {},
70
- {content: s, reasoning: n} = r;
71
- return {content: s, reasoning: n};
72
+ e?.(t);
73
+ let {message: s} = t.choices?.[0] ?? {},
74
+ {content: a, reasoning: n} = s;
75
+ return {content: a, reasoning: n};
72
76
  };
73
- var z = ['response.reasoning_text.delta', 'response.reasoning_summary_text.delta'],
74
- _ = async (t, i, o) => {
75
- if (i) {
76
- let s = '',
77
+ var d = {};
78
+ U(d, {chat: () => u, default: () => Q, generate: () => G, image: () => J, responses: () => x});
79
+ var D = ['response.reasoning_text.delta', 'response.reasoning_summary_text.delta'],
80
+ x = async (t, o, e) => {
81
+ if (o) {
82
+ let a = '',
77
83
  n = '';
78
- for await (let e of t) {
79
- let {type: a, delta: c} = e;
80
- (z.includes(a) && (n += c), a === 'response.output_text.delta' && (s += c), o?.({content: s, reasoning: n}, e));
84
+ for await (let r of t) {
85
+ let {type: i, delta: c} = r;
86
+ (D.includes(i) && (n += c), i === 'response.output_text.delta' && (a += c), e?.({content: a, reasoning: n}, r));
81
87
  }
82
- return {content: s, reasoning: n};
88
+ return {content: a, reasoning: n};
83
89
  }
84
- return (o?.(t), {reasoning: (t.output?.[0]?.content ?? t.output?.[0]?.summary)?.[0]?.text, content: t.output_text});
90
+ return (e?.(t), {reasoning: (t.output?.[0]?.content ?? t.output?.[0]?.summary)?.[0]?.text, content: t.output_text});
85
91
  },
86
- R = async (t, i, o) => {
87
- if (i) {
92
+ G = async (t, o, e) => {
93
+ if (o) {
88
94
  let n = '',
89
- e = '';
90
- for await (let a of t) {
91
- let c = a.reasoning ?? a.thinking,
92
- p = a.content ?? a.response;
93
- (c && (e += c), p && (n += p), o?.({content: n, reasoning: e}, a));
95
+ r = '';
96
+ for await (let i of t) {
97
+ let c = i.reasoning ?? i.thinking,
98
+ p = i.content ?? i.response;
99
+ (c && (r += c), p && (n += p), e?.({content: n, reasoning: r}, i));
94
100
  }
95
- return {content: n, reasoning: e};
101
+ return {content: n, reasoning: r};
96
102
  }
97
- o?.(t);
98
- let r = t.reasoning ?? t.thinking;
99
- return {content: t.content ?? t.response, reasoning: r};
103
+ e?.(t);
104
+ let s = t.reasoning ?? t.thinking;
105
+ return {content: t.content ?? t.response, reasoning: s};
106
+ },
107
+ J = async (t, o, e) => {
108
+ for await (let s of t) e?.(s);
100
109
  },
101
- A = async (t, i, o) => {
102
- if (i) {
103
- let e = '',
104
- a = '';
110
+ u = async (t, o, e) => {
111
+ if (o) {
112
+ let r = '',
113
+ i = '';
105
114
  for await (let c of t) {
106
115
  let {message: p} = c,
107
- u = p.reasoning ?? p.thinking,
116
+ m = p.reasoning ?? p.thinking,
108
117
  f = p.content ?? p.response;
109
- (u && (a += u), f && (e += f), o?.({content: e, reasoning: a}, c));
118
+ (m && (i += m), f && (r += f), e?.({content: r, reasoning: i}, c));
110
119
  }
111
- return {content: e, reasoning: a};
120
+ return {content: r, reasoning: i};
121
+ }
122
+ let {message: s} = t;
123
+ e?.(t);
124
+ let a = s.reasoning ?? s.thinking;
125
+ return {content: s.content ?? s.response, reasoning: a};
126
+ },
127
+ Q = u;
128
+ import C from 'node:fs/promises';
129
+ var g = {'.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.webp': 'image/webp', '.bmp': 'image/bmp', '.svg': 'image/svg+xml', '.tiff': 'image/tiff'},
130
+ h = '.png',
131
+ X = t => Object.fromEntries(Object.entries(t).map(([o, e]) => [e, o])),
132
+ I = X(g);
133
+ var Z = t => {
134
+ let o = /^data:(image\/[a-z]+);base64,(.+)$/i,
135
+ e = t.match(o);
136
+ return e ? {ext: I[e[1]] ?? h, data: e[2]} : {ext: h, data: t};
137
+ },
138
+ tt = async (t, o = './images', e) => {
139
+ try {
140
+ await C.mkdir(o, {recursive: !0});
141
+ let {ext: s, data: a} = Z(t),
142
+ n = `${o}/image_${e || Date.now()}${s}`;
143
+ return (await C.writeFile(n, Buffer.from(a, 'base64')), n);
144
+ } catch (s) {
145
+ throw (console.error('\u4FDD\u5B58\u56FE\u7247\u5931\u8D25:', s.message), s);
112
146
  }
113
- let {message: r} = t;
114
- o?.(t);
115
- let s = r.reasoning ?? r.thinking;
116
- return {content: r.content ?? r.response, reasoning: s};
147
+ },
148
+ L = tt;
149
+ import {readFile as et} from 'fs/promises';
150
+ import {extname as nt} from 'path';
151
+ var ot = async (t, o = !1) => {
152
+ try {
153
+ let e = nt(t).toLowerCase();
154
+ if (!g[e]) throw new Error(`\u4E0D\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${e}`);
155
+ let s = await et(t, 'base64');
156
+ return o ? `data:${g[e]};base64,${s}` : s;
157
+ } catch (e) {
158
+ throw e.code === 'ENOENT' ? new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${t}`) : e;
159
+ }
160
+ },
161
+ O = ot;
162
+ var st = t => (t?.startsWith('x/') ? 'image' : 'generate'),
163
+ E = {
164
+ openai: (t, o) => ({
165
+ chat: async (e, s = {}, a) => {
166
+ let n = o(e, s, 'chat'),
167
+ r = b,
168
+ i = await t.chat.completions.create(n);
169
+ return r(i, n.stream, a);
170
+ },
171
+ responses: async (e, s = {}, a) => {
172
+ let n = o(e, s, 'responses'),
173
+ r = A,
174
+ i = await t.responses.create(n);
175
+ return r(i, n.stream, a);
176
+ },
177
+ }),
178
+ ollama: (t, o) => ({
179
+ chat: async (e, s = {}, a) => {
180
+ let n = o(e, s, 'chat'),
181
+ r = u,
182
+ i = await t.chat(n);
183
+ return r(i, n.stream, a);
184
+ },
185
+ generate: async (e, s = {}, a) => {
186
+ let n = o(e, s, 'generate'),
187
+ r = d[st(n.model)],
188
+ i = await t.generate(n);
189
+ return r(i, n.stream, a);
190
+ },
191
+ responses: async (e, s = {}, a) => {
192
+ let n = o(e, s, 'responses'),
193
+ r = x,
194
+ i = await t.responses(n);
195
+ return r(i, n.stream, a);
196
+ },
197
+ saveImage: L,
198
+ imageToBase64: O,
199
+ }),
117
200
  };
118
- var w = {
119
- openai: (t, i) => ({
120
- chat: async (o, r = {}, s) => {
121
- let n = i(o, r, 'chat'),
122
- e = y,
123
- a = await t.chat.completions.create(n);
124
- return e(a, n.stream, s);
125
- },
126
- responses: async (o, r = {}, s) => {
127
- let n = i(o, r, 'responses'),
128
- e = d,
129
- a = await t.responses.create(n);
130
- return e(a, n.stream, s);
131
- },
132
- }),
133
- ollama: (t, i) => ({
134
- chat: async (o, r = {}, s) => {
135
- let n = i(o, r, 'chat'),
136
- e = A,
137
- a = await t.chat(n);
138
- return e(a, n.stream, s);
139
- },
140
- generate: async (o, r = {}, s) => {
141
- let n = i(o, r, 'generate'),
142
- e = R,
143
- a = await t.generate(n);
144
- return e(a, n.stream, s);
145
- },
146
- responses: async (o, r = {}, s) => {
147
- let n = i(o, r, 'responses'),
148
- e = _,
149
- a = await t.responses(n);
150
- return e(a, n.stream, s);
151
- },
152
- }),
153
- };
154
- var D = {
155
- ollama: {hostKey: 'host', envConfig: g, API: ({apiKey: t, headers: i, ...o}) => new $({headers: {Authorization: t ? `Bearer ${t}` : void 0, ...i}, ...o})},
156
- openai: {hostKey: 'baseURL', envConfig: h, API: t => new q(t)},
201
+ var it = {
202
+ ollama: {hostKey: 'host', envConfig: w, API: ({apiKey: t, headers: o, ...e}) => new rt({headers: {Authorization: t ? `Bearer ${t}` : void 0, ...o}, ...e})},
203
+ openai: {hostKey: 'baseURL', envConfig: _, API: t => new at(t)},
157
204
  },
158
- G = (t = 'ollama', i = {}, o = {}) => {
205
+ ct = (t = 'ollama', o = {}, e = {}) => {
159
206
  t = ['ollama', 'openai'].includes(t) ? t : 'ollama';
160
- let {hostKey: r, envConfig: s, API: n} = D[t],
161
- {config: e, params: a, options: c} = s,
162
- {baseURL: p, host: u, dispatcher: f, ...m} = {...e, ...i};
163
- if (((m[r] = u || p), !m[r])) throw Error('\u8BF7\u914D\u7F6E\u5927\u6A21\u578B API \u5730\u5740 host/baseURL !');
164
- let I = n({fetch: l(f), ...m}),
165
- {options: P, extra_body: C, ...L} = o,
166
- O = {params: {...a, ...L}, options: {...c, ...C, ...P}},
167
- K = x(O, t);
168
- return w[t](I, K);
207
+ let {hostKey: s, envConfig: a, API: n} = it[t],
208
+ {config: r, params: i, options: c} = a,
209
+ {baseURL: p, host: m, dispatcher: f, ...l} = {...r, ...o};
210
+ if (((l[s] = m || p), !l[s])) throw Error('\u8BF7\u914D\u7F6E\u5927\u6A21\u578B API \u5730\u5740 host/baseURL !');
211
+ let P = n({fetch: y(f), ...l}),
212
+ {options: v, extra_body: K, ...$} = e,
213
+ j = {params: {...i, ...$}, options: {...c, ...K, ...v}},
214
+ k = R(j, t);
215
+ return E[t](P, k);
169
216
  },
170
- rt = G;
171
- export {rt as default, G as startApi};
217
+ jt = ct;
218
+ export {jt as default, ct as startApi};