@nickyzj2023/utils 1.0.65 → 1.0.67

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 CHANGED
@@ -1,31 +1,35 @@
1
- 男生自用全新前端工具库,0依赖
2
-
3
- 安装到你的前端项目里:
4
-
5
- ```bash
6
- # npm
7
- npm install @nickyzj2023/utils
8
-
9
- # yarn
10
- yarn add @nickyzj2023/utils
11
-
12
- # pnpm
13
- pnpm add @nickyzj2023/utils
14
-
15
- ```
16
-
17
- 使用方式:
18
-
19
- ```typescript
20
- import { fetcher, to } from "@nickyzj2023/utils";
21
-
22
- const api = fetcher("https://api.example.com");
23
-
24
- const [error, data] = await to(api.get<Blog>("/blogs/hello-world"));
25
- if (error) {
26
- console.error(error);
27
- return;
28
- }
29
-
30
- console.log(data);
31
- ```
1
+ 男生自用全新前端工具库,0依赖
2
+
3
+ ## 安装到你的项目里
4
+
5
+ ```bash
6
+ # npm
7
+ npm install @nickyzj2023/utils
8
+
9
+ # yarn
10
+ yarn add @nickyzj2023/utils
11
+
12
+ # pnpm
13
+ pnpm add @nickyzj2023/utils
14
+
15
+ ```
16
+
17
+ ## 使用方式
18
+
19
+ ```typescript
20
+ import { fetcher, to } from "@nickyzj2023/utils";
21
+
22
+ const api = fetcher("https://api.example.com");
23
+
24
+ const [error, data] = await to(api.get<Blog>("/blogs/hello-world"));
25
+ if (error) {
26
+ console.error(error);
27
+ return;
28
+ }
29
+
30
+ console.log(data);
31
+ ```
32
+
33
+ ## 完整文档
34
+
35
+ [点我](https://nickyzj628.github.io/utils/)
package/dist/index.d.mts CHANGED
@@ -59,7 +59,8 @@ declare namespace ChatCompletions {
59
59
  };
60
60
  type ExtraBody = {
61
61
  /** 工具列表 */tools?: ToolDefinition[]; /** 工具调用函数表,key 为工具名,value 为函数 */
62
- toolHandlers?: Record<string, (args: any) => any | Promise<any>>; /** 其他额外参数 */
62
+ toolHandlers?: Record<string, (args: any) => any | Promise<any>>; /** 是否使用流式传输,启用后函数返回异步迭代器 */
63
+ stream?: boolean; /** 其他额外参数 */
63
64
  [key: string]: any;
64
65
  };
65
66
  type Result = {
@@ -67,18 +68,48 @@ declare namespace ChatCompletions {
67
68
  usage: Usage; /** 原始响应中的其他字段 */
68
69
  [key: string]: any;
69
70
  };
71
+ /** 流式响应中的单个 SSE 数据块(OpenAI 原始格式) */
72
+ type StreamResponse = {
73
+ id: string;
74
+ object: "chat.completion.chunk";
75
+ created: number;
76
+ model: string;
77
+ choices: Array<{
78
+ index: number;
79
+ delta: {
80
+ role?: Message["role"];
81
+ content?: string | null;
82
+ tool_calls?: Array<{
83
+ index: number;
84
+ id?: string;
85
+ type?: "function";
86
+ function?: {
87
+ name?: string;
88
+ arguments?: string;
89
+ };
90
+ }>;
91
+ };
92
+ finish_reason: "stop" | "length" | "tool_calls" | "content_filter" | null;
93
+ }>;
94
+ usage?: Usage;
95
+ };
96
+ /** 流式调用 chatCompletions 时迭代器产出的数据块 */
97
+ type StreamChunk = {
98
+ /** 模型流式返回的内容增量(仅在生成过程中出现) */content?: string; /** Token 消耗情况(仅在最后一帧出现) */
99
+ usage?: Usage;
100
+ };
70
101
  }
71
102
  //#endregion
72
103
  //#region src/ai/chatCompletions/index.d.ts
73
104
  /**
74
105
  * 兼容 OpenAI API 的聊天补全函数
75
106
  * - 自动处理工具调用
76
- * - 返回最终回复内容和 token 消耗情况
107
+ * - 同时支持普通响应和流式响应
77
108
  *
78
109
  * @param model 模型配置,包含 model、baseURL、apiKey
79
110
  * @param messages OpenAI API 兼容的消息数组
80
- * @param extraBody 可选的额外参数,如 tools、toolHandlers、temperature 等
81
- * @returns 包含 contentusage 和其他原始字段的对象
111
+ * @param extraBody 可选的额外参数,如 tools、toolHandlers、temperature、stream
112
+ * @returns 普通模式下返回 `{ content, usage, ... }`;`stream: true` 时返回异步迭代器
82
113
  *
83
114
  * @example
84
115
  * // 最简调用
@@ -109,11 +140,26 @@ declare namespace ChatCompletions {
109
140
  * },
110
141
  * },
111
142
  * );
143
+ *
144
+ * @example
145
+ * // 流式传输
146
+ * const result = await chatCompletions(
147
+ * { baseURL: "http://127.0.0.1:11434/v1" },
148
+ * [{ role: "user", content: "你好" }],
149
+ * { stream: true },
150
+ * );
151
+ * for await (const { content, usage } of result) {
152
+ * if (content) {
153
+ * console.log("流式传输中:", content);
154
+ * } else if (usage) {
155
+ * console.log("对话结束,消耗:", usage);
156
+ * }
157
+ * }
112
158
  */
113
- declare const chatCompletions: (model: ChatCompletions.Model, messages: ChatCompletions.Message[], extraBody?: ChatCompletions.ExtraBody) => Promise<ChatCompletions.Result>;
114
- declare namespace index_d_exports {
115
- export { chatCompletions };
116
- }
159
+ declare function chatCompletions(model: ChatCompletions.Model, messages: ChatCompletions.Message[], extraBody: ChatCompletions.ExtraBody & {
160
+ stream: true;
161
+ }): Promise<AsyncGenerator<ChatCompletions.StreamChunk>>;
162
+ declare function chatCompletions(model: ChatCompletions.Model, messages: ChatCompletions.Message[], extraBody?: ChatCompletions.ExtraBody): Promise<ChatCompletions.Result>;
117
163
  //#endregion
118
164
  //#region src/dom/log.d.ts
119
165
  /**
@@ -144,9 +190,6 @@ interface LogOptions {
144
190
  * log(["消息1", "消息2"]); // "[14:30:00] [index.ts:15] 消息1 消息2"
145
191
  */
146
192
  declare const log: (message: any | any[], options?: LogOptions) => void;
147
- declare namespace index_d_exports$1 {
148
- export { LogOptions, log };
149
- }
150
193
  //#endregion
151
194
  //#region src/function/loop-until.d.ts
152
195
  /**
@@ -184,9 +227,6 @@ declare const loopUntil: <T>(fn: (count: number) => T | Promise<T>, options?: {
184
227
  maxRetries?: number; /** 停止循环条件。如果未传递,则执行 maxRetries 次后退出并返回最后结果 */
185
228
  shouldStop?: (result: T) => boolean;
186
229
  }) => Promise<T>;
