@nickyzj2023/utils 1.0.81 → 1.0.83
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 +2 -2
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -275,7 +275,7 @@ declare const getModelName: (baseUrl: string) => Promise<string>;
|
|
|
275
275
|
* 根据上下文里的中/英文/多模态消息,估算出可能消耗的token
|
|
276
276
|
* - 单词 ≈ 1.5token
|
|
277
277
|
* - 标点/空白等非词字符每 4 个 ≈ 1token
|
|
278
|
-
* - 图片/音频/视频/文件 ≈
|
|
278
|
+
* - 图片/音频/视频/文件 ≈ 4096token(不好估算,取个较大的值)
|
|
279
279
|
*/
|
|
280
280
|
declare const estimateTokens: (messages?: AI.Message[]) => number;
|
|
281
281
|
//#endregion
|
|
@@ -831,7 +831,7 @@ declare const qs: {
|
|
|
831
831
|
/**
|
|
832
832
|
* 创建XML标签包裹的文本
|
|
833
833
|
* @param tagName 标签名
|
|
834
|
-
* @param content
|
|
834
|
+
* @param content 标签内容,支持嵌套createXMLText()
|
|
835
835
|
* @param props 标签属性
|
|
836
836
|
* @returns `<tagName>\ncontent\n</tagName>`
|
|
837
837
|
* @example
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,6 @@ const e=e=>e==null,t=e=>e?.constructor===Object,n=e=>e==null||typeof e!=`object`
|
|
|
2
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:``}},_=(e,t,n={})=>{let r=Object.entries(n).map(([e,t])=>`${e}="${t}"`);return`<${e}${r.length>0?` ${r.join(` `)}`:``}>\n${t}\n</${e}>`},v=(e,t=[])=>{let n=new Set(t.map(e=>e.toLowerCase()));for(let t of e.matchAll(/<\/?([a-zA-Z][\w-]*)/g))if(!n.has((t[1]??``).toLowerCase()))return!0;return!1},y=(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`})}},b=async e=>{try{return[null,await e]}catch(e){return[e,void 0]}},x=async e=>{let[t,n]=await b(fetch(e,{method:`HEAD`,redirect:`manual`}));return t?e:n.headers.get(`location`)||e},S=e=>{let t=new Uint8Array(e),n=``;for(let e=0;e<t.byteLength;e++)n+=String.fromCharCode(t[e]);return btoa(n)},C=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,${S(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,${S(await(await r.convertToBlob({type:o,quality:n})).arrayBuffer())}`}catch{return e?.close(),`data:${o};base64,${S(s)}`}}return`data:${o};base64,${S(s)}`};async function*w(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(`
|
|
3
3
|
|
|
4
4
|
`);r=a.pop()||``;for(let e of a){let t=e.split(`
|
|
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 T=e=>({inputs:[`text`],context:128e3,...e}),E=(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}},D=async e=>{let t=(await y(e).get(`/v1/models`)).data[0]?.id;if(!t)throw Error(`无法从/models获取模型名称`);return t},O=e=>{if(!e?.length)return 0;let t=new Intl.Segmenter([],{granularity:`word`}),n=e=>{let n=0,r=0;for(let i of t.segment(e))i.isWordLike?n++:r++;return Math.ceil(n*1.5+r/4)};return e.reduce((e,t)=>{let{content:r,tool_calls:i,...a}=t;if(typeof r==`string`)e+=n(r);else for(let t of r)t.type===`text`?e+=n(t.text):e+=
|
|
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 T=e=>({inputs:[`text`],context:128e3,...e}),E=(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}},D=async e=>{let t=(await y(e).get(`/v1/models`)).data[0]?.id;if(!t)throw Error(`无法从/models获取模型名称`);return t},O=e=>{if(!e?.length)return 0;let t=new Intl.Segmenter([],{granularity:`word`}),n=e=>{let n=0,r=0;for(let i of t.segment(e))i.isWordLike?n++:r++;return Math.ceil(n*1.5+r/4)};return e.reduce((e,t)=>{let{content:r,tool_calls:i,...a}=t;if(typeof r==`string`)e+=n(r);else for(let t of r)t.type===`text`?e+=n(t.text):e+=4096;return i&&(e+=n(JSON.stringify(i))),e+=n(JSON.stringify(a)),e},0)},k=e=>[c(e,[`type`,`function`]),c(e,[`handler`])],A=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)}`}},j=e=>e.reasoning||e.reasoning_content,M=e=>typeof e==`string`?e:e.filter(e=>e.type===`text`).map(e=>e.text).join(`
|
|
6
6
|
`),N=e=>{switch(e.type){case`text`:return`text`;case`image_url`:return`image`;case`input_audio`:return`audio`;case`video_url`:return`video`}},P=(e,t)=>{let{inputs:n=[`text`]}=e;for(let e of t){let{content:t}=e;if(typeof t!=`string`&&Array.isArray(t))for(let e of t){let t=N(e);if(!t||!n.includes(t))return t||`unknown`}}return``},F=async(e,t,n=[],r)=>{let{onToolHandled:i}=r??{};for(;;){let{choices:r,usage:a,...o}=await e.post(`/chat/completions`,{}),{message:s}=r?.[0]??{};if(!s)throw Error(`模型没有回复任何内容`);t.push(s);let{content:c=``,tool_calls:l=[],...u}=s,d=j(s);if(l.length>0&&n.length>0){for(let e of l){let r=await A(e,n,{messages:t});t.push({role:`tool`,content:r,tool_call_id:e.id}),i?.(e.function.name,e.function.arguments,r)}continue}return{content:M(c),reasoning:d,usage:a,...o,...u}}},I=async function*(e,t,n=[],r){let{onToolHandled:i}=r??{};for(;;){let r=new Map,a=``,o=null,s,c=await e.post(`/chat/completions`,{stream:!0},{parser:async e=>e});for await(let e of w(c)){e.usage&&(s=e.usage);let t=e.choices?.[0];if(!t)continue;let{delta:n}=t,{content:i,tool_calls:c}=n,l=j(n);if(l&&(yield{reasoning:l}),i&&(a+=i,yield{content:i}),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&&(o=t.finish_reason)}let l=Array.from(r.values());if(o===`tool_calls`&&l.length>0&&n.length>0){t.push({role:`assistant`,content:a,tool_calls:l});for(let e of l){let r=await A(e,n,{messages:t});t.push({role:`tool`,content:r,tool_call_id:e.id}),i?.(e.function.name,e.function.arguments,r)}continue}t.push({role:`assistant`,content:a}),s&&(yield{usage:s});break}};async function L(e,t,n){let{baseUrl:r,apiKey:i=``,model:a,customBody:o,inputs:s,...c}=e,{stream:l,tools:u=[],...d}=n??{},f=P(e,t);if(f)throw Error(`当前上下文含有模型不支持的输入类型:${f}`);let p=y(r,{headers:{Authorization:`Bearer ${i}`},body:{model:a??await D(r),messages:t,tools:u?.map(e=>k(e)[0]),...o}});return(l?I:F)(p,t,u,{...d,...c})}function R(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
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 z(e,...n){if(t(e)&&(`withTime`in e||`withFileName`in e))return R(e,0);R({},1)(e,...n)}const B=e=>{if(e.role===`system`)return!1;let t;if(typeof e.content==`string`)t=e.content;else if(Array.isArray(e.content))t=e.content.filter(e=>e.type===`text`).map(e=>e.text).join(``);else return!1;return!v(t,[`summary`])},V=e=>e?.role===`assistant`&&Array.isArray(e.tool_calls),H=(e,t)=>{for(t=Math.min(t,e.length);e[t]?.role===`tool`;)t++;return t},U=(e,t)=>{let n=e.reduce((e,n)=>(n.role===`tool`&&(n.content=t(n.content),e++),e),0);n>0&&z(`软删除了${n}条工具调用结果消息`)},W=(e,t)=>{let n=[`image_url`,`input_audio`,`video_url`],r=e.reduce((e,r)=>(Array.isArray(r.content)&&r.content.some(e=>n.includes(e.type))&&(r.content=t(r.content),e++),e),0);r>0&&z(`软删除了${r}条旧图片/音频/视频消息`)},G=async(e,t)=>{let{model:n,keepCount:r,systemPrompt:i}=t??{},a=e.length-r;if(a<=0){z(`消息太少,无需总结`);return}a=H(e,a);let o=[],s=[];for(let t=0;t<a;t++){let n=e[t];if(n){if(V(n)){let n=t+1;for(;n<a&&e[n]?.role===`tool`;)n++;let r=e.slice(t,n);if(r.every(B)){for(let e=t;e<n;e++)o.push(e);s.push(...r)}t=n-1;continue}n.role!==`tool`&&B(n)&&(o.push(t),s.push(n))}}if(o.length===0){z(`没有可总结的消息`);return}s.push({role:`system`,content:i},{role:`user`,content:`开始总结上下文`});let[c,l]=await b(L(n,s));if(c){z(`总结失败:${c.message}`);return}let u=o[0]??0;for(let t=o.length-1;t>=0;t--){let n=o[t];n!==void 0&&e.splice(n,1)}e.splice(u,0,{role:`user`,content:_(`summary`,l.content)})},K=(e,t)=>{let n=e.findIndex(e=>e.role===`user`),r=e.length-t;if(r<=n){z(`消息太少,无需硬删除`);return}r=H(e,r);let i=r-n;e.splice(n,i),z(`硬删除了${i}条较早的消息`)},q=async(e,t,n)=>{let{usage:r,ratioToCompactToolResult:i=.6,replacerOfToolResultContent:a=()=>`已被消费`,ratioToCompactMedia:o=.7,replacerOfMediaContent:s=()=>`已被丢弃`,ratioToSummarize:c=.8,summarizeOptions:l}=n??{},u=t?.context??128e3,d=r?.total_tokens??O(e);if(d>u*i&&U(e,a),d>u*o&&W(e,s),d>u*c){let{keepCount:n=10,systemPrompt:r=`总结历史消息`}=l??{},[i]=await b(G(e,{model:t,keepCount:n,systemPrompt:r}));if(!i)return;K(e,n)}},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})且未满足停止执行条件`)},Y=(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},X=(e,t)=>Math.floor(Math.random()*(t-e+1))+e,Z=(e,t=300)=>{let n=null;return(...r)=>{n&&clearTimeout(n),n=setTimeout(()=>{e(...r)},t)}};var Q=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 $=async(e=150)=>new Promise(t=>{setTimeout(t,e)}),ee=(e,t=300)=>{let n=null;return function(...r){n||=setTimeout(()=>{n=null,e.apply(this,r)},t)}};export{Q as LockQueue,d as camelToSnake,f as capitalize,L as chatCompletions,q as compactMessages,m as compactStr,_ as createXMLText,Z as debounce,p as decapitalize,T as defineModel,E as defineTool,O as estimateTokens,h as extractErrorMessage,y as fetcher,D as getModelName,x as getRealURL,v as hasXmlTags,C as imageUrlToBase64,e as isNil,t as isObject,n as isPrimitive,z as logger,J as loopUntil,r as mapKeys,i as mapValues,a as mergeObjects,o as omit,s as omitBy,w as parseSSE,c as pick,l as pickBy,g as qs,X as randomInt,$ as sleep,u as snakeToCamel,ee as throttle,b as to,Y as withCache};
|