@nickyzj2023/utils 1.0.78 → 1.0.79
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 +4 -4
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -221,7 +221,7 @@ declare namespace Compact {
|
|
|
221
221
|
/**
|
|
222
222
|
* 自动优化上下文,类似AI Coding Agent的/compact命令
|
|
223
223
|
*/
|
|
224
|
-
declare const
|
|
224
|
+
declare const compactMessages: (messages: AI.Message[], model: AI.Model, options?: {
|
|
225
225
|
/** 提供token消耗情况时,能更准确地判断上下文是否达到阈值 */usage?: ChatCompletions.Usage;
|
|
226
226
|
/**
|
|
227
227
|
* 上下文>总上下文*ratio时压缩工具调用结果
|
|
@@ -237,7 +237,7 @@ declare const compact: (messages: AI.Message[], model: AI.Model, options?: {
|
|
|
237
237
|
* 上下文>总上下文*ratio时压缩图片/音频/视频消息
|
|
238
238
|
* @default 0.7
|
|
239
239
|
*/
|
|
240
|
-
|
|
240
|
+
ratioOfCompactMedia?: number;
|
|
241
241
|
/**
|
|
242
242
|
* 如何压缩媒体消息,例如让其他模型用自然语言简短描述一遍
|
|
243
243
|
* @default (content) => "(已被丢弃)"
|
|
@@ -248,7 +248,7 @@ declare const compact: (messages: AI.Message[], model: AI.Model, options?: {
|
|
|
248
248
|
* @default 0.8
|
|
249
249
|
* @remarks 如果总结成功,会把summarizeOptions.keepPercent(默认0.2(20%))以外的消息压成一条消息;如果总结失败,会采取兜底压缩方法:硬删除summarizeOptions.keepPercent以外的消息
|
|
250
250
|
*/
|
|
251
|
-
|
|
251
|
+
ratioOfSummarize?: number;
|
|
252
252
|
/**
|
|
253
253
|
* 总结消息时的配置项
|
|
254
254
|
* @default { keepPercent: 0.2, model: undefined, systemPrompt: "总结历史消息" }
|
|
@@ -913,4 +913,4 @@ declare const sleep: (time?: number) => Promise<unknown>;
|
|
|
913
913
|
*/
|
|
914
914
|
declare const throttle: <T extends (...args: any[]) => any>(fn: T, delay?: number) => (this: any, ...args: Parameters<T>) => void;
|
|
915
915
|
//#endregion
|
|
916
|
-
export { AI, CamelToSnake, Capitalize, type ChatCompletions, type Compact, Decapitalize, DeepMapKeys, DeepMapValues, ImageCompressionOptions, LockQueue, LoggerOptions, Primitive, RequestInit, SetTtl, SnakeToCamel, camelToSnake, capitalize, chatCompletions,
|
|
916
|
+
export { AI, CamelToSnake, Capitalize, type ChatCompletions, type Compact, Decapitalize, DeepMapKeys, DeepMapValues, ImageCompressionOptions, LockQueue, LoggerOptions, Primitive, RequestInit, SetTtl, SnakeToCamel, camelToSnake, capitalize, chatCompletions, compactMessages, compactStr, debounce, decapitalize, defineModel, defineTool, estimateTokens, 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
|
@@ -4,4 +4,4 @@ const e=e=>e==null,t=e=>e?.constructor===Object,n=e=>e==null||typeof e!=`object`
|
|
|
4
4
|
`);r=a.pop()||``;for(let e of a){let t=e.split(`
|
|
5
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=>({inputs:[`text`],context:128e3,...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=>{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+=1e4;return i&&(e+=n(JSON.stringify(i))),e+=n(JSON.stringify(a)),e},0)},D=e=>[c(e,[`type`,`function`]),c(e,[`handler`])],O=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)}`}},k=e=>e.reasoning||e.reasoning_content,A=e=>typeof e==`string`?e:e.filter(e=>e.type===`text`).map(e=>e.text).join(`
|
|
6
6
|
`),j=e=>{switch(e.type){case`text`:return`text`;case`image_url`:return`image`;case`input_audio`:return`audio`;case`video_url`:return`video`}},M=(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=j(e);if(!t||!n.includes(t))return t||`unknown`}}return``},N=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=k(s);if(l.length>0&&n.length>0){for(let e of l){let r=await O(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:A(c),reasoning:d,usage:a,...o,...u}}},P=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 S(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=k(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 O(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 F(e,t,n){let{baseUrl:r,apiKey:i=``,model:a,customBody:o,inputs:s,...c}=e,{stream:l,tools:u=[],...d}=n??{},f=M(e,t);if(f)throw Error(`当前上下文含有模型不支持的输入类型:${f}`);let p=_(r,{headers:{Authorization:`Bearer ${i}`},body:{model:a??await T(r),messages:t,tools:u?.map(e=>D(e)[0]),...o}});return(l?P:N)(p,t,u,{...d,...c})}function I(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 L(e,...n){if(t(e)&&(`withTime`in e||`withFileName`in e))return I(e,0);I({},1)(e,...n)}const R=e=>e?.role===`assistant`&&Array.isArray(e.tool_calls),z=(e,t)=>{for(t=Math.min(t,e.length),R(e[t-1])&&e[t]?.role!==`tool`&&t--;e[t]?.role===`tool`&&!R(e[t-1]);)t++;return t},B=(e,t)=>{let n=e.reduce((e,n)=>(n.role===`tool`&&(n.content=t(n.content),e++),e),0);n>0&&L(`软删除了${n}条工具调用结果消息`)},V=(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&&L(`软删除了${r}条旧图片/音频/视频消息`)},H=async(e,t)=>{let{model:n,keepPercent:r,systemPrompt:i}=t??{},a=e.findIndex(e=>e.role===`user`),o=Math.ceil(e.length*r),s=e.length-o;if(s<=a)return L(`消息太少,无需总结`),!1;s=z(e,s);let c=e.slice(a,s);c.push({role:`system`,content:i},{role:`user`,content:`开始总结上下文`});let[l,u]=await v(F(n,c));if(l)return L(`总结失败:${l.message}`),!1;e.splice(a,s-a,{role:`user`,content:`<summary>\n${u.content}\n</summary>`})},U=(e,t)=>{let n=e.findIndex(e=>e.role===`user`),r=Math.ceil(e.length*t),i=e.length-r;if(i<=n){L(`消息太少,无需硬删除`);return}i=z(e,i);let a=i-n;e.splice(n,a),L(`硬删除了${a}条较早的消息`)},W=async(e,t,n)=>{let{usage:r,ratioOfCompactToolResult:i=.6,replacerOfToolResultContent:a=()=>`已被消费`,
|
|
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 L(e,...n){if(t(e)&&(`withTime`in e||`withFileName`in e))return I(e,0);I({},1)(e,...n)}const R=e=>e?.role===`assistant`&&Array.isArray(e.tool_calls),z=(e,t)=>{for(t=Math.min(t,e.length),R(e[t-1])&&e[t]?.role!==`tool`&&t--;e[t]?.role===`tool`&&!R(e[t-1]);)t++;return t},B=(e,t)=>{let n=e.reduce((e,n)=>(n.role===`tool`&&(n.content=t(n.content),e++),e),0);n>0&&L(`软删除了${n}条工具调用结果消息`)},V=(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&&L(`软删除了${r}条旧图片/音频/视频消息`)},H=async(e,t)=>{let{model:n,keepPercent:r,systemPrompt:i}=t??{},a=e.findIndex(e=>e.role===`user`),o=Math.ceil(e.length*r),s=e.length-o;if(s<=a)return L(`消息太少,无需总结`),!1;s=z(e,s);let c=e.slice(a,s);c.push({role:`system`,content:i},{role:`user`,content:`开始总结上下文`});let[l,u]=await v(F(n,c));if(l)return L(`总结失败:${l.message}`),!1;e.splice(a,s-a,{role:`user`,content:`<summary>\n${u.content}\n</summary>`})},U=(e,t)=>{let n=e.findIndex(e=>e.role===`user`),r=Math.ceil(e.length*t),i=e.length-r;if(i<=n){L(`消息太少,无需硬删除`);return}i=z(e,i);let a=i-n;e.splice(n,a),L(`硬删除了${a}条较早的消息`)},W=async(e,t,n)=>{let{usage:r,ratioOfCompactToolResult:i=.6,replacerOfToolResultContent:a=()=>`已被消费`,ratioOfCompactMedia:o=.7,replacerOfMediaContent:s=()=>`已被丢弃`,ratioOfSummarize:c=.8,summarizeOptions:l}=n??{},u=t?.context??128e3,d=r?.total_tokens??E(e);if(d>u*i&&B(e,a),d>u*o&&V(e,s),d>u*c){let{keepPercent:n=.2,systemPrompt:r=`总结历史消息`}=l??{},[i]=await v(H(e,{model:t,keepPercent:n,systemPrompt:r}));if(!i)return;U(e,n)}},G=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})且未满足停止执行条件`)},K=(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},q=(e,t)=>Math.floor(Math.random()*(t-e+1))+e,J=(e,t=300)=>{let n=null;return(...r)=>{n&&clearTimeout(n),n=setTimeout(()=>{e(...r)},t)}};var Y=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 X=async(e=150)=>new Promise(t=>{setTimeout(t,e)}),Z=(e,t=300)=>{let n=null;return function(...r){n||=setTimeout(()=>{n=null,e.apply(this,r)},t)}};export{Y as LockQueue,d as camelToSnake,f as capitalize,F as chatCompletions,W as compactMessages,m as compactStr,J as debounce,p as decapitalize,C as defineModel,w as defineTool,E as estimateTokens,h as extractErrorMessage,_ as fetcher,T as getModelName,y as getRealURL,x as imageUrlToBase64,e as isNil,t as isObject,n as isPrimitive,L as logger,G 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,q as randomInt,X as sleep,u as snakeToCamel,Z as throttle,v as to,K as withCache};
|