@nickyzj2023/utils 1.0.74 → 1.0.76
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/index.d.mts +88 -70
- package/dist/index.mjs +5 -5
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
//#region src/ai/
|
|
2
|
-
declare namespace
|
|
1
|
+
//#region src/ai/types.d.ts
|
|
2
|
+
declare namespace AI {
|
|
3
3
|
type Model = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
apiKey?: string;
|
|
4
|
+
baseUrl: string; /** 不传则自动使用{baseUrl}/models接口的第一个模型 */
|
|
5
|
+
model?: string;
|
|
6
|
+
apiKey?: string; /** POST /chat/completions时注入自定义请求体 */
|
|
7
|
+
customBody?: Record<string, any>; /** 模型支持的消息输入类型,如果填了,则会在调用chatCompletions前校验上下文,含有不支持的输入时抑制请求 */
|
|
8
|
+
inputs?: ["text" | "image" | "video" | "audio" | "file"]; /** 模型的最大上下文(输入+输出),如果填了,则会在调用chatCompletions后选择性调用compact(当前上下文>最大上下文*80%时) */
|
|
9
|
+
context?: number;
|
|
10
|
+
};
|
|
11
|
+
type Message = {
|
|
12
|
+
role: "system" | "user" | "assistant" | "tool" | "function"; /** OpenRouter的思考内容字段,其他供应商的会尽可能合并到该字段内 */
|
|
13
|
+
reasoning?: string | null;
|
|
14
|
+
content: string | ContentPart[];
|
|
15
|
+
tool_calls?: ToolCall[];
|
|
16
|
+
tool_call_id?: string;
|
|
17
|
+
[key: string]: unknown;
|
|
7
18
|
};
|
|
8
19
|
type TextContent = {
|
|
9
20
|
type: "text";
|
|
@@ -18,7 +29,7 @@ declare namespace ChatCompletions {
|
|
|
18
29
|
type AudioContent = {
|
|
19
30
|
type: "input_audio";
|
|
20
31
|
input_audio: {
|
|
21
|
-
/**
|
|
32
|
+
/** 使用公网可访问的音频链接 */url?: string; /** 使用base64 */
|
|
22
33
|
data?: string;
|
|
23
34
|
format: string;
|
|
24
35
|
};
|
|
@@ -30,63 +41,61 @@ declare namespace ChatCompletions {
|
|
|
30
41
|
};
|
|
31
42
|
};
|
|
32
43
|
type ContentPart = TextContent | ImageContent | AudioContent | VideoContent;
|
|
33
|
-
type
|
|
34
|
-
role: "system" | "user" | "assistant" | "tool" | "function"; /** 字节的思考字段 */
|
|
35
|
-
reasoning_content?: string | null; /** OpenRouter的思考字段 */
|
|
36
|
-
reasoning?: string | null;
|
|
37
|
-
content: string | ContentPart[];
|
|
38
|
-
name?: string;
|
|
39
|
-
tool_calls?: ToolCall[];
|
|
40
|
-
tool_call_id?: string;
|
|
41
|
-
};
|
|
42
|
-
type ToolCall = {
|
|
43
|
-
id: string;
|
|
44
|
+
type ToolDefinition = {
|
|
44
45
|
type: "function";
|
|
45
46
|
function: {
|
|
46
47
|
name: string;
|
|
47
|
-
|
|
48
|
+
description: string;
|
|
49
|
+
parameters: {
|
|
50
|
+
type: "object";
|
|
51
|
+
properties: Record<string, {
|
|
52
|
+
type: string;
|
|
53
|
+
description?: string; /** 在此处设置的required,发出请求前会自动提到外面去 */
|
|
54
|
+
required?: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
required?: string[];
|
|
57
|
+
};
|
|
48
58
|
};
|
|
59
|
+
handler: (...args: any) => any;
|
|
49
60
|
};
|
|
50
|
-
type
|
|
61
|
+
type ToolCall = {
|
|
62
|
+
id: string;
|
|
51
63
|
type: "function";
|
|
52
64
|
function: {
|
|
53
65
|
name: string;
|
|
54
|
-
|
|
55
|
-
parameters?: Record<string, any>;
|
|
66
|
+
arguments: string;
|
|
56
67
|
};
|
|
57
68
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
type Response = {
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/ai/chatCompletions/types.d.ts
|
|
72
|
+
declare namespace ChatCompletions {
|
|
73
|
+
/** 非流式POST /chat/completions的响应结果 */
|
|
74
|
+
type NonStreamResponse = {
|
|
65
75
|
id: string;
|
|
66
76
|
object: "chat.completion";
|
|
67
77
|
created: number;
|
|
68
78
|
model: string;
|
|
69
79
|
choices: Array<{
|
|
70
80
|
index: number;
|
|
71
|
-
message: Message;
|
|
81
|
+
message: AI.Message;
|
|
72
82
|
finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | null;
|
|
73
83
|
}>;
|
|
74
84
|
usage: Usage;
|
|
75
85
|
system_fingerprint?: string;
|
|
76
86
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
stream?: boolean; /** 其他额外参数 */
|
|
81
|
-
[key: string]: any;
|
|
82
|
-
};
|
|
83
|
-
/** 调用 chatCompletions 返回的结果,流式/非流式通用 */
|
|
84
|
-
type Result = {
|
|
85
|
-
/** 模型的最终回复内容(多模态时取所有 text 拼接) */content: string; /** Token 消耗情况 */
|
|
87
|
+
/** 调用chatCompletions返回的结果,流式/非流式通用 */
|
|
88
|
+
type NonStreamResult = {
|
|
89
|
+
/** 模型的最终回复内容(多模态时取所有text拼接) */content: string; /** Token 消耗情况 */
|
|
86
90
|
usage: Usage; /** 原始响应中的其他字段 */
|
|
87
91
|
[key: string]: any;
|
|
88
92
|
};
|
|
89
|
-
|
|
93
|
+
type Usage = {
|
|
94
|
+
prompt_tokens: number;
|
|
95
|
+
completion_tokens: number;
|
|
96
|
+
total_tokens: number;
|
|
97
|
+
};
|
|
98
|
+
/** 流式响应中的单个SSE数据块(OpenAI原始格式) */
|
|
90
99
|
type StreamResponse = {
|
|
91
100
|
id: string;
|
|
92
101
|
object: "chat.completion.chunk";
|
|
@@ -94,28 +103,18 @@ declare namespace ChatCompletions {
|
|
|
94
103
|
model: string;
|
|
95
104
|
choices: Array<{
|
|
96
105
|
index: number;
|
|
97
|
-
delta: {
|
|
98
|
-
role?: Message["role"];
|
|
99
|
-
content?: string | null; /** 字节的思考字段 */
|
|
100
|
-
reasoning_content?: string | null; /** OpenRouter的思考字段 */
|
|
101
|
-
reasoning?: string | null;
|
|
106
|
+
delta: Pick<AI.Message, "role" | "reasoning" | "content"> & {
|
|
102
107
|
tool_calls?: Array<{
|
|
103
108
|
index: number;
|
|
104
|
-
|
|
105
|
-
type?: "function";
|
|
106
|
-
function?: {
|
|
107
|
-
name?: string;
|
|
108
|
-
arguments?: string;
|
|
109
|
-
};
|
|
110
|
-
}>;
|
|
109
|
+
} & Partial<AI.ToolCall>>;
|
|
111
110
|
};
|
|
112
111
|
finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | null;
|
|
113
112
|
}>;
|
|
114
113
|
usage?: Usage;
|
|
115
114
|
};
|
|
116
|
-
/** 流式调用
|
|
115
|
+
/** 流式调用chatCompletions时迭代器产出的数据块 */
|
|
117
116
|
type StreamChunk = {
|
|
118
|
-
/** 模型流式返回的思考内容增量(仅在生成过程中出现) */
|
|
117
|
+
/** 模型流式返回的思考内容增量(仅在生成过程中出现) */reasoning?: string; /** 模型流式返回的内容增量(仅在生成过程中出现) */
|
|
119
118
|
content?: string; /** Token 消耗情况(仅在最后一帧出现) */
|
|
120
119
|
usage?: Usage;
|
|
121
120
|
};
|
|
@@ -123,22 +122,23 @@ declare namespace ChatCompletions {
|
|
|
123
122
|
//#endregion
|
|
124
123
|
//#region src/ai/chatCompletions/index.d.ts
|
|
125
124
|
/**
|
|
126
|
-
* 兼容
|
|
125
|
+
* 兼容OpenAI API的聊天补全函数
|
|
127
126
|
* - 自动处理工具调用
|
|
128
|
-
* -
|
|
127
|
+
* - 支持普通/流式响应
|
|
129
128
|
*
|
|
130
|
-
* @param model 模型配置,包含
|
|
131
|
-
* @param messages OpenAI API
|
|
132
|
-
* @param extraBody 可选的额外参数,如
|
|
133
|
-
* @returns
|
|
129
|
+
* @param model 模型配置,包含model、baseUrl、apiKey
|
|
130
|
+
* @param messages OpenAI API兼容的消息数组
|
|
131
|
+
* @param extraBody 可选的额外参数,如tools、temperature、stream等
|
|
132
|
+
* @returns 普通模式下返回`{ content, usage, ... }`;`stream: true`时返回异步迭代器
|
|
134
133
|
*
|
|
135
134
|
* @example
|
|
136
135
|
* // 最简调用
|
|
137
|
-
* // 未填写模型名,会自动使用/v1/models
|
|
138
|
-
* const { content, usage } = await chatCompletions(
|
|
136
|
+
* // 未填写模型名,会自动使用/v1/models返回的第一个模型
|
|
137
|
+
* const { reasoning, content, usage } = await chatCompletions(
|
|
139
138
|
* { baseUrl: "http://127.0.0.1:11434/v1" },
|
|
140
139
|
* [{ role: "user", content: "你好" }],
|
|
141
140
|
* );
|
|
141
|
+
* console.log(reasoning); // "The user said..."
|
|
142
142
|
* console.log(content); // "你好!有什么我可以帮你的吗?"
|
|
143
143
|
* console.log(usage); // { prompt_tokens: 13, completion_tokens: 9, total_tokens: 22 }
|
|
144
144
|
*
|
|
@@ -154,11 +154,9 @@ declare namespace ChatCompletions {
|
|
|
154
154
|
* name: "getWeather",
|
|
155
155
|
* description: "查询城市天气情况",
|
|
156
156
|
* parameters: { type: "object", properties: { city: { type: "string" } } },
|
|
157
|
+
* handler: (args) => `${args.city}今日晴转多云,25°C`,
|
|
157
158
|
* },
|
|
158
159
|
* }],
|
|
159
|
-
* toolHandlers: {
|
|
160
|
-
* getWeather: (args) => `${args.city}今日晴转多云,25°C`,
|
|
161
|
-
* },
|
|
162
160
|
* },
|
|
163
161
|
* );
|
|
164
162
|
*
|
|
@@ -177,14 +175,30 @@ declare namespace ChatCompletions {
|
|
|
177
175
|
* }
|
|
178
176
|
* }
|
|
179
177
|
*/
|
|
180
|
-
declare function chatCompletions(model:
|
|
178
|
+
declare function chatCompletions(model: AI.Model, messages: AI.Message[], options: {
|
|
181
179
|
stream: true;
|
|
180
|
+
tools?: AI.ToolDefinition[];
|
|
182
181
|
}): Promise<AsyncGenerator<ChatCompletions.StreamChunk>>;
|
|
183
|
-
declare function chatCompletions(model:
|
|
182
|
+
declare function chatCompletions(model: AI.Model, messages: AI.Message[], options?: {
|
|
183
|
+
stream?: boolean;
|
|
184
|
+
tools?: AI.ToolDefinition[];
|
|
185
|
+
}): Promise<ChatCompletions.NonStreamResult>;
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/ai/helper.d.ts
|
|
184
188
|
/**
|
|
185
|
-
* 辅助定义一个
|
|
189
|
+
* 辅助定义一个POST /chat/completions支持的model参数
|
|
190
|
+
* @remarks 只有baseUrl字段是必须的,其他字段请查看AI.Model类型
|
|
186
191
|
*/
|
|
187
|
-
declare const defineModel: (config:
|
|
192
|
+
declare const defineModel: (config: AI.Model) => AI.Model;
|
|
193
|
+
/**
|
|
194
|
+
* 辅助定义一个POST /chat/completions支持的tools中的子元素
|
|
195
|
+
* @param handler 在AI请求调用工具时用到
|
|
196
|
+
*/
|
|
197
|
+
declare const defineTool: (name: AI.ToolDefinition["function"]["name"], description: AI.ToolDefinition["function"]["description"], properties: AI.ToolDefinition["function"]["parameters"]["properties"], handler: AI.ToolDefinition["handler"]) => AI.ToolDefinition;
|
|
198
|
+
/**
|
|
199
|
+
* 从GET /models获取模型名称
|
|
200
|
+
*/
|
|
201
|
+
declare const getModelName: (baseUrl: string) => Promise<string>;
|
|
188
202
|
//#endregion
|
|
189
203
|
//#region src/dom/logger.d.ts
|
|
190
204
|
/**
|
|
@@ -342,11 +356,15 @@ type Primitive = number | string | boolean | symbol | bigint | undefined | null;
|
|
|
342
356
|
declare const isPrimitive: (value: any) => value is Primitive;
|
|
343
357
|
//#endregion
|
|
344
358
|
//#region src/network/fetcher.d.ts
|
|
345
|
-
type RequestInit = globalThis.RequestInit & {
|
|
359
|
+
type RequestInit = Omit<globalThis.RequestInit, "body"> & {
|
|
346
360
|
/**
|
|
347
361
|
* searchParams 查询参数对象
|
|
348
362
|
*/
|
|
349
363
|
params?: Record<string, any>;
|
|
364
|
+
/**
|
|
365
|
+
* 请求体支持任意类型
|
|
366
|
+
*/
|
|
367
|
+
body?: any;
|
|
350
368
|
/**
|
|
351
369
|
* 响应解析器,默认的解析方法为 response.json()
|
|
352
370
|
*/
|
|
@@ -812,4 +830,4 @@ declare const sleep: (time?: number) => Promise<unknown>;
|
|
|
812
830
|
*/
|
|
813
831
|
declare const throttle: <T extends (...args: any[]) => any>(fn: T, delay?: number) => (this: any, ...args: Parameters<T>) => void;
|
|
814
832
|
//#endregion
|
|
815
|
-
export { CamelToSnake, Capitalize, type ChatCompletions, Decapitalize, DeepMapKeys, DeepMapValues, ImageCompressionOptions, LockQueue, LoggerOptions, Primitive, RequestInit, SetTtl, SnakeToCamel, camelToSnake, capitalize, chatCompletions, compactStr, debounce, decapitalize, defineModel, extractErrorMessage, fetcher, getRealURL, imageUrlToBase64, isNil, isObject, isPrimitive, logger, loopUntil, mapKeys, mapValues, mergeObjects, omit, omitBy, parseSSE, pick, pickBy, qs, randomInt, sleep, snakeToCamel, throttle, to, withCache };
|
|
833
|
+
export { AI, CamelToSnake, Capitalize, type ChatCompletions, Decapitalize, DeepMapKeys, DeepMapValues, ImageCompressionOptions, LockQueue, LoggerOptions, Primitive, RequestInit, SetTtl, SnakeToCamel, camelToSnake, capitalize, chatCompletions, compactStr, debounce, decapitalize, defineModel, defineTool, extractErrorMessage, fetcher, getModelName, getRealURL, imageUrlToBase64, isNil, isObject, isPrimitive, logger, loopUntil, mapKeys, mapValues, mergeObjects, omit, omitBy, parseSSE, pick, pickBy, qs, randomInt, sleep, snakeToCamel, throttle, to, withCache };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const e=e=>e==null,t=e=>e?.constructor===Object,n=e=>e==null||typeof e!=`object`&&typeof e!=`function`,r=(e,n)=>Array.isArray(e)?e.map(e=>r(e,n)):t(e)?Object.keys(e).reduce((t,i)=>{let a=n(i),o=e[i];return t[a]=r(o,n),t},{}):e,i=(e,n,r)=>{let{filter:a}=r??{};if(Array.isArray(e)){let o=e.map((e,a)=>t(e)?i(e,n,r):n(e,a));return a?o.filter((e,t)=>a(e,t)):o}return t(e)?Object.keys(e).reduce((o,s)=>{let c=e[s],l;return l=t(c)||Array.isArray(c)?i(c,n,r):n(c,s),(!a||a(l,s))&&(o[s]=l),o},{}):e},a=(e,r)=>{let i={...e};for(let e of Object.keys(r)){let o=i[e],s=r[e];if(n(o)&&n(s)){i[e]=s;continue}if(Array.isArray(o)&&Array.isArray(s)){i[e]=o.concat(s);continue}if(t(o)&&t(s)){i[e]=a(o,s);continue}i[e]=s}return i},o=(e,t)=>{let n={...e};for(let e of t)delete n[e];return n},s=(e,t)=>{let n={};for(let[r,i]of Object.entries(e))t(r,i)||(n[r]=i);return n},c=(e,t)=>t.reduce((t,n)=>(Object.hasOwn(e,n)&&(t[n]=e[n]),t),{}),l=(e,t)=>{let n={};for(let[r,i]of Object.entries(e))t(r,i)&&(n[r]=i);return n},u=
|
|
1
|
+
const e=e=>e==null,t=e=>e?.constructor===Object,n=e=>e==null||typeof e!=`object`&&typeof e!=`function`,r=(e,n)=>Array.isArray(e)?e.map(e=>r(e,n)):t(e)?Object.keys(e).reduce((t,i)=>{let a=n(i),o=e[i];return t[a]=r(o,n),t},{}):e,i=(e,n,r)=>{let{filter:a}=r??{};if(Array.isArray(e)){let o=e.map((e,a)=>t(e)?i(e,n,r):n(e,a));return a?o.filter((e,t)=>a(e,t)):o}return t(e)?Object.keys(e).reduce((o,s)=>{let c=e[s],l;return l=t(c)||Array.isArray(c)?i(c,n,r):n(c,s),(!a||a(l,s))&&(o[s]=l),o},{}):e},a=(e,r)=>{let i={...e};for(let e of Object.keys(r)){let o=i[e],s=r[e];if(n(o)&&n(s)){i[e]=s;continue}if(Array.isArray(o)&&Array.isArray(s)){i[e]=o.concat(s);continue}if(t(o)&&t(s)){i[e]=a(o,s);continue}i[e]=s}return i},o=(e,t)=>{let n={...e};for(let e of t)delete n[e];return n},s=(e,t)=>{let n={};for(let[r,i]of Object.entries(e))t(r,i)||(n[r]=i);return n},c=(e,t)=>t.reduce((t,n)=>(Object.hasOwn(e,n)&&(t[n]=e[n]),t),{}),l=(e,t)=>{let n={};for(let[r,i]of Object.entries(e))t(r,i)&&(n[r]=i);return n},u=e=>e.replace(/_([a-zA-Z])/g,(e,t)=>t.toUpperCase()),d=e=>e.replace(/([A-Z])/g,(e,t)=>`_${t.toLowerCase()}`),f=e=>e.charAt(0).toUpperCase()+e.slice(1),p=e=>e.charAt(0).toLowerCase()+e.slice(1),m=(e=``,t)=>{if(!e)return``;let{maxLength:n=1/0,disableNewLineReplace:r=!1,disableCollapse:i=!1}=t??{},a=e;return i||(a=a.replace(/[\n\t]+/g,`
|
|
2
|
+
`)),a=r?a.replace(/\r?\n/g,` `):a.replace(/\r?\n/g,`\\n`),a=a.replace(/\s+/g,` `).trim(),n>0&&a.length>n?`${a.slice(0,n)}...`:a},h=e=>{if(e instanceof Error)return e.message;if(typeof e==`string`)return e;if(t(e)){let t=e.message||e.msg;if(t)return t;for(let t of Object.values(e)){let e=h(t);if(e)return e}}return JSON.stringify(e,null,2)},g={parse:e=>{let t=new URLSearchParams(e),n={};for(let[e,r]of t)Number.isNaN(Number(r))?n[e]=r:n[e]=Number(r);return n},stringify:(e,t)=>{let{addQueryPrefix:n=!1}=t??{},r=new URLSearchParams(e).toString();return r?n?`?${r}`:r:``}},_=(n=``,r={})=>{let i=async(i,o={})=>{let s=new URL(n?`${n}${i}`:i),{params:c,parser:l,...u}=a(r,o);t(c)&&Object.entries(c).forEach(([t,n])=>{e(n)||s.searchParams.append(t,n.toString())}),(t(u.body)||Array.isArray(u.body))&&(u.body=JSON.stringify(u.body),u.headers={...u.headers,"Content-Type":`application/json`});let d=await fetch(s,u);if(!d.ok){let e=d.statusText||await d.text();try{e=h(JSON.parse(e))}catch{}throw Error(e)}return await(l?.(d)??d.json())};return{get:(e,t)=>i(e,{...t,method:`GET`}),post:(e,t,n)=>i(e,{...n,method:`POST`,body:t}),put:(e,t,n)=>i(e,{...n,method:`PUT`,body:t}),delete:(e,t)=>i(e,{...t,method:`DELETE`})}},v=async e=>{try{return[null,await e]}catch(e){return[e,void 0]}},y=async e=>{let[t,n]=await v(fetch(e,{method:`HEAD`,redirect:`manual`}));return t?e:n.headers.get(`location`)||e},b=e=>{let t=new Uint8Array(e),n=``;for(let e=0;e<t.byteLength;e++)n+=String.fromCharCode(t[e]);return btoa(n)},x=async(e,t={})=>{let{quality:n=.92,compressor:r,fetcher:i=fetch}=t;if(!e.startsWith(`http`))throw Error(`图片地址必须以http或https开头`);let a=await i(e);if(!a.ok)throw Error(`获取图片失败: ${a.statusText}`);let o=a.headers.get(`Content-Type`)||`image/jpeg`,s=await a.arrayBuffer();if(o!==`image/jpeg`&&o!==`image/png`)return`data:${o};base64,${b(s)}`;if(r)return await r(s,o,n);if(typeof OffscreenCanvas<`u`){let e=null;try{let t=new Blob([s],{type:o});e=await createImageBitmap(t);let r=new OffscreenCanvas(e.width,e.height),i=r.getContext(`2d`);if(!i)throw Error(`无法获取 OffscreenCanvas context`);return i.drawImage(e,0,0),e.close(),e=null,`data:${o};base64,${b(await(await r.convertToBlob({type:o,quality:n})).arrayBuffer())}`}catch{return e?.close(),`data:${o};base64,${b(s)}`}}return`data:${o};base64,${b(s)}`};async function*S(e){let t=e.body?.getReader();if(!t)return;let n=new TextDecoder,r=``;try{for(;;){let{value:e,done:i}=await t.read();if(i)break;r+=n.decode(e,{stream:!0});let a=r.split(`
|
|
2
3
|
|
|
3
4
|
`);r=a.pop()||``;for(let e of a){let t=e.split(`
|
|
4
|
-
`);for(let e of t){if(!e.startsWith(`data:`))continue;let t=e.replace(/^data:\s*/,``).trim();try{yield JSON.parse(t)}catch{yield t}}}}}finally{t.releaseLock()}}const
|
|
5
|
-
`)
|
|
6
|
-
`)
|
|
7
|
-
`)[2+t]?.trim())?.match(/^at\s+(?:.*?\s*\()?(.*?):(\d+)(?::(\d+))?\)?/);if(n?.[1]){let e=n[1].split(/[/\\]/).pop();i.push(`[${e}:${n[2]}]`)}}i.push(...e),console.log(...i)}}function j(e,...n){if(t(e)&&(`withTime`in e||`withFileName`in e))return A(e,0);A({},1)(e,...n)}const M=async(e,t)=>{let{maxRetries:n=5,shouldStop:r}=t??{},i;for(let t=0;t<n;t++)if(i=await e(t),r?.(i)===!0)return i;if(!r)return i;throw Error(`超过了最大循环次数(${n})且未满足停止执行条件`)},N=(e,t=-1)=>{let n=new Map,r=(...r)=>{let i=JSON.stringify(r),a=Date.now(),o=n.get(i);if(o&&a<o.expiresAt)return o.value;let s=t===-1?1/0:a+t*1e3,c=e.apply({setTtl:e=>{s=a+e*1e3}},r);if(c instanceof Promise){let e=c.then(e=>(n.set(i,{value:e,expiresAt:s}),e));return n.set(i,{value:e,expiresAt:s}),e}return n.set(i,{value:c,expiresAt:s}),c};return r.clear=()=>n.clear(),r.updateTtl=e=>{t=e;let r=Date.now(),i=r+e*1e3;for(let[e,t]of n.entries())t.expiresAt>r&&(t.expiresAt=i,n.set(e,t))},r},P=(e,t)=>Math.floor(Math.random()*(t-e+1))+e,F=(e,t=300)=>{let n=null;return(...r)=>{n&&clearTimeout(n),n=setTimeout(()=>{e(...r)},t)}};var I=class{queue;constructor(){this.queue=Promise.resolve()}waitInQueue(){let e,t=new Promise(t=>{e=t}),n=this.queue.then(()=>e);return this.queue=t,n}};const L=async(e=150)=>new Promise(t=>{setTimeout(t,e)}),R=(e,t=300)=>{let n=null;return function(...r){n||=setTimeout(()=>{n=null,e.apply(this,r)},t)}};export{I as LockQueue,_ as camelToSnake,v as capitalize,O as chatCompletions,b as compactStr,F as debounce,y as decapitalize,k as defineModel,x as extractErrorMessage,u as fetcher,f as getRealURL,m as imageUrlToBase64,e as isNil,t as isObject,n as isPrimitive,j as logger,M as loopUntil,r as mapKeys,i as mapValues,a as mergeObjects,o as omit,s as omitBy,h as parseSSE,c as pick,l as pickBy,S as qs,P as randomInt,L as sleep,g as snakeToCamel,R as throttle,d as to,N as withCache};
|
|
5
|
+
`);for(let e of t){if(!e.startsWith(`data:`))continue;let t=e.replace(/^data:\s*/,``).trim();try{yield JSON.parse(t)}catch{yield t}}}}}finally{t.releaseLock()}}const C=e=>e,w=(e,t,n,r)=>{let i=[];return{type:`function`,function:{name:e,description:t,parameters:{type:`object`,properties:l(n,e=>e===`required`?(i.push(e),!1):!0),required:i}},handler:r}},T=async e=>{let t=(await _(e).get(`/v1/models`)).data[0]?.id;if(!t)throw Error(`无法从/models获取模型名称`);return t},E=e=>[c(e,[`type`,`function`]),c(e,[`handler`])],D=async(e,t,n)=>{let r=t.find(t=>t.function.name===e.function.name)?.handler;if(!r)return`没有找到工具“${e.function.name}”的处理函数`;try{let t=await r(JSON.parse(e.function.arguments),n);return typeof t==`string`?t:JSON.stringify(t)}catch(t){if(t instanceof Error&&t.name===e.function.name)throw t;return`工具“${e.function.name}”处理失败:${h(t)}`}},O=e=>e.reasoning||e.reasoning_content,k=e=>typeof e==`string`?e:e.filter(e=>e.type===`text`).map(e=>e.text).join(`
|
|
6
|
+
`),A=async(e,t,n=[])=>{for(;;){let{choices:r,usage:i,...a}=await e.post(`/chat/completions`,{}),{message:o}=r?.[0]??{};if(!o)throw Error(`模型没有回复任何内容`);t.push(o);let{content:s=``,tool_calls:c=[],...l}=o,u=O(o);if(c.length>0&&n.length>0){for(let e of c){let r=await D(e,n,{messages:t});t.push({role:`tool`,content:r,tool_call_id:e.id})}continue}return{content:k(s),reasoning:u,usage:i,...a,...l}}},j=async function*(e,t,n=[]){for(;;){let r=new Map,i=``,a=null,o,s=await e.post(`/chat/completions`,{stream:!0},{parser:async e=>e});for await(let e of S(s)){e.usage&&(o=e.usage);let t=e.choices?.[0];if(!t)continue;let{delta:n}=t,{content:s,tool_calls:c}=n,l=O(n);if(l&&(yield{reasoning:l}),s&&(i+=s,yield{content:s}),c)for(let e of c){let t=r.get(e.index)??{id:``,type:`function`,function:{name:``,arguments:``}};e.id&&(t.id=e.id),e.function?.name&&(t.function.name+=e.function.name),e.function?.arguments&&(t.function.arguments+=e.function.arguments),r.set(e.index,t)}t.finish_reason&&(a=t.finish_reason)}let c=Array.from(r.values());if(a===`tool_calls`&&c.length>0&&n.length>0){t.push({role:`assistant`,content:i,tool_calls:c});for(let e of c){let r=await D(e,n,{messages:t});t.push({role:`tool`,content:r,tool_call_id:e.id})}continue}t.push({role:`assistant`,content:i}),o&&(yield{usage:o});break}};async function M(e,t,n){let{baseUrl:r,apiKey:i=``,model:a,...o}=e,{stream:s,tools:c=[]}=n??{},l=_(r,{headers:{Authorization:`Bearer ${i}`},body:{model:a??await T(r),messages:t,tools:c?.map(e=>E(e)[0]),...o.customBody}});return(s?j:A)(l,t,c)}function N(e={},t=0){let{withTime:n=!0,withFileName:r=!0}=e;return(...e)=>{let i=[];if(n&&i.push(`[${new Date().toLocaleTimeString()}]`),r){let{stack:e}=Error(),n=(e?.split(`
|
|
7
|
+
`)[2+t]?.trim())?.match(/^at\s+(?:.*?\s*\()?(.*?):(\d+)(?::(\d+))?\)?/);if(n?.[1]){let e=n[1].split(/[/\\]/).pop();i.push(`[${e}:${n[2]}]`)}}i.push(...e),console.log(...i)}}function P(e,...n){if(t(e)&&(`withTime`in e||`withFileName`in e))return N(e,0);N({},1)(e,...n)}const F=async(e,t)=>{let{maxRetries:n=5,shouldStop:r}=t??{},i;for(let t=0;t<n;t++)if(i=await e(t),r?.(i)===!0)return i;if(!r)return i;throw Error(`超过了最大循环次数(${n})且未满足停止执行条件`)},I=(e,t=-1)=>{let n=new Map,r=(...r)=>{let i=JSON.stringify(r),a=Date.now(),o=n.get(i);if(o&&a<o.expiresAt)return o.value;let s=t===-1?1/0:a+t*1e3,c=e.apply({setTtl:e=>{s=a+e*1e3}},r);if(c instanceof Promise){let e=c.then(e=>(n.set(i,{value:e,expiresAt:s}),e));return n.set(i,{value:e,expiresAt:s}),e}return n.set(i,{value:c,expiresAt:s}),c};return r.clear=()=>n.clear(),r.updateTtl=e=>{t=e;let r=Date.now(),i=r+e*1e3;for(let[e,t]of n.entries())t.expiresAt>r&&(t.expiresAt=i,n.set(e,t))},r},L=(e,t)=>Math.floor(Math.random()*(t-e+1))+e,R=(e,t=300)=>{let n=null;return(...r)=>{n&&clearTimeout(n),n=setTimeout(()=>{e(...r)},t)}};var z=class{queue;constructor(){this.queue=Promise.resolve()}waitInQueue(){let e,t=new Promise(t=>{e=t}),n=this.queue.then(()=>e);return this.queue=t,n}};const B=async(e=150)=>new Promise(t=>{setTimeout(t,e)}),V=(e,t=300)=>{let n=null;return function(...r){n||=setTimeout(()=>{n=null,e.apply(this,r)},t)}};export{z as LockQueue,d as camelToSnake,f as capitalize,M as chatCompletions,m as compactStr,R as debounce,p as decapitalize,C as defineModel,w as defineTool,h as extractErrorMessage,_ as fetcher,T as getModelName,y as getRealURL,x as imageUrlToBase64,e as isNil,t as isObject,n as isPrimitive,P as logger,F as loopUntil,r as mapKeys,i as mapValues,a as mergeObjects,o as omit,s as omitBy,S as parseSSE,c as pick,l as pickBy,g as qs,L as randomInt,B as sleep,u as snakeToCamel,V as throttle,v as to,I as withCache};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nickyzj2023/utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.76",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"url": "https://github.com/Nickyzj628/utils.git"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/node": "^25.9.
|
|
28
|
-
"tsdown": "^0.22.
|
|
27
|
+
"@types/node": "^25.9.3",
|
|
28
|
+
"tsdown": "^0.22.2",
|
|
29
29
|
"typedoc": "^0.28.19",
|
|
30
30
|
"typedoc-material-theme": "^1.4.1",
|
|
31
31
|
"typescript": "^6.0.3"
|