187
- declare namespace index_d_exports$2 {
188
- export { loopUntil };
189
- }
190
230
  //#endregion
191
231
  //#region src/hoc/with-cache.d.ts
192
232
  type SetTtl = (seconds: number) => void;
@@ -231,9 +271,6 @@ declare const withCache: <Args extends any[], Result>(fn: (this: {
231
271
  clear(): void;
232
272
  updateTtl(seconds: number): void;
233
273
  };
234
- declare namespace index_d_exports$3 {
235
- export { SetTtl, withCache };
236
- }
237
274
  //#endregion
238
275
  //#region src/is/is-nil.d.ts
239
276
  /**
@@ -266,9 +303,6 @@ type Primitive = number | string | boolean | symbol | bigint | undefined | null;
266
303
  * isPrimitive([]); // false
267
304
  */
268
305
  declare const isPrimitive: (value: any) => value is Primitive;
269
- declare namespace index_d_exports$4 {
270
- export { Primitive, isNil, isObject, isPrimitive };
271
- }
272
306
  //#endregion
273
307
  //#region src/network/fetcher.d.ts
274
308
  type RequestInit = globalThis.RequestInit & {
@@ -404,9 +438,6 @@ declare const imageUrlToBase64: (imageUrl: string, options?: ImageCompressionOpt
404
438
  * const [error, response] = await to(fetcher().get<Blog>("/blogs/hello-world"));
405
439
  */
406
440
  declare const to: <T, E = Error>(promise: Promise<T>) => Promise<[null, T] | [E, undefined]>;
407
- declare namespace index_d_exports$5 {
408
- export { ImageCompressionOptions, RequestInit, fetcher, getRealURL, imageUrlToBase64, to };
409
- }
410
441
  //#endregion
411
442
  //#region src/number/random-int.d.ts
412
443
  /**
@@ -416,9 +447,6 @@ declare namespace index_d_exports$5 {
416
447
  * randomInt(1, 10); // 1 <= x <= 10
417
448
  */
418
449
  declare const randomInt: (min: number, max: number) => number;
419
- declare namespace index_d_exports$6 {
420
- export { randomInt };
421
- }
422
450
  //#endregion
423
451
  //#region src/object/map.d.ts
424
452
  type DeepMapKeys<T> = T extends Array<infer U> ? Array<DeepMapKeys<U>> : T extends object ? {
@@ -556,9 +584,6 @@ declare const pick: <T extends Record<string, any>, K extends keyof T>(obj: T, k
556
584
  * const numericFields = pickBy(user, (key, value) => typeof value === "number");
557
585
  */
558
586
  declare const pickBy: <T extends Record<string, any>>(obj: T, shouldPick: (key: keyof T, value: T[keyof T]) => boolean) => Partial<T>;
559
- declare namespace index_d_exports$7 {
560
- export { DeepMapKeys, DeepMapValues, mapKeys, mapValues, mergeObjects, omit, omitBy, pick, pickBy };
561
- }
562
587
  //#endregion
563
588
  //#region src/string/case.d.ts
564
589
  type SnakeToCamel<S extends string> = S extends `${infer Before}_${infer After}` ? After extends `${infer First}${infer Rest}` ? `${Before}${Uppercase<First>}${SnakeToCamel<Rest>}` : Before : S;
@@ -649,9 +674,6 @@ declare const qs: {
649
674
  addQueryPrefix: boolean;
650
675
  }) => string;
651
676
  };
652
- declare namespace index_d_exports$8 {
653
- export { CamelToSnake, Capitalize, Decapitalize, SnakeToCamel, camelToSnake, capitalize, compactStr, decapitalize, extractErrorMessage, qs, snakeToCamel };
654
- }
655
677
  //#endregion
656
678
  //#region src/time/debounce.d.ts
657
679
  /**
@@ -738,8 +760,5 @@ declare const sleep: (time?: number) => Promise<unknown>;
738
760
  * window.addEventListener('scroll', handleScroll);
739
761
  */
740
762
  declare const throttle: <T extends (...args: any[]) => any>(fn: T, delay?: number) => (this: any, ...args: Parameters<T>) => void;
741
- declare namespace index_d_exports$9 {
742
- export { LockQueue, debounce, sleep, throttle };
743
- }
744
763
  //#endregion
745
- export { index_d_exports as ai, index_d_exports$1 as dom, index_d_exports$2 as function, index_d_exports$3 as hoc, index_d_exports$4 as is, index_d_exports$5 as network, index_d_exports$6 as number, index_d_exports$7 as object, index_d_exports$8 as string, index_d_exports$9 as time };
764
+ export { CamelToSnake, Capitalize, type ChatCompletions, Decapitalize, DeepMapKeys, DeepMapValues, ImageCompressionOptions, LockQueue, LogOptions, Primitive, RequestInit, SetTtl, SnakeToCamel, camelToSnake, capitalize, chatCompletions, compactStr, debounce, decapitalize, extractErrorMessage, fetcher, getRealURL, imageUrlToBase64, isNil, isObject, isPrimitive, log, loopUntil, mapKeys, mapValues, mergeObjects, omit, omitBy, pick, pickBy, qs, randomInt, sleep, snakeToCamel, throttle, to, withCache };
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import{t as e}from"./chunk-0Lt9GpW0.mjs";const t=e=>e==null,n=e=>e?.constructor===Object,r=e=>e==null||typeof e!=`object`&&typeof e!=`function`;var i=e({isNil:()=>t,isObject:()=>n,isPrimitive:()=>r});const a=(e,t)=>Array.isArray(e)?e.map(e=>a(e,t)):n(e)?Object.keys(e).reduce((n,r)=>{let i=t(r),o=e[r];return n[i]=a(o,t),n},{}):e,o=(e,t,r)=>{let{filter:i}=r??{};if(Array.isArray(e)){let a=e.map((e,i)=>n(e)?o(e,t,r):t(e,i));return i?a.filter((e,t)=>i(e,t)):a}return n(e)?Object.keys(e).reduce((a,s)=>{let c=e[s],l;return l=n(c)||Array.isArray(c)?o(c,t,r):t(c,s),(!i||i(l,s))&&(a[s]=l),a},{}):e},s=(e,t)=>{let i={...e};for(let e of Object.keys(t)){let a=i[e],o=t[e];if(r(a)&&r(o)){i[e]=o;continue}if(Array.isArray(a)&&Array.isArray(o)){i[e]=a.concat(o);continue}if(n(a)&&n(o)){i[e]=s(a,o);continue}i[e]=o}return i},c=(e,t)=>{let n={...e};for(let e of t)delete n[e];return n},l=(e,t)=>{let n={};for(let[r,i]of Object.entries(e))t(r,i)||(n[r]=i);return n},u=(e,t)=>t.reduce((t,n)=>(Object.hasOwn(e,n)&&(t[n]=e[n]),t),{}),d=(e,t)=>{let n={};for(let[r,i]of Object.entries(e))t(r,i)&&(n[r]=i);return n};var f=e({mapKeys:()=>a,mapValues:()=>o,mergeObjects:()=>s,omit:()=>c,omitBy:()=>l,pick:()=>u,pickBy:()=>d});const p=(e=``,r={})=>{let i=async(i,a={})=>{let o=new URL(e?`${e}${i}`:i),{params:c,parser:l,...u}=s(r,a);n(c)&&Object.entries(c).forEach(([e,n])=>{t(n)||o.searchParams.append(e,n.toString())}),(n(u.body)||Array.isArray(u.body))&&(u.body=JSON.stringify(u.body),u.headers={...u.headers,"Content-Type":`application/json`});let d=await fetch(o,u);if(!d.ok){if(d.headers.get(`Content-Type`)?.startsWith(`application/json`)){let e=await d.json(),t=Error(e.error?.message||d.statusText);throw t.data=e,t}throw Error(d.statusText)}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`})}},m=async e=>{try{return[null,await e]}catch(e){return[e,void 0]}},h=async e=>{let[t,n]=await m(fetch(e,{method:`HEAD`,redirect:`manual`}));return t?e:n.headers.get(`location`)||e},g=e=>{let t=new Uint8Array(e),n=``;for(let e=0;e<t.byteLength;e++)n+=String.fromCharCode(t[e]);return btoa(n)},_=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,${g(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,${g(await(await r.convertToBlob({type:o,quality:n})).arrayBuffer())}`}catch{return e?.close(),`data:${o};base64,${g(s)}`}}return`data:${o};base64,${g(s)}`};var v=e({fetcher:()=>p,getRealURL:()=>h,imageUrlToBase64:()=>_,to:()=>m});const y=e=>e.replace(/_([a-zA-Z])/g,(e,t)=>t.toUpperCase()),b=e=>e.replace(/([A-Z])/g,(e,t)=>`_${t.toLowerCase()}`),x=e=>e.charAt(0).toUpperCase()+e.slice(1),S=e=>e.charAt(0).toLowerCase()+e.slice(1),C=(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},w=e=>{if(e instanceof Error)return e.message;if(typeof e==`string`)return e;if(n(e)){let t=e.message||e.msg;if(t)return t;for(let t of Object.values(e)){let e=w(t);if(e)return e}}return JSON.stringify(e,null,2)},T={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:``}};var E=e({camelToSnake:()=>b,capitalize:()=>x,compactStr:()=>C,decapitalize:()=>S,extractErrorMessage:()=>w,qs:()=>T,snakeToCamel:()=>y});const D=async e=>{let t=(await e.get(`/models`)).data[0]?.id;if(!t)throw Error(`无法从 /models 获取模型名称`);return t},O=async(e,t,n={})=>{let{model:r,baseURL:i,apiKey:a=``}=e,o=p(i,{headers:{Authorization:`Bearer ${a}`}}),s={model:r??await D(o),messages:t,...n};return o.post(`/chat/completions`,s)},k=async(e,t)=>{let n=t[e.function.name];if(!n)return`没有找到工具“${e.function.name}”的处理函数`;try{let t=await n(JSON.parse(e.function.arguments));return typeof t==`string`?t:JSON.stringify(t)}catch(t){return`工具“${e.function.name}”处理失败:${w(t)}`}},A=e=>typeof e==`string`?e:e.filter(e=>e.type===`text`).map(e=>e.text).join(`
3
- `),j=async(e,t,n={})=>{let{toolHandlers:r={},...i}=n;for(;;){let{choices:n,usage:a,...o}=await O(e,t,i),{message:s}=n[0]??{};if(!s)throw Error(`模型没有回复任何内容`);let{content:c=``,tool_calls:l=[],...u}=s;if(t.push(s),l.length>0&&Object.keys(r).length>0){for(let e of l){let n=await k(e,r);t.push({role:`tool`,content:n,tool_call_id:e.id})}continue}return{content:A(c),usage:a,...o,...u}}};var M=e({chatCompletions:()=>j});const N=(e,t)=>{let{time:n=!0,fileName:r=!0}=t??{},i=[];if(n&&i.push(`[${new Date().toLocaleTimeString()}]`),r){let{stack:e}=Error(),t=(e?.split(`
4
- `)[2]?.trim())?.match(/at\s+(.*):(\d+)/);if(t?.[1]){let e=t[1].split(/[/\\]/).pop();i.push(`[${e}:${t[2]}]`)}}Array.isArray(e)?i.push(...e):i.push(e),console.log(...i)};var P=e({log:()=>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})且未满足停止执行条件`)};var I=e({loopUntil:()=>F});const L=(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};var R=e({withCache:()=>L});const z=(e,t)=>Math.floor(Math.random()*(t-e+1))+e;var B=e({randomInt:()=>z});const V=(e,t=300)=>{let n=null;return(...r)=>{n&&clearTimeout(n),n=setTimeout(()=>{e(...r)},t)}};var H=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 U=async(e=150)=>new Promise(t=>{setTimeout(t,e)}),W=(e,t=300)=>{let n=null;return function(...r){n||=setTimeout(()=>{n=null,e.apply(this,r)},t)}};var G=e({LockQueue:()=>H,debounce:()=>V,sleep:()=>U,throttle:()=>W});export{M as ai,P as dom,I as function,R as hoc,i as is,v as network,B as number,f as object,E as string,G as time};
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=(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){if(d.headers.get(`Content-Type`)?.startsWith(`application/json`)){let e=await d.json(),t=Error(e.error?.message||d.statusText);throw t.data=e,t}throw Error(d.statusText)}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`})}},d=async e=>{try{return[null,await e]}catch(e){return[e,void 0]}},f=async e=>{let[t,n]=await d(fetch(e,{method:`HEAD`,redirect:`manual`}));return t?e:n.headers.get(`location`)||e},p=e=>{let t=new Uint8Array(e),n=``;for(let e=0;e<t.byteLength;e++)n+=String.fromCharCode(t[e]);return btoa(n)},m=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,${p(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,${p(await(await r.convertToBlob({type:o,quality:n})).arrayBuffer())}`}catch{return e?.close(),`data:${o};base64,${p(s)}`}}return`data:${o};base64,${p(s)}`},h=e=>e.replace(/_([a-zA-Z])/g,(e,t)=>t.toUpperCase()),g=e=>e.replace(/([A-Z])/g,(e,t)=>`_${t.toLowerCase()}`),_=e=>e.charAt(0).toUpperCase()+e.slice(1),v=e=>e.charAt(0).toLowerCase()+e.slice(1),y=(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},b=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=b(t);if(e)return e}}return JSON.stringify(e,null,2)},x={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:``}},S=async e=>{let t=(await e.get(`/models`)).data[0]?.id;if(!t)throw Error(`无法从 /models 获取模型名称`);return t},C=async(e,t,n={})=>{let{model:r,baseURL:i,apiKey:a=``}=e,o=u(i,{headers:{Authorization:`Bearer ${a}`}}),s={model:r??await S(o),messages:t,...n};return o.post(`/chat/completions`,s)},w=async function*(e,t,n={}){let{model:r,baseURL:i,apiKey:a=``}=e,o=u(i,{headers:{Authorization:`Bearer ${a}`}}),s={model:r??await S(o),messages:t,...n,stream:!0,stream_options:{include_usage:!0,...n.stream_options??{}}},c=await o.post(`/chat/completions`,s,{parser:async e=>e});if(!c.body)throw Error(`响应没有 body,无法读取流式数据`);let l=c.body.getReader(),d=new TextDecoder,f=``;try{for(;;){let{done:e,value:t}=await l.read();if(e)break;f+=d.decode(t,{stream:!0});let n=f.split(`
3
+ `);f=n.pop()??``;for(let e of n){let t=e.trim();if(!t.startsWith(`data:`))continue;let n=t.slice(5).trim();if(n===`[DONE]`)return;try{yield JSON.parse(n)}catch{}}}}finally{l.releaseLock()}},T=async(e,t)=>{let n=t[e.function.name];if(!n)return`没有找到工具“${e.function.name}”的处理函数`;try{let t=await n(JSON.parse(e.function.arguments));return typeof t==`string`?t:JSON.stringify(t)}catch(t){return`工具“${e.function.name}”处理失败:${b(t)}`}},E=e=>typeof e==`string`?e:e.filter(e=>e.type===`text`).map(e=>e.text).join(`
4
+ `),D=async(e,t,n,r)=>{for(;;){let{choices:i,usage:a,...o}=await C(e,t,r),{message:s}=i[0]??{};if(!s)throw Error(`模型没有回复任何内容`);t.push(s);let{content:c=``,tool_calls:l=[],...u}=s;if(l.length>0&&Object.keys(n).length>0){for(let e of l){let r=await T(e,n);t.push({role:`tool`,content:r,tool_call_id:e.id})}continue}return{content:E(c),usage:a,...o,...u}}},O=async function*(e,t,n,r){for(;;){let i=``,a=new Map,o=null,s;for await(let n of w(e,t,r)){n.usage&&(s=n.usage);let e=n.choices?.[0];if(!e)continue;let{delta:t}=e,{content:r,tool_calls:c}=t;if(r&&(i+=r,yield{content:r}),c)for(let e of c){let t=a.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),a.set(e.index,t)}e.finish_reason&&(o=e.finish_reason)}let c=Array.from(a.values());if(o===`tool_calls`&&c.length>0&&Object.keys(n).length>0){t.push({role:`assistant`,content:i,tool_calls:c});for(let e of c){let r=await T(e,n);t.push({role:`tool`,content:r,tool_call_id:e.id})}continue}s&&(yield{usage:s});return}};async function k(e,t,n={}){let{stream:r,toolHandlers:i={},...a}=n;return r?O(e,t,i,a):D(e,t,i,a)}const A=(e,t)=>{let{time:n=!0,fileName:r=!0}=t??{},i=[];if(n&&i.push(`[${new Date().toLocaleTimeString()}]`),r){let{stack:e}=Error(),t=(e?.split(`
5
+ `)[2]?.trim())?.match(/at\s+(.*):(\d+)/);if(t?.[1]){let e=t[1].split(/[/\\]/).pop();i.push(`[${e}:${t[2]}]`)}}Array.isArray(e)?i.push(...e):i.push(e),console.log(...i)},j=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})且未满足停止执行条件`)},M=(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},N=(e,t)=>Math.floor(Math.random()*(t-e+1))+e,P=(e,t=300)=>{let n=null;return(...r)=>{n&&clearTimeout(n),n=setTimeout(()=>{e(...r)},t)}};var F=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 I=async(e=150)=>new Promise(t=>{setTimeout(t,e)}),L=(e,t=300)=>{let n=null;return function(...r){n||=setTimeout(()=>{n=null,e.apply(this,r)},t)}};export{F as LockQueue,g as camelToSnake,_ as capitalize,k as chatCompletions,y as compactStr,P as debounce,v as decapitalize,b as extractErrorMessage,u as fetcher,f as getRealURL,m as imageUrlToBase64,e as isNil,t as isObject,n as isPrimitive,A as log,j as loopUntil,r as mapKeys,i as mapValues,a as mergeObjects,o as omit,s as omitBy,c as pick,l as pickBy,x as qs,N as randomInt,I as sleep,h as snakeToCamel,L as throttle,d as to,M as withCache};
package/package.json CHANGED
@@ -1,33 +1,33 @@
1
1
  {
2
- "name": "@nickyzj2023/utils",
3
- "version": "1.0.65",
4
- "type": "module",
5
- "main": "dist/index.mjs",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.mts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.mts",
11
- "import": "./dist/index.mjs"
12
- }
13
- },
14
- "files": [
15
- "dist"
16
- ],
17
- "scripts": {
18
- "build": "tsdown",
19
- "docs": "typedoc src/index.ts --plugin typedoc-material-theme",
20
- "check": "biome check --diagnostic-level=error --write src/"
21
- },
22
- "repository": {
23
- "type": "git",
24
- "url": "https://github.com/Nickyzj628/utils.git"
25
- },
26
- "devDependencies": {
27
- "@types/node": "^25.6.0",
28
- "tsdown": "^0.21.10",
29
- "typedoc": "^0.28.19",
30
- "typedoc-material-theme": "^1.4.1",
31
- "typescript": "^6.0.3"
32
- }
33
- }
2
+ "name": "@nickyzj2023/utils",
3
+ "version": "1.0.67",
4
+ "type": "module",
5
+ "main": "dist/index.mjs",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.mts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.mts",
11
+ "import": "./dist/index.mjs"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/Nickyzj628/utils.git"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^25.6.0",
23
+ "tsdown": "^0.21.10",
24
+ "typedoc": "^0.28.19",
25
+ "typedoc-material-theme": "^1.4.1",
26
+ "typescript": "^6.0.3"
27
+ },
28
+ "scripts": {
29
+ "build": "tsdown",
30
+ "docs": "typedoc",
31
+ "check": "biome check --diagnostic-level=error --write src/"
32
+ }
33
+ }
@@ -1 +0,0 @@
1
- var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};export{t};