@easbot/ollama-sdk 0.3.3 → 0.3.5

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.en.md CHANGED
@@ -142,7 +142,7 @@ const model3 = new NativeOllamaLanguageModel('llama3.1:8b');
142
142
 
143
143
  ### Tool Definition Format
144
144
 
145
- Tool definitions follow the AI SDK v2 specification, using the `inputSchema` field:
145
+ Tool definitions follow the AI SDK v3 specification, using the `inputSchema` field:
146
146
 
147
147
  ```typescript
148
148
  const tools = {
@@ -348,7 +348,7 @@ For complete usage examples, please refer to:
348
348
 
349
349
  ### `NativeOllamaLanguageModel`
350
350
 
351
- Native Ollama language model implementation, fully compatible with AI SDK v2.
351
+ Native Ollama language model implementation, fully compatible with AI SDK v3.
352
352
 
353
353
  **Constructor:**
354
354
 
@@ -395,7 +395,7 @@ const generateResult = await model.doGenerate({
395
395
 
396
396
  ## Event Stream
397
397
 
398
- This SDK generates a complete AI SDK v2 event stream, including:
398
+ This SDK generates a complete AI SDK v3 event stream, including:
399
399
 
400
400
  ### Basic Events
401
401
 
@@ -522,7 +522,7 @@ Ollama HTTP API
522
522
  ### Features
523
523
 
524
524
  - **Zero Dependencies**: No dependency on `ai-sdk-ollama` or other third-party libraries
525
- - **Complete Implementation**: Fully compliant with AI SDK v2 specification
525
+ - **Complete Implementation**: Fully compliant with AI SDK v3 specification
526
526
  - **High Performance**: Batch event queue, timely state cleanup
527
527
  - **Debuggable**: Structured logging, trace ID tracking
528
528
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # @easbot/ollama-sdk
4
4
 
5
- AI SDK v2 兼容的 Ollama Provider,自动生成完整的事件流。
5
+ AI SDK v3 兼容的 Ollama Provider,自动生成完整的事件流。
6
6
 
7
7
  ## 特性
8
8
 
@@ -142,7 +142,7 @@ const model3 = new NativeOllamaLanguageModel('llama3.1:8b');
142
142
 
143
143
  ### 工具定义格式
144
144
 
145
- 工具定义遵循 AI SDK v2 规范,使用 `inputSchema` 字段:
145
+ 工具定义遵循 AI SDK v3 规范,使用 `inputSchema` 字段:
146
146
 
147
147
  ```typescript
148
148
  const tools = {
@@ -348,7 +348,7 @@ const tools = {
348
348
 
349
349
  ### `NativeOllamaLanguageModel`
350
350
 
351
- 原生 Ollama 语言模型实现,完全兼容 AI SDK v2
351
+ 原生 Ollama 语言模型实现,完全兼容 AI SDK v3
352
352
 
353
353
  **构造函数:**
354
354
 
@@ -395,7 +395,7 @@ const generateResult = await model.doGenerate({
395
395
 
396
396
  ## 事件流
397
397
 
398
- 本 SDK 生成完整的 AI SDK v2 事件流,包括:
398
+ 本 SDK 生成完整的 AI SDK v3 事件流,包括:
399
399
 
400
400
  ### 基本事件
401
401
 
@@ -522,7 +522,7 @@ Ollama HTTP API
522
522
  ### 特点
523
523
 
524
524
  - **零依赖**: 不依赖 `ai-sdk-ollama` 或其他第三方库
525
- - **完整实现**: 完全符合 AI SDK v2 规范
525
+ - **完整实现**: 完全符合 AI SDK v3 规范
526
526
  - **高性能**: 批量事件队列、及时状态清理
527
527
  - **可调试**: 结构化日志、trace ID 追踪
528
528
 
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
- 'use strict';var utils=require('@easbot/utils');var H=Object.defineProperty;var X=(s,t,e)=>t in s?H(s,t,{enumerable:true,configurable:true,writable:true,value:e}):s[t]=e;var r=(s,t,e)=>X(s,typeof t!="symbol"?t+"":t,e);var D=class{constructor(t="http://localhost:11434"){r(this,"baseURL",t);}async*streamChat(t){let e=await fetch(`${this.baseURL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,stream:true})});if(!e.ok)throw new Error(`Ollama API error: ${e.status} ${e.statusText}`);if(!e.body)throw new Error("Response body is null");let o=e.body.getReader(),a=new TextDecoder,n="";try{for(;;){let{done:l,value:c}=await o.read();if(l){if(n.trim())try{yield JSON.parse(n);}catch{}break}n+=a.decode(c,{stream:!0});let u=n.split(`
2
- `);n=u.pop()||"";for(let b of u){let O=b.trim();if(O)try{yield JSON.parse(O);}catch{}}}}finally{o.releaseLock();}}async chat(t){let e=await fetch(`${this.baseURL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,stream:false})});if(!e.ok)throw new Error(`Ollama API error: ${e.status} ${e.statusText}`);return e.json()}};var w={numCtx:262144,temperature:.7,topK:40,topP:.9,repeatPenalty:1.1,numPredict:32e3},ee=["numCtx","repeatPenalty","numPredict"];var I=class extends Error{constructor(e,o){super(e);r(this,"cause",o);this.name="OllamaError",Object.setPrototypeOf(this,new.target.prototype);}},J=class extends I{constructor(e,o,a){super(e,a);r(this,"url",o);this.name="ConnectionError";}},G=class extends I{constructor(e,o){super(`Model not found: ${e}`,o);r(this,"modelId",e);this.name="ModelNotFoundError";}},z=class extends I{constructor(e,o,a,n){super(e,n);r(this,"field",o);r(this,"value",a);this.name="ValidationError";}},B=class extends I{constructor(e,o,a){super(e,a);r(this,"timeoutMs",o);this.name="TimeoutError";}},T=class extends I{constructor(e,o,a){super(`Invalid tool definition for "${e}": missing ${o}`,a);r(this,"toolName",e);r(this,"missingField",o);this.name="InvalidToolDefinitionError";}};function Q(s,t,e){let o;return t?o="tool-calls":s?o="stop":o="other",{unified:o,raw:e}}var x=utils.Log.create({service:"ollama-sdk",module:"native-language-model"});function V(){return `${Date.now()}-${Math.random().toString(36).substring(2,9)}`}function U(s){if(!s||s.trim().length===0)return false;try{return JSON.parse(s),!0}catch{return false}}function K(s){return typeof s=="string"?s:JSON.stringify(s)}function Y(s){return !s.tools||s.tools.length===0?{}:{tools:s.tools.map(t=>{if(!t.name)throw new T(t.name||"unknown","name");let e=t.inputSchema||t.parameters;if(!e)throw new T(t.name,"inputSchema");return {type:"function",function:{name:t.name,description:t.description,parameters:e}}})}}function Z(s){let t=[];for(let e of s)try{switch(e.role){case "system":t.push({role:"system",content:e.content});break;case "user":{let o=e.content.filter(a=>a.type==="text").map(a=>a.text).join(`
3
- `);o&&t.push({role:"user",content:o});break}case "assistant":{let o="",a=[];for(let n of e.content)if(n.type==="text")o+=n.text;else if(n.type==="tool-call"){let l=n;a.push({id:l.toolCallId,type:"function",function:{name:l.toolName,arguments:JSON.stringify(l.input)}});}t.push({role:"assistant",content:o||null,tool_calls:a.length>0?a:void 0});break}case "tool":for(let o of e.content){let a=o.output,n;switch(a.type){case "text":case "error-text":n=a.value;break;case "content":case "json":case "error-json":n=JSON.stringify(a.value);break;default:n=String(a.value);break}t.push({role:"tool",tool_call_id:o.toolCallId,content:n});}break}}catch(o){x.warn("Failed to convert message",{messageRole:e.role,error:o instanceof Error?o.message:String(o)});}return t}var q=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"defaultObjectGenerationMode","json");r(this,"supportedUrls",{});r(this,"client");this.modelId=t,this.client=new D(e.baseURL);}async generate(t){return this.doGenerate(t)}async stream(t){return this.doStream(t)}async doStream(t){let e=V();x.debug("Stream request started",{traceId:e,modelId:this.modelId,hasTools:!!t.tools&&t.tools.length>0,toolCount:t.tools?.length||0,messageCount:t.prompt.length});let o=Z(t.prompt),a=Y({tools:t.tools}),n=JSON.stringify({model:this.modelId,messages:o,options:w,...a}),l=null,c=null,u=m=>{l&&l.enqueue(m),c&&c.enqueue(m);},b=async()=>{try{let m=!0,M=!1,L=!1,E="",p="",N=!1,P=[],d=[],j=()=>{if(d.length>0){for(let v of d)u(v);d.length=0;}},y=null;for await(let v of this.client.streamChat({model:this.modelId,messages:o,options:w,...a})){if(y&&y.chunkCount++,m){m=!1;let k={type:"response-metadata",id:V(),modelId:this.modelId,timestamp:new Date(v.created_at)};u(k),y&&y.eventCount++;}if(v.message?.tool_calls){L&&(d.push({type:"reasoning-end",id:"reasoning-0"}),L=!1),M&&(d.push({type:"text-end",id:"txt-0"}),M=!1);for(let k of v.message.tool_calls){if(!k.id||!k.function?.name){let $=k.id?"function.name":"id";x.error("Invalid tool call: missing field",{traceId:e,missingField:$,toolCall:void 0}),d.push({type:"error",error:`Invalid tool call: missing ${$}`});continue}let f=k.id,C=k.function.name,h=K(k.function.arguments),S=P.find($=>$.id===f);S?(S.function.arguments+=h,x.debug("Tool call arguments accumulated",{traceId:e,toolCallId:f,toolName:C,deltaLength:h.length,totalLength:S.function.arguments.length,isComplete:U(S.function.arguments)}),h&&d.push({type:"tool-input-delta",id:f,delta:h})):(S={id:f,type:"function",function:{name:C,arguments:h},hasFinished:!1},P.push(S),y&&y.toolCallCount++,x.debug("Tool call received",{traceId:e,toolCallId:f,toolName:C,argumentsLength:h.length,isComplete:U(h)}),d.push({type:"tool-input-start",id:f,toolName:C}),h&&d.push({type:"tool-input-delta",id:f,delta:h}));let W=U(S.function.arguments);!S.hasFinished&&W&&(x.debug("Tool call arguments complete",{traceId:e,toolCallId:f,toolName:C,isComplete:W,argumentsLength:S.function.arguments.length}),d.push({type:"tool-input-end",id:f}),d.push({type:"tool-call",toolCallId:f,toolName:C,input:S.function.arguments}),S.hasFinished=!0);}j(),y&&(y.eventCount+=d.length);}if(v.message?.content){let k=v.message.content;for(p+=k;p.length>0;)if(N){let f=p.indexOf("</think>");if(f===-1){p.length>0&&d.push({type:"reasoning-delta",id:"reasoning-0",delta:p}),p="";break}else {if(f>0){let C=p.substring(0,f);d.push({type:"reasoning-delta",id:"reasoning-0",delta:C});}d.push({type:"reasoning-end",id:"reasoning-0"}),L=!1,N=!1,p=p.substring(f+8);}}else {let f=p.indexOf("<think>");if(f===-1){p.length>0&&(M||(M=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:p}),E+=p),p="";break}else {if(f>0){let C=p.substring(0,f);M||(M=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:C}),E+=C,d.push({type:"text-end",id:"txt-0"}),M=!1;}N=!0,p=p.substring(f+7),L||(L=!0,d.push({type:"reasoning-start",id:"reasoning-0"}));}}}if(j(),y&&(y.eventCount+=d.length),v.done){let k=P.filter(h=>!h.hasFinished);for(let h of k)d.push({type:"tool-input-end",id:h.id}),d.push({type:"tool-call",toolCallId:h.id,toolName:h.function.name,input:h.function.arguments});p.length>0&&(N?(d.push({type:"reasoning-delta",id:"reasoning-0",delta:p}),d.push({type:"reasoning-end",id:"reasoning-0"})):(M||(M=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:p}),E+=p)),M&&d.push({type:"text-end",id:"txt-0"});let C=P.length>0?"tool-calls":"stop";if(d.push({type:"finish",finishReason:C,usage:{inputTokens:{total:v.prompt_eval_count||0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:v.eval_count||0,text:void 0,reasoning:void 0}},providerMetadata:{ollama:{totalDuration:v.total_duration||0,loadDuration:v.load_duration||0,promptEvalDuration:v.prompt_eval_duration||0,evalDuration:v.eval_duration||0}}}),j(),y&&(y.eventCount+=d.length),y);P.length=0;}}}catch(m){x.error("Stream processing error",{traceId:e,error:m instanceof Error?m.message:String(m),stack:void 0}),u({type:"error",error:m instanceof Error?m.message:String(m)}),u({type:"finish",finishReason:"error",usage:{inputTokens:{total:0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:0,text:void 0,reasoning:void 0}}});}finally{l&&l.close(),c&&c.close();}},O=new ReadableStream({start(m){l=m;}}),i=new ReadableStream({start(m){c=m;}});b();async function*g(m){let M=m.getReader();try{for(;;){let{done:L,value:E}=await M.read();if(L)break;yield E;}}finally{M.releaseLock();}}return {stream:O,fullStream:g(i),request:{body:n},response:{}}}async doGenerate(t){let e=V();x.debug("Generate request started",{traceId:e,modelId:this.modelId,hasTools:!!t.tools&&t.tools.length>0,toolCount:t.tools?.length||0,messageCount:t.prompt.length});let o=Z(t.prompt),a=Y({tools:t.tools}),n=await this.client.chat({model:this.modelId,messages:o,options:w,...a}),l=[];if(n.message?.content&&l.push({type:"text",text:n.message.content}),n.message?.tool_calls){x.debug("Tool calls received in generate",{traceId:e,toolCallCount:n.message.tool_calls.length});for(let b of n.message.tool_calls){let O=b.id||V(),i=b.function.name,g=K(b.function.arguments);x.debug("Processing tool call in generate",{traceId:e,toolCallId:O,toolName:i,argumentsLength:g.length,isComplete:U(g)}),l.push({type:"tool-call",toolCallId:O,toolName:i,input:g});}}let c=!!(n.message?.tool_calls&&n.message.tool_calls.length>0),u=Q(n.done??false,c,"stop");return x.debug("Generate request completed",{traceId:e,finishReason:u,contentParts:l.length,hasToolCalls:c,inputTokens:n.prompt_eval_count||0,outputTokens:n.eval_count||0}),{content:l,finishReason:u,usage:{inputTokens:{total:n.prompt_eval_count||0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:n.eval_count||0,text:void 0,reasoning:void 0}},providerMetadata:{ollama:{totalDuration:n.total_duration||0,loadDuration:n.load_duration||0,promptEvalDuration:n.prompt_eval_duration||0,evalDuration:n.eval_duration||0}},request:{body:JSON.stringify({model:this.modelId,messages:o,options:w,...a})},response:{}}}};var _=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"defaultObjectGenerationMode","json");r(this,"supportedUrls",{});r(this,"nativeModel");this.modelId=t,this.nativeModel=new q(t,{baseURL:e.baseURL||"http://localhost:11434"});}doGenerate(t){return this.nativeModel.doGenerate(t)}doStream(t){return this.nativeModel.doStream(t)}async generate(t){return this.nativeModel.generate(t)}async stream(t){return this.nativeModel.stream(t)}};var A=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"maxEmbeddingsPerCall");r(this,"supportsParallelCalls",true);r(this,"baseURL");this.modelId=t,this.baseURL=e.baseURL||"http://localhost:11434";}async doEmbed(t){let{values:e,abortSignal:o}=t,a=utils.Log.create({service:"ollama-embedding"});if(!e||e.length===0)return a.warn("Empty values array provided",{modelId:this.modelId}),{embeddings:[],usage:{tokens:0},warnings:[]};a.debug("Generating text embeddings",{modelId:this.modelId,valuesCount:e.length,baseURL:this.baseURL});try{let n=await Promise.all(e.map(c=>this.generateSingleEmbedding(c,o))),l=e.reduce((c,u)=>c+this.estimateTokens(u),0);return a.debug("Text embeddings generated successfully",{modelId:this.modelId,embeddingsCount:n.length,totalTokens:l}),{embeddings:n,usage:{tokens:l},warnings:[]}}catch(n){throw a.error("Text embedding generation failed",{modelId:this.modelId,error:n instanceof Error?n.message:String(n),stack:void 0}),n}}async generateSingleEmbedding(t,e){let o=`${this.baseURL}/api/embeddings`;try{let a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:this.modelId,prompt:t}),signal:e});if(!a.ok){let l=await a.text();throw new Error(`Ollama embedding API error (${a.status}): ${l}`)}let n=await a.json();if(!n.embedding||!Array.isArray(n.embedding))throw new Error("Invalid response format from Ollama embedding API");return n.embedding}catch(a){throw a instanceof TypeError&&a.message.includes("fetch")?new Error(`Failed to connect to Ollama server at ${this.baseURL}: ${a.message}`):a}}estimateTokens(t){let e=t.match(/[a-zA-Z]/g)?.length||0,o=t.match(/[\u4e00-\u9fa5]/g)?.length||0,a=t.length-e-o;return Math.ceil(e/4)+o+Math.ceil(a*.5)}};var F=class{constructor(t,e,o={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"languageModel");r(this,"options");r(this,"log",utils.Log.create({service:"ollama-sdk",module:"reranking-model"}));this.modelId=t,this.languageModel=new _(t,e),this.options={totalScore:o.totalScore??1,useEnhancedPrompt:o.useEnhancedPrompt??true,promptLanguage:o.promptLanguage??"en"},this.log.debug("OllamaRerankingModel created",{modelId:t,totalScore:this.options.totalScore,useEnhancedPrompt:this.options.useEnhancedPrompt,promptLanguage:this.options.promptLanguage});}async doRerank(t){let{query:e,documents:o,topN:a}=t,n;Array.isArray(o)?n=o:o.type==="text"?n=o.values.map((u,b)=>({content:u,id:`doc_${b+1}`})):n=o.values;let l=a??n.length;if(n.length===0)return {ranking:[],warnings:[]};try{let u=this.buildRerankPrompt(e,n),O=(await this.languageModel.doGenerate({prompt:[{role:"user",content:[{type:"text",text:u}]}],maxOutputTokens:500,temperature:.1})).content.find(g=>g.type==="text")?.text||"",i=this.parseRerankResult(O,n,l);return {ranking:i.results.map(g=>({index:g.index,relevanceScore:g.score,document:g.document})),warnings:i.warnings.map(g=>({type:"other",message:g}))}}catch(u){return this.log.error("Reranking failed",{error:u.message}),{ranking:n.map((O,i)=>({index:i,relevanceScore:this.options.totalScore*(1-i/n.length),document:O})),warnings:[{type:"other",message:`Reranking failed, using original order: ${u.message}`}]}}}buildRerankPrompt(t,e){let o=e.map((a,n)=>{let l=a.id||`doc_${n+1}`,c=a.content.slice(0,300);return `${n+1}. [ID: ${l}] ${c}`}).join(`
1
+ 'use strict';var utils=require('@easbot/utils');var B=Object.defineProperty;var Q=(s,t,e)=>t in s?B(s,t,{enumerable:true,configurable:true,writable:true,value:e}):s[t]=e;var r=(s,t,e)=>Q(s,typeof t!="symbol"?t+"":t,e);var T=class{constructor(t="http://localhost:11434"){r(this,"baseURL",t);}async*streamChat(t){let e=await fetch(`${this.baseURL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,stream:true})});if(!e.ok)throw new Error(`Ollama API error: ${e.status} ${e.statusText}`);if(!e.body)throw new Error("Response body is null");let o=e.body.getReader(),a=new TextDecoder,n="";try{for(;;){let{done:i,value:y}=await o.read();if(i){if(n.trim())try{yield JSON.parse(n);}catch{}break}n+=a.decode(y,{stream:!0});let g=n.split(`
2
+ `);n=g.pop()||"";for(let u of g){let c=u.trim();if(c)try{yield JSON.parse(c);}catch{}}}}finally{o.releaseLock();}}async chat(t){let e=await fetch(`${this.baseURL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,stream:false})});if(!e.ok)throw new Error(`Ollama API error: ${e.status} ${e.statusText}`);return e.json()}};var P={numCtx:262144,temperature:.7,topK:40,topP:.9,repeatPenalty:1.1,numPredict:32e3},K=["numCtx","repeatPenalty","numPredict"];var R=class extends Error{constructor(e,o){super(e);r(this,"cause",o);this.name="OllamaError",Object.setPrototypeOf(this,new.target.prototype);}},F=class extends R{constructor(e,o,a){super(e,a);r(this,"url",o);this.name="ConnectionError";}},j=class extends R{constructor(e,o){super(`Model not found: ${e}`,o);r(this,"modelId",e);this.name="ModelNotFoundError";}},q=class extends R{constructor(e,o,a,n){super(e,n);r(this,"field",o);r(this,"value",a);this.name="ValidationError";}},G=class extends R{constructor(e,o,a){super(e,a);r(this,"timeoutMs",o);this.name="TimeoutError";}},I=class extends R{constructor(e,o,a){super(`Invalid tool definition for "${e}": missing ${o}`,a);r(this,"toolName",e);r(this,"missingField",o);this.name="InvalidToolDefinitionError";}};function A(s,t,e){let o;return t?o="tool-calls":e==="length"?o="length":s?o="stop":o="other",{unified:o,raw:e}}var C=utils.Log.create({service:"ollama-sdk",module:"native-language-model"});function N(){return `${Date.now()}-${Math.random().toString(36).substring(2,9)}`}function V(s){if(!s||s.trim().length===0)return false;try{return JSON.parse(s),!0}catch{return false}}function z(s){return typeof s=="string"?s:JSON.stringify(s)}function W(s){return !s.tools||s.tools.length===0?{}:{tools:s.tools.map(t=>{if(!t.name)throw new I(t.name||"unknown","name");let e=t.inputSchema||t.parameters;if(!e)throw new I(t.name,"inputSchema");return {type:"function",function:{name:t.name,description:t.description,parameters:e}}})}}function Y(s){let t=[];for(let e of s)try{switch(e.role){case "system":t.push({role:"system",content:e.content});break;case "user":{let o=e.content.filter(a=>a.type==="text").map(a=>a.text).join(`
3
+ `);o&&t.push({role:"user",content:o});break}case "assistant":{let o="",a=[];for(let n of e.content)if(n.type==="text")o+=n.text;else if(n.type==="tool-call"){let i=n;a.push({id:i.toolCallId,type:"function",function:{name:i.toolName,arguments:JSON.stringify(i.input)}});}t.push({role:"assistant",content:o||null,tool_calls:a.length>0?a:void 0});break}case "tool":for(let o of e.content){let a=o.output,n;switch(a.type){case "text":case "error-text":n=a.value;break;case "content":case "json":case "error-json":n=JSON.stringify(a.value);break;default:n=String(a.value);break}t.push({role:"tool",tool_call_id:o.toolCallId,content:n});}break}}catch(o){C.warn("Failed to convert message",{messageRole:e.role,error:o instanceof Error?o.message:String(o)});}return t}var D=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"defaultObjectGenerationMode","json");r(this,"supportedUrls",{});r(this,"client");this.modelId=t,this.client=new T(e.baseURL);}async doStream(t){let e=N();C.debug("Stream request started",{traceId:e,modelId:this.modelId,hasTools:!!t.tools&&t.tools.length>0,toolCount:t.tools?.length||0,messageCount:t.prompt.length});let o=Y(t.prompt),a=W({tools:t.tools}),n=null,i=u=>{n&&n.enqueue(u);},y=async()=>{try{let u=!0,c=!1,l=!1,p="",m="",w=!1,_=[],d=[],U=()=>{if(d.length>0){for(let v of d)i(v);d.length=0;}},b=null;for await(let v of this.client.streamChat({model:this.modelId,messages:o,options:P,...a})){if(b&&b.chunkCount++,u){u=!1;let M={type:"response-metadata",id:N(),modelId:this.modelId,timestamp:new Date(v.created_at)};i(M),b&&b.eventCount++;}if(v.message?.tool_calls){l&&(d.push({type:"reasoning-end",id:"reasoning-0"}),l=!1),c&&(d.push({type:"text-end",id:"txt-0"}),c=!1);for(let M of v.message.tool_calls){if(!M.id||!M.function?.name){let E=M.id?"function.name":"id";C.error("Invalid tool call: missing field",{traceId:e,missingField:E,toolCall:void 0}),d.push({type:"error",error:`Invalid tool call: missing ${E}`});continue}let h=M.id,O=M.function.name,f=z(M.function.arguments),k=_.find(E=>E.id===h);k?(k.function.arguments+=f,C.debug("Tool call arguments accumulated",{traceId:e,toolCallId:h,toolName:O,deltaLength:f.length,totalLength:k.function.arguments.length,isComplete:V(k.function.arguments)}),f&&d.push({type:"tool-input-delta",id:h,delta:f})):(k={id:h,type:"function",function:{name:O,arguments:f},hasFinished:!1},_.push(k),b&&b.toolCallCount++,C.debug("Tool call received",{traceId:e,toolCallId:h,toolName:O,argumentsLength:f.length,isComplete:V(f)}),d.push({type:"tool-input-start",id:h,toolName:O}),f&&d.push({type:"tool-input-delta",id:h,delta:f}));let J=V(k.function.arguments);!k.hasFinished&&J&&(C.debug("Tool call arguments complete",{traceId:e,toolCallId:h,toolName:O,isComplete:J,argumentsLength:k.function.arguments.length}),d.push({type:"tool-input-end",id:h}),d.push({type:"tool-call",toolCallId:h,toolName:O,input:k.function.arguments}),k.hasFinished=!0);}U(),b&&(b.eventCount+=d.length);}if(v.message?.content){let M=v.message.content;for(m+=M;m.length>0;)if(w){let h=m.indexOf("</think>");if(h===-1){m.length>0&&d.push({type:"reasoning-delta",id:"reasoning-0",delta:m}),m="";break}else {if(h>0){let O=m.substring(0,h);d.push({type:"reasoning-delta",id:"reasoning-0",delta:O});}d.push({type:"reasoning-end",id:"reasoning-0"}),l=!1,w=!1,m=m.substring(h+8);}}else {let h=m.indexOf("<think>");if(h===-1){m.length>0&&(c||(c=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:m}),p+=m),m="";break}else {if(h>0){let O=m.substring(0,h);c||(c=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:O}),p+=O,d.push({type:"text-end",id:"txt-0"}),c=!1;}w=!0,m=m.substring(h+7),l||(l=!0,d.push({type:"reasoning-start",id:"reasoning-0"}));}}}if(U(),b&&(b.eventCount+=d.length),v.done){let M=_.filter(f=>!f.hasFinished);for(let f of M)d.push({type:"tool-input-end",id:f.id}),d.push({type:"tool-call",toolCallId:f.id,toolName:f.function.name,input:f.function.arguments});m.length>0&&(w?(d.push({type:"reasoning-delta",id:"reasoning-0",delta:m}),d.push({type:"reasoning-end",id:"reasoning-0"})):(c||(c=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:m}),p+=m)),c&&d.push({type:"text-end",id:"txt-0"});let h=_.length>0,O=A(!0,h,"stop");if(d.push({type:"finish",finishReason:O,usage:{inputTokens:{total:v.prompt_eval_count||0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:v.eval_count||0,text:void 0,reasoning:void 0}},providerMetadata:{ollama:{totalDuration:v.total_duration||0,loadDuration:v.load_duration||0,promptEvalDuration:v.prompt_eval_duration||0,evalDuration:v.eval_duration||0}}}),U(),b&&(b.eventCount+=d.length),b);_.length=0;}}}catch(u){C.error("Stream processing error",{traceId:e,error:u instanceof Error?u.message:String(u),stack:void 0}),i({type:"error",error:u instanceof Error?u.message:String(u)}),i({type:"finish",finishReason:A(false,false,"error"),usage:{inputTokens:{total:0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:0,text:void 0,reasoning:void 0}}});}finally{n&&n.close();}},g=new ReadableStream({start(u){n=u;}});return y(),{stream:g}}async doGenerate(t){let e=N();C.debug("Generate request started",{traceId:e,modelId:this.modelId,hasTools:!!t.tools&&t.tools.length>0,toolCount:t.tools?.length||0,messageCount:t.prompt.length});let o=Y(t.prompt),a=W({tools:t.tools}),n=await this.client.chat({model:this.modelId,messages:o,options:P,...a}),i=[];if(n.message?.content&&i.push({type:"text",text:n.message.content}),n.message?.tool_calls){C.debug("Tool calls received in generate",{traceId:e,toolCallCount:n.message.tool_calls.length});for(let u of n.message.tool_calls){let c=u.id||N(),l=u.function.name,p=z(u.function.arguments);C.debug("Processing tool call in generate",{traceId:e,toolCallId:c,toolName:l,argumentsLength:p.length,isComplete:V(p)}),i.push({type:"tool-call",toolCallId:c,toolName:l,input:p});}}let y=!!(n.message?.tool_calls&&n.message.tool_calls.length>0),g=A(n.done??false,y,"stop");return C.debug("Generate request completed",{traceId:e,finishReason:g,contentParts:i.length,hasToolCalls:y,inputTokens:n.prompt_eval_count||0,outputTokens:n.eval_count||0}),{content:i,finishReason:g,usage:{inputTokens:{total:n.prompt_eval_count||0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:n.eval_count||0,text:void 0,reasoning:void 0}},providerMetadata:{ollama:{totalDuration:n.total_duration||0,loadDuration:n.load_duration||0,promptEvalDuration:n.prompt_eval_duration||0,evalDuration:n.eval_duration||0}},warnings:[],response:{id:e,timestamp:new Date(n.created_at||new Date().toISOString()),modelId:this.modelId}}}};var L=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"defaultObjectGenerationMode","json");r(this,"supportedUrls",{});r(this,"nativeModel");this.modelId=t,this.nativeModel=new D(t,{baseURL:e.baseURL||"http://localhost:11434"});}doGenerate(t){return this.nativeModel.doGenerate(t)}doStream(t){return this.nativeModel.doStream(t)}};var S=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"maxEmbeddingsPerCall");r(this,"supportsParallelCalls",true);r(this,"baseURL");this.modelId=t,this.baseURL=e.baseURL||"http://localhost:11434";}async doEmbed(t){let{values:e,abortSignal:o}=t,a=utils.Log.create({service:"ollama-embedding"});if(!e||e.length===0)return a.warn("Empty values array provided",{modelId:this.modelId}),{embeddings:[],usage:{tokens:0},warnings:[]};a.debug("Generating text embeddings",{modelId:this.modelId,valuesCount:e.length,baseURL:this.baseURL});try{let n=await Promise.all(e.map(y=>this.generateSingleEmbedding(y,o))),i=e.reduce((y,g)=>y+this.estimateTokens(g),0);return a.debug("Text embeddings generated successfully",{modelId:this.modelId,embeddingsCount:n.length,totalTokens:i}),{embeddings:n,usage:{tokens:i},warnings:[]}}catch(n){throw a.error("Text embedding generation failed",{modelId:this.modelId,error:n instanceof Error?n.message:String(n),stack:void 0}),n}}async generateSingleEmbedding(t,e){let o=`${this.baseURL}/api/embeddings`;try{let a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:this.modelId,prompt:t}),signal:e});if(!a.ok){let i=await a.text();throw new Error(`Ollama embedding API error (${a.status}): ${i}`)}let n=await a.json();if(!n.embedding||!Array.isArray(n.embedding))throw new Error("Invalid response format from Ollama embedding API");return n.embedding}catch(a){throw a instanceof TypeError&&a.message.includes("fetch")?new Error(`Failed to connect to Ollama server at ${this.baseURL}: ${a.message}`):a}}estimateTokens(t){let e=t.match(/[a-zA-Z]/g)?.length||0,o=t.match(/[\u4e00-\u9fa5]/g)?.length||0,a=t.length-e-o;return Math.ceil(e/4)+o+Math.ceil(a*.5)}};var $=class{constructor(t,e,o={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"languageModel");r(this,"options");r(this,"log",utils.Log.create({service:"ollama-sdk",module:"reranking-model"}));this.modelId=t,this.languageModel=new L(t,e),this.options={totalScore:o.totalScore??1,useEnhancedPrompt:o.useEnhancedPrompt??true,promptLanguage:o.promptLanguage??"en"},this.log.debug("OllamaRerankingModel created",{modelId:t,totalScore:this.options.totalScore,useEnhancedPrompt:this.options.useEnhancedPrompt,promptLanguage:this.options.promptLanguage});}async doRerank(t){let{query:e,documents:o,topN:a}=t,n;Array.isArray(o)?n=o:o.type==="text"?n=o.values.map((g,u)=>({content:g,id:`doc_${u+1}`})):n=o.values;let i=a??n.length;if(n.length===0)return {ranking:[],warnings:[]};try{let g=this.buildRerankPrompt(e,n),c=(await this.languageModel.doGenerate({prompt:[{role:"user",content:[{type:"text",text:g}]}],maxOutputTokens:500,temperature:.1})).content.find(p=>p.type==="text")?.text||"";this.log.debug("Rerank LLM raw output",{query:e,textContent:c,documentsCount:n.length});let l=this.parseRerankResult(c,n,i);return {ranking:l.results.map(p=>({index:p.index,relevanceScore:p.score})),warnings:l.warnings.map(p=>({type:"other",message:p}))}}catch(g){return this.log.error("Reranking failed",{error:g.message}),{ranking:n.map((c,l)=>({index:l,relevanceScore:n.length>1?this.options.totalScore*(1-.9*l/(n.length-1)):this.options.totalScore,document:c})),warnings:[{type:"other",message:`Reranking failed, using original order: ${g.message}`}]}}}buildRerankPrompt(t,e){let o=e.map((a,n)=>{let i=a.id||`doc_${n+1}`,y=a.content.slice(0,300);return `${n+1}. [ID: ${i}] ${y}`}).join(`
4
4
 
5
5
  `);if(this.options.promptLanguage==="en")return `You are a professional information retrieval reranking expert. Please rank the following documents according to the query.
6
6
 
@@ -13,18 +13,16 @@ Please rank the documents according to the following requirements:
13
13
  1. Carefully analyze the user's query intent and key information needs
14
14
  2. Evaluate each document's relevance to the query
15
15
  3. Consider the document's completeness, accuracy, and usefulness
16
- 4. Return the ranked document numbers separated by commas
17
- 5. Most relevant documents should be at the front
16
+ 4. Return ONLY the document numbers (1, 2, 3, ...) in order from MOST relevant to LEAST relevant, separated by commas
17
+ 5. Most relevant documents MUST be at the front
18
+ 6. Every document must appear exactly once in your output
18
19
 
19
- Use a scoring scale from ${this.options.totalScore*.1} to ${this.options.totalScore} where ${this.options.totalScore*.1} is most relevant and ${this.options.totalScore} is least relevant.
20
- The actual score for each position should be calculated dynamically. For example:
21
- - If totalScore is 10: scores might be: 9, 7, 10, 4, 2
22
- - If totalScore is 1: scores might be: 0.2, 0.5, 1.0, 0.06, 0.04
23
- - If totalScore is 5: scores might be: 0.1, 0.25, 0.5, 0.03, 0.02
20
+ IMPORTANT:
21
+ - You MUST actually reorder the documents based on relevance to the query
22
+ - Do NOT output the original order unless it happens to be the correct relevance order
23
+ - Output format: just the numbers separated by commas, nothing else
24
24
 
25
- Example output format: 2,5,1,4,3
26
-
27
- Please only output the ranked document numbers separated by commas, do not include the actual scores.`;{let a=this.options.totalScore===1?"1.0":`${this.options.totalScore}`;return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u4FE1\u606F\u68C0\u7D22\u91CD\u6392\u5E8F\u4E13\u5BB6\u3002\u8BF7\u6839\u636E\u7528\u6237\u67E5\u8BE2\u5BF9\u6587\u6863\u8FDB\u884C\u76F8\u5173\u6027\u6392\u5E8F\u3002
25
+ Example output format: 2,5,1,4,3 (where document 2 is most relevant to the query)`;{this.options.totalScore===1?"1.0":`${this.options.totalScore}`;return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u4FE1\u606F\u68C0\u7D22\u91CD\u6392\u5E8F\u4E13\u5BB6\u3002\u8BF7\u6839\u636E\u7528\u6237\u67E5\u8BE2\u5BF9\u6587\u6863\u8FDB\u884C\u76F8\u5173\u6027\u6392\u5E8F\u3002
28
26
 
29
27
  \u7528\u6237\u67E5\u8BE2\uFF1A${t}
30
28
 
@@ -35,10 +33,13 @@ ${o}
35
33
  1. \u4ED4\u7EC6\u5206\u6790\u7528\u6237\u67E5\u8BE2\u7684\u610F\u56FE\u548C\u5173\u952E\u4FE1\u606F\u9700\u6C42
36
34
  2. \u8BC4\u4F30\u6BCF\u4E2A\u6587\u6863\u4E0E\u67E5\u8BE2\u7684\u76F8\u5173\u6027
37
35
  3. \u8003\u8651\u6587\u6863\u7684\u5B8C\u6574\u6027\u3001\u51C6\u786E\u6027\u548C\u5B9E\u7528\u6027
38
- 4. \u8FD4\u56DE\u6392\u5E8F\u540E\u7684\u6587\u6863\u7F16\u53F7\uFF0C\u7528\u9017\u53F7\u5206\u9694
39
- 5. \u6700\u76F8\u5173\u7684\u6587\u6863\u6392\u5728\u6700\u524D\u9762
40
- 6. \u8BF7\u4F7F\u7528 ${a} \u5206\u503C\u8FDB\u884C\u8BC4\u5206
36
+ 4. \u53EA\u8F93\u51FA\u6587\u6863\u7F16\u53F7\uFF081, 2, 3, ...\uFF09\uFF0C\u6309"\u6700\u76F8\u5173"\u5230"\u6700\u4E0D\u76F8\u5173"\u987A\u5E8F\u6392\u5217\uFF0C\u7528\u9017\u53F7\u5206\u9694
37
+ 5. \u6700\u76F8\u5173\u7684\u6587\u6863\u5FC5\u987B\u6392\u5728\u6700\u524D\u9762
38
+ 6. \u6BCF\u4E2A\u6587\u6863\u5FC5\u987B\u51FA\u73B0\u4E14\u4EC5\u51FA\u73B0\u4E00\u6B21
41
39
 
42
- \u793A\u4F8B\u8F93\u51FA\u683C\u5F0F\uFF1A2,5,1,4,3
40
+ \u91CD\u8981\u63D0\u793A\uFF1A
41
+ - \u5FC5\u987B\u6839\u636E\u4E0E\u67E5\u8BE2\u7684\u76F8\u5173\u6027\u5B9E\u9645\u91CD\u65B0\u6392\u5E8F\u6587\u6863
42
+ - \u9664\u975E\u539F\u59CB\u987A\u5E8F\u6070\u597D\u5C31\u662F\u6B63\u786E\u7684\u76F8\u5173\u6027\u6392\u5E8F\uFF0C\u5426\u5219\u4E0D\u8981\u8F93\u51FA\u539F\u59CB\u987A\u5E8F
43
+ - \u8F93\u51FA\u683C\u5F0F\uFF1A\u4EC5\u5305\u542B\u7528\u9017\u53F7\u5206\u9694\u7684\u7F16\u53F7\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u5185\u5BB9
43
44
 
44
- \u8BF7\u53EA\u8F93\u51FA\u6392\u5E8F\u540E\u7684\u7F16\u53F7\uFF0C\u4E0D\u8981\u5305\u542B\u5176\u4ED6\u89E3\u91CA\u3002`}}parseRerankResult(t,e,o){let a=[];try{let n=t.match(/\d+/g)?.map(i=>parseInt(i,10)-1).filter(i=>i>=0&&i<e.length)||[],l=[],c=new Set;for(let i of n)c.has(i)||(c.add(i),l.push(i));let u=[];for(let i=0;i<l.length;i++){let g=l[i];if(g!==void 0&&g>=0&&g<e.length){let m=e[g];m&&u.push({index:g,score:this.options.totalScore*(1-i/l.length),document:m});}}let b=new Set(l);for(let i=0;i<e.length;i++)if(!b.has(i)){let g=this.options.totalScore*.1*(1-i/e.length);u.push({index:i,score:g,document:e[i]});}let O=o?u.slice(0,o):u;return this.log.debug("Parsed rerank results",{extractedNumbers:n,uniqueIndices:l,resultCount:O.length,topK:o}),{results:O,warnings:a}}catch(n){return this.log.error("Failed to parse rerank result",{error:n.message,text:t}),{results:e.map((c,u)=>({index:u,score:this.options.totalScore*(1-u/e.length),document:c})),warnings:[`Failed to parse rerank result: ${n.message}`]}}}};function ae(s={}){return Object.assign(e=>new _(e,s),{languageModel:e=>new _(e,s),chat:e=>new _(e,s),embeddingModel:e=>new A(e,s),rerankingModel:(e,o)=>new F(e,s,o)})}var re="0.1.0";exports.ConnectionError=J;exports.ModelNotFoundError=G;exports.OLLAMA_DEFAULT_OPTIONS=w;exports.OLLAMA_SPECIFIC_PARAMS=ee;exports.OllamaEmbeddingModel=A;exports.OllamaError=I;exports.OllamaLanguageModel=_;exports.TimeoutError=B;exports.VERSION=re;exports.ValidationError=z;exports.createOllama=ae;
45
+ \u793A\u4F8B\u8F93\u51FA\u683C\u5F0F\uFF1A2,5,1,4,3\uFF08\u5176\u4E2D\u6587\u6863 2 \u4E0E\u67E5\u8BE2\u6700\u76F8\u5173\uFF09`}}parseRerankResult(t,e,o){let a=[];try{let n=t.match(/\d+/g)?.map(l=>parseInt(l,10)-1).filter(l=>l>=0&&l<e.length)||[],i=[],y=new Set;for(let l of n)y.has(l)||(y.add(l),i.push(l));let g=[];for(let l=0;l<i.length;l++){let p=i[l];if(p!==void 0&&p>=0&&p<e.length){let m=e[p];m&&g.push({index:p,score:i.length>1?this.options.totalScore*(1-.9*l/(i.length-1)):this.options.totalScore,document:m});}}let u=new Set(i);for(let l=0;l<e.length;l++)if(!u.has(l)){let p=this.options.totalScore*.1;g.push({index:l,score:p,document:e[l]});}let c=o?g.slice(0,o):g;return this.log.debug("Parsed rerank results",{extractedNumbers:n,uniqueIndices:i,resultCount:c.length,topK:o}),{results:c,warnings:a}}catch(n){return this.log.error("Failed to parse rerank result",{error:n.message,text:t}),{results:e.map((y,g)=>({index:g,score:e.length>1?this.options.totalScore*(1-.9*g/(e.length-1)):this.options.totalScore,document:y})),warnings:[`Failed to parse rerank result: ${n.message}`]}}}};function ee(s={}){return Object.assign(e=>new L(e,s),{languageModel:e=>new L(e,s),chat:e=>new L(e,s),embeddingModel:e=>new S(e,s),rerankingModel:(e,o)=>new $(e,s,o)})}var te="0.1.0";exports.ConnectionError=F;exports.ModelNotFoundError=j;exports.OLLAMA_DEFAULT_OPTIONS=P;exports.OLLAMA_SPECIFIC_PARAMS=K;exports.OllamaEmbeddingModel=S;exports.OllamaError=R;exports.OllamaLanguageModel=L;exports.TimeoutError=G;exports.VERSION=te;exports.ValidationError=q;exports.createOllama=ee;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { LanguageModelV3, LanguageModelV3CallOptions, EmbeddingModelV3, EmbeddingModelV3CallOptions, EmbeddingModelV3Result, RerankingModelV3 } from '@ai-sdk/provider';
1
+ import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3GenerateResult, LanguageModelV3StreamResult, EmbeddingModelV3, EmbeddingModelV3CallOptions, EmbeddingModelV3Result, RerankingModelV3 } from '@ai-sdk/provider';
2
2
 
3
3
  type OllamaModelId = string;
4
4
  interface OllamaConfig {
@@ -25,19 +25,8 @@ declare class OllamaLanguageModel implements LanguageModelV3 {
25
25
  readonly supportedUrls: Record<string, RegExp[]>;
26
26
  private nativeModel;
27
27
  constructor(modelId: OllamaModelId, config?: OllamaConfig);
28
- doGenerate(options: LanguageModelV3CallOptions): ReturnType<LanguageModelV3['doGenerate']>;
29
- doStream(options: LanguageModelV3CallOptions): ReturnType<LanguageModelV3['doStream']>;
30
- generate(options: LanguageModelV3CallOptions): Promise<any>;
31
- stream(options: LanguageModelV3CallOptions): Promise<{
32
- stream: ReadableStream<any>;
33
- fullStream: AsyncIterable<any>;
34
- request: {
35
- body: string;
36
- };
37
- response: {
38
- headers?: Record<string, string>;
39
- };
40
- }>;
28
+ doGenerate(options: LanguageModelV3CallOptions): PromiseLike<LanguageModelV3GenerateResult>;
29
+ doStream(options: LanguageModelV3CallOptions): PromiseLike<LanguageModelV3StreamResult>;
41
30
  }
42
31
 
43
32
  declare class OllamaEmbeddingModel implements EmbeddingModelV3 {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { LanguageModelV3, LanguageModelV3CallOptions, EmbeddingModelV3, EmbeddingModelV3CallOptions, EmbeddingModelV3Result, RerankingModelV3 } from '@ai-sdk/provider';
1
+ import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3GenerateResult, LanguageModelV3StreamResult, EmbeddingModelV3, EmbeddingModelV3CallOptions, EmbeddingModelV3Result, RerankingModelV3 } from '@ai-sdk/provider';
2
2
 
3
3
  type OllamaModelId = string;
4
4
  interface OllamaConfig {
@@ -25,19 +25,8 @@ declare class OllamaLanguageModel implements LanguageModelV3 {
25
25
  readonly supportedUrls: Record<string, RegExp[]>;
26
26
  private nativeModel;
27
27
  constructor(modelId: OllamaModelId, config?: OllamaConfig);
28
- doGenerate(options: LanguageModelV3CallOptions): ReturnType<LanguageModelV3['doGenerate']>;
29
- doStream(options: LanguageModelV3CallOptions): ReturnType<LanguageModelV3['doStream']>;
30
- generate(options: LanguageModelV3CallOptions): Promise<any>;
31
- stream(options: LanguageModelV3CallOptions): Promise<{
32
- stream: ReadableStream<any>;
33
- fullStream: AsyncIterable<any>;
34
- request: {
35
- body: string;
36
- };
37
- response: {
38
- headers?: Record<string, string>;
39
- };
40
- }>;
28
+ doGenerate(options: LanguageModelV3CallOptions): PromiseLike<LanguageModelV3GenerateResult>;
29
+ doStream(options: LanguageModelV3CallOptions): PromiseLike<LanguageModelV3StreamResult>;
41
30
  }
42
31
 
43
32
  declare class OllamaEmbeddingModel implements EmbeddingModelV3 {
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import {Log}from'@easbot/utils';var X=Object.defineProperty;var ee=(s,t,e)=>t in s?X(s,t,{enumerable:true,configurable:true,writable:true,value:e}):s[t]=e;var r=(s,t,e)=>ee(s,typeof t!="symbol"?t+"":t,e);var V=class{constructor(t="http://localhost:11434"){r(this,"baseURL",t);}async*streamChat(t){let e=await fetch(`${this.baseURL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,stream:true})});if(!e.ok)throw new Error(`Ollama API error: ${e.status} ${e.statusText}`);if(!e.body)throw new Error("Response body is null");let o=e.body.getReader(),a=new TextDecoder,n="";try{for(;;){let{done:l,value:c}=await o.read();if(l){if(n.trim())try{yield JSON.parse(n);}catch{}break}n+=a.decode(c,{stream:!0});let u=n.split(`
2
- `);n=u.pop()||"";for(let b of u){let O=b.trim();if(O)try{yield JSON.parse(O);}catch{}}}}finally{o.releaseLock();}}async chat(t){let e=await fetch(`${this.baseURL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,stream:false})});if(!e.ok)throw new Error(`Ollama API error: ${e.status} ${e.statusText}`);return e.json()}};var E={numCtx:262144,temperature:.7,topK:40,topP:.9,repeatPenalty:1.1,numPredict:32e3},te=["numCtx","repeatPenalty","numPredict"];var L=class extends Error{constructor(e,o){super(e);r(this,"cause",o);this.name="OllamaError",Object.setPrototypeOf(this,new.target.prototype);}},G=class extends L{constructor(e,o,a){super(e,a);r(this,"url",o);this.name="ConnectionError";}},z=class extends L{constructor(e,o){super(`Model not found: ${e}`,o);r(this,"modelId",e);this.name="ModelNotFoundError";}},B=class extends L{constructor(e,o,a,n){super(e,n);r(this,"field",o);r(this,"value",a);this.name="ValidationError";}},W=class extends L{constructor(e,o,a){super(e,a);r(this,"timeoutMs",o);this.name="TimeoutError";}},A=class extends L{constructor(e,o,a){super(`Invalid tool definition for "${e}": missing ${o}`,a);r(this,"toolName",e);r(this,"missingField",o);this.name="InvalidToolDefinitionError";}};function K(s,t,e){let o;return t?o="tool-calls":s?o="stop":o="other",{unified:o,raw:e}}var x=Log.create({service:"ollama-sdk",module:"native-language-model"});function U(){return `${Date.now()}-${Math.random().toString(36).substring(2,9)}`}function q(s){if(!s||s.trim().length===0)return false;try{return JSON.parse(s),!0}catch{return false}}function Y(s){return typeof s=="string"?s:JSON.stringify(s)}function Z(s){return !s.tools||s.tools.length===0?{}:{tools:s.tools.map(t=>{if(!t.name)throw new A(t.name||"unknown","name");let e=t.inputSchema||t.parameters;if(!e)throw new A(t.name,"inputSchema");return {type:"function",function:{name:t.name,description:t.description,parameters:e}}})}}function H(s){let t=[];for(let e of s)try{switch(e.role){case "system":t.push({role:"system",content:e.content});break;case "user":{let o=e.content.filter(a=>a.type==="text").map(a=>a.text).join(`
3
- `);o&&t.push({role:"user",content:o});break}case "assistant":{let o="",a=[];for(let n of e.content)if(n.type==="text")o+=n.text;else if(n.type==="tool-call"){let l=n;a.push({id:l.toolCallId,type:"function",function:{name:l.toolName,arguments:JSON.stringify(l.input)}});}t.push({role:"assistant",content:o||null,tool_calls:a.length>0?a:void 0});break}case "tool":for(let o of e.content){let a=o.output,n;switch(a.type){case "text":case "error-text":n=a.value;break;case "content":case "json":case "error-json":n=JSON.stringify(a.value);break;default:n=String(a.value);break}t.push({role:"tool",tool_call_id:o.toolCallId,content:n});}break}}catch(o){x.warn("Failed to convert message",{messageRole:e.role,error:o instanceof Error?o.message:String(o)});}return t}var F=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"defaultObjectGenerationMode","json");r(this,"supportedUrls",{});r(this,"client");this.modelId=t,this.client=new V(e.baseURL);}async generate(t){return this.doGenerate(t)}async stream(t){return this.doStream(t)}async doStream(t){let e=U();x.debug("Stream request started",{traceId:e,modelId:this.modelId,hasTools:!!t.tools&&t.tools.length>0,toolCount:t.tools?.length||0,messageCount:t.prompt.length});let o=H(t.prompt),a=Z({tools:t.tools}),n=JSON.stringify({model:this.modelId,messages:o,options:E,...a}),l=null,c=null,u=m=>{l&&l.enqueue(m),c&&c.enqueue(m);},b=async()=>{try{let m=!0,M=!1,w=!1,P="",p="",$=!1,T=[],d=[],J=()=>{if(d.length>0){for(let v of d)u(v);d.length=0;}},y=null;for await(let v of this.client.streamChat({model:this.modelId,messages:o,options:E,...a})){if(y&&y.chunkCount++,m){m=!1;let k={type:"response-metadata",id:U(),modelId:this.modelId,timestamp:new Date(v.created_at)};u(k),y&&y.eventCount++;}if(v.message?.tool_calls){w&&(d.push({type:"reasoning-end",id:"reasoning-0"}),w=!1),M&&(d.push({type:"text-end",id:"txt-0"}),M=!1);for(let k of v.message.tool_calls){if(!k.id||!k.function?.name){let D=k.id?"function.name":"id";x.error("Invalid tool call: missing field",{traceId:e,missingField:D,toolCall:void 0}),d.push({type:"error",error:`Invalid tool call: missing ${D}`});continue}let f=k.id,C=k.function.name,h=Y(k.function.arguments),S=T.find(D=>D.id===f);S?(S.function.arguments+=h,x.debug("Tool call arguments accumulated",{traceId:e,toolCallId:f,toolName:C,deltaLength:h.length,totalLength:S.function.arguments.length,isComplete:q(S.function.arguments)}),h&&d.push({type:"tool-input-delta",id:f,delta:h})):(S={id:f,type:"function",function:{name:C,arguments:h},hasFinished:!1},T.push(S),y&&y.toolCallCount++,x.debug("Tool call received",{traceId:e,toolCallId:f,toolName:C,argumentsLength:h.length,isComplete:q(h)}),d.push({type:"tool-input-start",id:f,toolName:C}),h&&d.push({type:"tool-input-delta",id:f,delta:h}));let Q=q(S.function.arguments);!S.hasFinished&&Q&&(x.debug("Tool call arguments complete",{traceId:e,toolCallId:f,toolName:C,isComplete:Q,argumentsLength:S.function.arguments.length}),d.push({type:"tool-input-end",id:f}),d.push({type:"tool-call",toolCallId:f,toolName:C,input:S.function.arguments}),S.hasFinished=!0);}J(),y&&(y.eventCount+=d.length);}if(v.message?.content){let k=v.message.content;for(p+=k;p.length>0;)if($){let f=p.indexOf("</think>");if(f===-1){p.length>0&&d.push({type:"reasoning-delta",id:"reasoning-0",delta:p}),p="";break}else {if(f>0){let C=p.substring(0,f);d.push({type:"reasoning-delta",id:"reasoning-0",delta:C});}d.push({type:"reasoning-end",id:"reasoning-0"}),w=!1,$=!1,p=p.substring(f+8);}}else {let f=p.indexOf("<think>");if(f===-1){p.length>0&&(M||(M=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:p}),P+=p),p="";break}else {if(f>0){let C=p.substring(0,f);M||(M=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:C}),P+=C,d.push({type:"text-end",id:"txt-0"}),M=!1;}$=!0,p=p.substring(f+7),w||(w=!0,d.push({type:"reasoning-start",id:"reasoning-0"}));}}}if(J(),y&&(y.eventCount+=d.length),v.done){let k=T.filter(h=>!h.hasFinished);for(let h of k)d.push({type:"tool-input-end",id:h.id}),d.push({type:"tool-call",toolCallId:h.id,toolName:h.function.name,input:h.function.arguments});p.length>0&&($?(d.push({type:"reasoning-delta",id:"reasoning-0",delta:p}),d.push({type:"reasoning-end",id:"reasoning-0"})):(M||(M=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:p}),P+=p)),M&&d.push({type:"text-end",id:"txt-0"});let C=T.length>0?"tool-calls":"stop";if(d.push({type:"finish",finishReason:C,usage:{inputTokens:{total:v.prompt_eval_count||0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:v.eval_count||0,text:void 0,reasoning:void 0}},providerMetadata:{ollama:{totalDuration:v.total_duration||0,loadDuration:v.load_duration||0,promptEvalDuration:v.prompt_eval_duration||0,evalDuration:v.eval_duration||0}}}),J(),y&&(y.eventCount+=d.length),y);T.length=0;}}}catch(m){x.error("Stream processing error",{traceId:e,error:m instanceof Error?m.message:String(m),stack:void 0}),u({type:"error",error:m instanceof Error?m.message:String(m)}),u({type:"finish",finishReason:"error",usage:{inputTokens:{total:0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:0,text:void 0,reasoning:void 0}}});}finally{l&&l.close(),c&&c.close();}},O=new ReadableStream({start(m){l=m;}}),i=new ReadableStream({start(m){c=m;}});b();async function*g(m){let M=m.getReader();try{for(;;){let{done:w,value:P}=await M.read();if(w)break;yield P;}}finally{M.releaseLock();}}return {stream:O,fullStream:g(i),request:{body:n},response:{}}}async doGenerate(t){let e=U();x.debug("Generate request started",{traceId:e,modelId:this.modelId,hasTools:!!t.tools&&t.tools.length>0,toolCount:t.tools?.length||0,messageCount:t.prompt.length});let o=H(t.prompt),a=Z({tools:t.tools}),n=await this.client.chat({model:this.modelId,messages:o,options:E,...a}),l=[];if(n.message?.content&&l.push({type:"text",text:n.message.content}),n.message?.tool_calls){x.debug("Tool calls received in generate",{traceId:e,toolCallCount:n.message.tool_calls.length});for(let b of n.message.tool_calls){let O=b.id||U(),i=b.function.name,g=Y(b.function.arguments);x.debug("Processing tool call in generate",{traceId:e,toolCallId:O,toolName:i,argumentsLength:g.length,isComplete:q(g)}),l.push({type:"tool-call",toolCallId:O,toolName:i,input:g});}}let c=!!(n.message?.tool_calls&&n.message.tool_calls.length>0),u=K(n.done??false,c,"stop");return x.debug("Generate request completed",{traceId:e,finishReason:u,contentParts:l.length,hasToolCalls:c,inputTokens:n.prompt_eval_count||0,outputTokens:n.eval_count||0}),{content:l,finishReason:u,usage:{inputTokens:{total:n.prompt_eval_count||0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:n.eval_count||0,text:void 0,reasoning:void 0}},providerMetadata:{ollama:{totalDuration:n.total_duration||0,loadDuration:n.load_duration||0,promptEvalDuration:n.prompt_eval_duration||0,evalDuration:n.eval_duration||0}},request:{body:JSON.stringify({model:this.modelId,messages:o,options:E,...a})},response:{}}}};var I=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"defaultObjectGenerationMode","json");r(this,"supportedUrls",{});r(this,"nativeModel");this.modelId=t,this.nativeModel=new F(t,{baseURL:e.baseURL||"http://localhost:11434"});}doGenerate(t){return this.nativeModel.doGenerate(t)}doStream(t){return this.nativeModel.doStream(t)}async generate(t){return this.nativeModel.generate(t)}async stream(t){return this.nativeModel.stream(t)}};var N=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"maxEmbeddingsPerCall");r(this,"supportsParallelCalls",true);r(this,"baseURL");this.modelId=t,this.baseURL=e.baseURL||"http://localhost:11434";}async doEmbed(t){let{values:e,abortSignal:o}=t,a=Log.create({service:"ollama-embedding"});if(!e||e.length===0)return a.warn("Empty values array provided",{modelId:this.modelId}),{embeddings:[],usage:{tokens:0},warnings:[]};a.debug("Generating text embeddings",{modelId:this.modelId,valuesCount:e.length,baseURL:this.baseURL});try{let n=await Promise.all(e.map(c=>this.generateSingleEmbedding(c,o))),l=e.reduce((c,u)=>c+this.estimateTokens(u),0);return a.debug("Text embeddings generated successfully",{modelId:this.modelId,embeddingsCount:n.length,totalTokens:l}),{embeddings:n,usage:{tokens:l},warnings:[]}}catch(n){throw a.error("Text embedding generation failed",{modelId:this.modelId,error:n instanceof Error?n.message:String(n),stack:void 0}),n}}async generateSingleEmbedding(t,e){let o=`${this.baseURL}/api/embeddings`;try{let a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:this.modelId,prompt:t}),signal:e});if(!a.ok){let l=await a.text();throw new Error(`Ollama embedding API error (${a.status}): ${l}`)}let n=await a.json();if(!n.embedding||!Array.isArray(n.embedding))throw new Error("Invalid response format from Ollama embedding API");return n.embedding}catch(a){throw a instanceof TypeError&&a.message.includes("fetch")?new Error(`Failed to connect to Ollama server at ${this.baseURL}: ${a.message}`):a}}estimateTokens(t){let e=t.match(/[a-zA-Z]/g)?.length||0,o=t.match(/[\u4e00-\u9fa5]/g)?.length||0,a=t.length-e-o;return Math.ceil(e/4)+o+Math.ceil(a*.5)}};var j=class{constructor(t,e,o={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"languageModel");r(this,"options");r(this,"log",Log.create({service:"ollama-sdk",module:"reranking-model"}));this.modelId=t,this.languageModel=new I(t,e),this.options={totalScore:o.totalScore??1,useEnhancedPrompt:o.useEnhancedPrompt??true,promptLanguage:o.promptLanguage??"en"},this.log.debug("OllamaRerankingModel created",{modelId:t,totalScore:this.options.totalScore,useEnhancedPrompt:this.options.useEnhancedPrompt,promptLanguage:this.options.promptLanguage});}async doRerank(t){let{query:e,documents:o,topN:a}=t,n;Array.isArray(o)?n=o:o.type==="text"?n=o.values.map((u,b)=>({content:u,id:`doc_${b+1}`})):n=o.values;let l=a??n.length;if(n.length===0)return {ranking:[],warnings:[]};try{let u=this.buildRerankPrompt(e,n),O=(await this.languageModel.doGenerate({prompt:[{role:"user",content:[{type:"text",text:u}]}],maxOutputTokens:500,temperature:.1})).content.find(g=>g.type==="text")?.text||"",i=this.parseRerankResult(O,n,l);return {ranking:i.results.map(g=>({index:g.index,relevanceScore:g.score,document:g.document})),warnings:i.warnings.map(g=>({type:"other",message:g}))}}catch(u){return this.log.error("Reranking failed",{error:u.message}),{ranking:n.map((O,i)=>({index:i,relevanceScore:this.options.totalScore*(1-i/n.length),document:O})),warnings:[{type:"other",message:`Reranking failed, using original order: ${u.message}`}]}}}buildRerankPrompt(t,e){let o=e.map((a,n)=>{let l=a.id||`doc_${n+1}`,c=a.content.slice(0,300);return `${n+1}. [ID: ${l}] ${c}`}).join(`
1
+ import {Log}from'@easbot/utils';var Q=Object.defineProperty;var K=(s,t,e)=>t in s?Q(s,t,{enumerable:true,configurable:true,writable:true,value:e}):s[t]=e;var r=(s,t,e)=>K(s,typeof t!="symbol"?t+"":t,e);var P=class{constructor(t="http://localhost:11434"){r(this,"baseURL",t);}async*streamChat(t){let e=await fetch(`${this.baseURL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,stream:true})});if(!e.ok)throw new Error(`Ollama API error: ${e.status} ${e.statusText}`);if(!e.body)throw new Error("Response body is null");let o=e.body.getReader(),a=new TextDecoder,n="";try{for(;;){let{done:i,value:y}=await o.read();if(i){if(n.trim())try{yield JSON.parse(n);}catch{}break}n+=a.decode(y,{stream:!0});let g=n.split(`
2
+ `);n=g.pop()||"";for(let u of g){let c=u.trim();if(c)try{yield JSON.parse(c);}catch{}}}}finally{o.releaseLock();}}async chat(t){let e=await fetch(`${this.baseURL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,stream:false})});if(!e.ok)throw new Error(`Ollama API error: ${e.status} ${e.statusText}`);return e.json()}};var A={numCtx:262144,temperature:.7,topK:40,topP:.9,repeatPenalty:1.1,numPredict:32e3},Z=["numCtx","repeatPenalty","numPredict"];var _=class extends Error{constructor(e,o){super(e);r(this,"cause",o);this.name="OllamaError",Object.setPrototypeOf(this,new.target.prototype);}},j=class extends _{constructor(e,o,a){super(e,a);r(this,"url",o);this.name="ConnectionError";}},q=class extends _{constructor(e,o){super(`Model not found: ${e}`,o);r(this,"modelId",e);this.name="ModelNotFoundError";}},G=class extends _{constructor(e,o,a,n){super(e,n);r(this,"field",o);r(this,"value",a);this.name="ValidationError";}},J=class extends _{constructor(e,o,a){super(e,a);r(this,"timeoutMs",o);this.name="TimeoutError";}},S=class extends _{constructor(e,o,a){super(`Invalid tool definition for "${e}": missing ${o}`,a);r(this,"toolName",e);r(this,"missingField",o);this.name="InvalidToolDefinitionError";}};function N(s,t,e){let o;return t?o="tool-calls":e==="length"?o="length":s?o="stop":o="other",{unified:o,raw:e}}var C=Log.create({service:"ollama-sdk",module:"native-language-model"});function V(){return `${Date.now()}-${Math.random().toString(36).substring(2,9)}`}function D(s){if(!s||s.trim().length===0)return false;try{return JSON.parse(s),!0}catch{return false}}function W(s){return typeof s=="string"?s:JSON.stringify(s)}function Y(s){return !s.tools||s.tools.length===0?{}:{tools:s.tools.map(t=>{if(!t.name)throw new S(t.name||"unknown","name");let e=t.inputSchema||t.parameters;if(!e)throw new S(t.name,"inputSchema");return {type:"function",function:{name:t.name,description:t.description,parameters:e}}})}}function B(s){let t=[];for(let e of s)try{switch(e.role){case "system":t.push({role:"system",content:e.content});break;case "user":{let o=e.content.filter(a=>a.type==="text").map(a=>a.text).join(`
3
+ `);o&&t.push({role:"user",content:o});break}case "assistant":{let o="",a=[];for(let n of e.content)if(n.type==="text")o+=n.text;else if(n.type==="tool-call"){let i=n;a.push({id:i.toolCallId,type:"function",function:{name:i.toolName,arguments:JSON.stringify(i.input)}});}t.push({role:"assistant",content:o||null,tool_calls:a.length>0?a:void 0});break}case "tool":for(let o of e.content){let a=o.output,n;switch(a.type){case "text":case "error-text":n=a.value;break;case "content":case "json":case "error-json":n=JSON.stringify(a.value);break;default:n=String(a.value);break}t.push({role:"tool",tool_call_id:o.toolCallId,content:n});}break}}catch(o){C.warn("Failed to convert message",{messageRole:e.role,error:o instanceof Error?o.message:String(o)});}return t}var $=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"defaultObjectGenerationMode","json");r(this,"supportedUrls",{});r(this,"client");this.modelId=t,this.client=new P(e.baseURL);}async doStream(t){let e=V();C.debug("Stream request started",{traceId:e,modelId:this.modelId,hasTools:!!t.tools&&t.tools.length>0,toolCount:t.tools?.length||0,messageCount:t.prompt.length});let o=B(t.prompt),a=Y({tools:t.tools}),n=null,i=u=>{n&&n.enqueue(u);},y=async()=>{try{let u=!0,c=!1,l=!1,p="",m="",E=!1,I=[],d=[],F=()=>{if(d.length>0){for(let v of d)i(v);d.length=0;}},b=null;for await(let v of this.client.streamChat({model:this.modelId,messages:o,options:A,...a})){if(b&&b.chunkCount++,u){u=!1;let M={type:"response-metadata",id:V(),modelId:this.modelId,timestamp:new Date(v.created_at)};i(M),b&&b.eventCount++;}if(v.message?.tool_calls){l&&(d.push({type:"reasoning-end",id:"reasoning-0"}),l=!1),c&&(d.push({type:"text-end",id:"txt-0"}),c=!1);for(let M of v.message.tool_calls){if(!M.id||!M.function?.name){let T=M.id?"function.name":"id";C.error("Invalid tool call: missing field",{traceId:e,missingField:T,toolCall:void 0}),d.push({type:"error",error:`Invalid tool call: missing ${T}`});continue}let h=M.id,O=M.function.name,f=W(M.function.arguments),k=I.find(T=>T.id===h);k?(k.function.arguments+=f,C.debug("Tool call arguments accumulated",{traceId:e,toolCallId:h,toolName:O,deltaLength:f.length,totalLength:k.function.arguments.length,isComplete:D(k.function.arguments)}),f&&d.push({type:"tool-input-delta",id:h,delta:f})):(k={id:h,type:"function",function:{name:O,arguments:f},hasFinished:!1},I.push(k),b&&b.toolCallCount++,C.debug("Tool call received",{traceId:e,toolCallId:h,toolName:O,argumentsLength:f.length,isComplete:D(f)}),d.push({type:"tool-input-start",id:h,toolName:O}),f&&d.push({type:"tool-input-delta",id:h,delta:f}));let z=D(k.function.arguments);!k.hasFinished&&z&&(C.debug("Tool call arguments complete",{traceId:e,toolCallId:h,toolName:O,isComplete:z,argumentsLength:k.function.arguments.length}),d.push({type:"tool-input-end",id:h}),d.push({type:"tool-call",toolCallId:h,toolName:O,input:k.function.arguments}),k.hasFinished=!0);}F(),b&&(b.eventCount+=d.length);}if(v.message?.content){let M=v.message.content;for(m+=M;m.length>0;)if(E){let h=m.indexOf("</think>");if(h===-1){m.length>0&&d.push({type:"reasoning-delta",id:"reasoning-0",delta:m}),m="";break}else {if(h>0){let O=m.substring(0,h);d.push({type:"reasoning-delta",id:"reasoning-0",delta:O});}d.push({type:"reasoning-end",id:"reasoning-0"}),l=!1,E=!1,m=m.substring(h+8);}}else {let h=m.indexOf("<think>");if(h===-1){m.length>0&&(c||(c=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:m}),p+=m),m="";break}else {if(h>0){let O=m.substring(0,h);c||(c=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:O}),p+=O,d.push({type:"text-end",id:"txt-0"}),c=!1;}E=!0,m=m.substring(h+7),l||(l=!0,d.push({type:"reasoning-start",id:"reasoning-0"}));}}}if(F(),b&&(b.eventCount+=d.length),v.done){let M=I.filter(f=>!f.hasFinished);for(let f of M)d.push({type:"tool-input-end",id:f.id}),d.push({type:"tool-call",toolCallId:f.id,toolName:f.function.name,input:f.function.arguments});m.length>0&&(E?(d.push({type:"reasoning-delta",id:"reasoning-0",delta:m}),d.push({type:"reasoning-end",id:"reasoning-0"})):(c||(c=!0,d.push({type:"text-start",id:"txt-0"})),d.push({type:"text-delta",id:"txt-0",delta:m}),p+=m)),c&&d.push({type:"text-end",id:"txt-0"});let h=I.length>0,O=N(!0,h,"stop");if(d.push({type:"finish",finishReason:O,usage:{inputTokens:{total:v.prompt_eval_count||0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:v.eval_count||0,text:void 0,reasoning:void 0}},providerMetadata:{ollama:{totalDuration:v.total_duration||0,loadDuration:v.load_duration||0,promptEvalDuration:v.prompt_eval_duration||0,evalDuration:v.eval_duration||0}}}),F(),b&&(b.eventCount+=d.length),b);I.length=0;}}}catch(u){C.error("Stream processing error",{traceId:e,error:u instanceof Error?u.message:String(u),stack:void 0}),i({type:"error",error:u instanceof Error?u.message:String(u)}),i({type:"finish",finishReason:N(false,false,"error"),usage:{inputTokens:{total:0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:0,text:void 0,reasoning:void 0}}});}finally{n&&n.close();}},g=new ReadableStream({start(u){n=u;}});return y(),{stream:g}}async doGenerate(t){let e=V();C.debug("Generate request started",{traceId:e,modelId:this.modelId,hasTools:!!t.tools&&t.tools.length>0,toolCount:t.tools?.length||0,messageCount:t.prompt.length});let o=B(t.prompt),a=Y({tools:t.tools}),n=await this.client.chat({model:this.modelId,messages:o,options:A,...a}),i=[];if(n.message?.content&&i.push({type:"text",text:n.message.content}),n.message?.tool_calls){C.debug("Tool calls received in generate",{traceId:e,toolCallCount:n.message.tool_calls.length});for(let u of n.message.tool_calls){let c=u.id||V(),l=u.function.name,p=W(u.function.arguments);C.debug("Processing tool call in generate",{traceId:e,toolCallId:c,toolName:l,argumentsLength:p.length,isComplete:D(p)}),i.push({type:"tool-call",toolCallId:c,toolName:l,input:p});}}let y=!!(n.message?.tool_calls&&n.message.tool_calls.length>0),g=N(n.done??false,y,"stop");return C.debug("Generate request completed",{traceId:e,finishReason:g,contentParts:i.length,hasToolCalls:y,inputTokens:n.prompt_eval_count||0,outputTokens:n.eval_count||0}),{content:i,finishReason:g,usage:{inputTokens:{total:n.prompt_eval_count||0,noCache:void 0,cacheRead:void 0,cacheWrite:void 0},outputTokens:{total:n.eval_count||0,text:void 0,reasoning:void 0}},providerMetadata:{ollama:{totalDuration:n.total_duration||0,loadDuration:n.load_duration||0,promptEvalDuration:n.prompt_eval_duration||0,evalDuration:n.eval_duration||0}},warnings:[],response:{id:e,timestamp:new Date(n.created_at||new Date().toISOString()),modelId:this.modelId}}}};var R=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"defaultObjectGenerationMode","json");r(this,"supportedUrls",{});r(this,"nativeModel");this.modelId=t,this.nativeModel=new $(t,{baseURL:e.baseURL||"http://localhost:11434"});}doGenerate(t){return this.nativeModel.doGenerate(t)}doStream(t){return this.nativeModel.doStream(t)}};var w=class{constructor(t,e={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"maxEmbeddingsPerCall");r(this,"supportsParallelCalls",true);r(this,"baseURL");this.modelId=t,this.baseURL=e.baseURL||"http://localhost:11434";}async doEmbed(t){let{values:e,abortSignal:o}=t,a=Log.create({service:"ollama-embedding"});if(!e||e.length===0)return a.warn("Empty values array provided",{modelId:this.modelId}),{embeddings:[],usage:{tokens:0},warnings:[]};a.debug("Generating text embeddings",{modelId:this.modelId,valuesCount:e.length,baseURL:this.baseURL});try{let n=await Promise.all(e.map(y=>this.generateSingleEmbedding(y,o))),i=e.reduce((y,g)=>y+this.estimateTokens(g),0);return a.debug("Text embeddings generated successfully",{modelId:this.modelId,embeddingsCount:n.length,totalTokens:i}),{embeddings:n,usage:{tokens:i},warnings:[]}}catch(n){throw a.error("Text embedding generation failed",{modelId:this.modelId,error:n instanceof Error?n.message:String(n),stack:void 0}),n}}async generateSingleEmbedding(t,e){let o=`${this.baseURL}/api/embeddings`;try{let a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:this.modelId,prompt:t}),signal:e});if(!a.ok){let i=await a.text();throw new Error(`Ollama embedding API error (${a.status}): ${i}`)}let n=await a.json();if(!n.embedding||!Array.isArray(n.embedding))throw new Error("Invalid response format from Ollama embedding API");return n.embedding}catch(a){throw a instanceof TypeError&&a.message.includes("fetch")?new Error(`Failed to connect to Ollama server at ${this.baseURL}: ${a.message}`):a}}estimateTokens(t){let e=t.match(/[a-zA-Z]/g)?.length||0,o=t.match(/[\u4e00-\u9fa5]/g)?.length||0,a=t.length-e-o;return Math.ceil(e/4)+o+Math.ceil(a*.5)}};var U=class{constructor(t,e,o={}){r(this,"specificationVersion","v3");r(this,"provider","ollama");r(this,"modelId");r(this,"languageModel");r(this,"options");r(this,"log",Log.create({service:"ollama-sdk",module:"reranking-model"}));this.modelId=t,this.languageModel=new R(t,e),this.options={totalScore:o.totalScore??1,useEnhancedPrompt:o.useEnhancedPrompt??true,promptLanguage:o.promptLanguage??"en"},this.log.debug("OllamaRerankingModel created",{modelId:t,totalScore:this.options.totalScore,useEnhancedPrompt:this.options.useEnhancedPrompt,promptLanguage:this.options.promptLanguage});}async doRerank(t){let{query:e,documents:o,topN:a}=t,n;Array.isArray(o)?n=o:o.type==="text"?n=o.values.map((g,u)=>({content:g,id:`doc_${u+1}`})):n=o.values;let i=a??n.length;if(n.length===0)return {ranking:[],warnings:[]};try{let g=this.buildRerankPrompt(e,n),c=(await this.languageModel.doGenerate({prompt:[{role:"user",content:[{type:"text",text:g}]}],maxOutputTokens:500,temperature:.1})).content.find(p=>p.type==="text")?.text||"";this.log.debug("Rerank LLM raw output",{query:e,textContent:c,documentsCount:n.length});let l=this.parseRerankResult(c,n,i);return {ranking:l.results.map(p=>({index:p.index,relevanceScore:p.score})),warnings:l.warnings.map(p=>({type:"other",message:p}))}}catch(g){return this.log.error("Reranking failed",{error:g.message}),{ranking:n.map((c,l)=>({index:l,relevanceScore:n.length>1?this.options.totalScore*(1-.9*l/(n.length-1)):this.options.totalScore,document:c})),warnings:[{type:"other",message:`Reranking failed, using original order: ${g.message}`}]}}}buildRerankPrompt(t,e){let o=e.map((a,n)=>{let i=a.id||`doc_${n+1}`,y=a.content.slice(0,300);return `${n+1}. [ID: ${i}] ${y}`}).join(`
4
4
 
5
5
  `);if(this.options.promptLanguage==="en")return `You are a professional information retrieval reranking expert. Please rank the following documents according to the query.
6
6
 
@@ -13,18 +13,16 @@ Please rank the documents according to the following requirements:
13
13
  1. Carefully analyze the user's query intent and key information needs
14
14
  2. Evaluate each document's relevance to the query
15
15
  3. Consider the document's completeness, accuracy, and usefulness
16
- 4. Return the ranked document numbers separated by commas
17
- 5. Most relevant documents should be at the front
16
+ 4. Return ONLY the document numbers (1, 2, 3, ...) in order from MOST relevant to LEAST relevant, separated by commas
17
+ 5. Most relevant documents MUST be at the front
18
+ 6. Every document must appear exactly once in your output
18
19
 
19
- Use a scoring scale from ${this.options.totalScore*.1} to ${this.options.totalScore} where ${this.options.totalScore*.1} is most relevant and ${this.options.totalScore} is least relevant.
20
- The actual score for each position should be calculated dynamically. For example:
21
- - If totalScore is 10: scores might be: 9, 7, 10, 4, 2
22
- - If totalScore is 1: scores might be: 0.2, 0.5, 1.0, 0.06, 0.04
23
- - If totalScore is 5: scores might be: 0.1, 0.25, 0.5, 0.03, 0.02
20
+ IMPORTANT:
21
+ - You MUST actually reorder the documents based on relevance to the query
22
+ - Do NOT output the original order unless it happens to be the correct relevance order
23
+ - Output format: just the numbers separated by commas, nothing else
24
24
 
25
- Example output format: 2,5,1,4,3
26
-
27
- Please only output the ranked document numbers separated by commas, do not include the actual scores.`;{let a=this.options.totalScore===1?"1.0":`${this.options.totalScore}`;return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u4FE1\u606F\u68C0\u7D22\u91CD\u6392\u5E8F\u4E13\u5BB6\u3002\u8BF7\u6839\u636E\u7528\u6237\u67E5\u8BE2\u5BF9\u6587\u6863\u8FDB\u884C\u76F8\u5173\u6027\u6392\u5E8F\u3002
25
+ Example output format: 2,5,1,4,3 (where document 2 is most relevant to the query)`;{this.options.totalScore===1?"1.0":`${this.options.totalScore}`;return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u4FE1\u606F\u68C0\u7D22\u91CD\u6392\u5E8F\u4E13\u5BB6\u3002\u8BF7\u6839\u636E\u7528\u6237\u67E5\u8BE2\u5BF9\u6587\u6863\u8FDB\u884C\u76F8\u5173\u6027\u6392\u5E8F\u3002
28
26
 
29
27
  \u7528\u6237\u67E5\u8BE2\uFF1A${t}
30
28
 
@@ -35,10 +33,13 @@ ${o}
35
33
  1. \u4ED4\u7EC6\u5206\u6790\u7528\u6237\u67E5\u8BE2\u7684\u610F\u56FE\u548C\u5173\u952E\u4FE1\u606F\u9700\u6C42
36
34
  2. \u8BC4\u4F30\u6BCF\u4E2A\u6587\u6863\u4E0E\u67E5\u8BE2\u7684\u76F8\u5173\u6027
37
35
  3. \u8003\u8651\u6587\u6863\u7684\u5B8C\u6574\u6027\u3001\u51C6\u786E\u6027\u548C\u5B9E\u7528\u6027
38
- 4. \u8FD4\u56DE\u6392\u5E8F\u540E\u7684\u6587\u6863\u7F16\u53F7\uFF0C\u7528\u9017\u53F7\u5206\u9694
39
- 5. \u6700\u76F8\u5173\u7684\u6587\u6863\u6392\u5728\u6700\u524D\u9762
40
- 6. \u8BF7\u4F7F\u7528 ${a} \u5206\u503C\u8FDB\u884C\u8BC4\u5206
36
+ 4. \u53EA\u8F93\u51FA\u6587\u6863\u7F16\u53F7\uFF081, 2, 3, ...\uFF09\uFF0C\u6309"\u6700\u76F8\u5173"\u5230"\u6700\u4E0D\u76F8\u5173"\u987A\u5E8F\u6392\u5217\uFF0C\u7528\u9017\u53F7\u5206\u9694
37
+ 5. \u6700\u76F8\u5173\u7684\u6587\u6863\u5FC5\u987B\u6392\u5728\u6700\u524D\u9762
38
+ 6. \u6BCF\u4E2A\u6587\u6863\u5FC5\u987B\u51FA\u73B0\u4E14\u4EC5\u51FA\u73B0\u4E00\u6B21
41
39
 
42
- \u793A\u4F8B\u8F93\u51FA\u683C\u5F0F\uFF1A2,5,1,4,3
40
+ \u91CD\u8981\u63D0\u793A\uFF1A
41
+ - \u5FC5\u987B\u6839\u636E\u4E0E\u67E5\u8BE2\u7684\u76F8\u5173\u6027\u5B9E\u9645\u91CD\u65B0\u6392\u5E8F\u6587\u6863
42
+ - \u9664\u975E\u539F\u59CB\u987A\u5E8F\u6070\u597D\u5C31\u662F\u6B63\u786E\u7684\u76F8\u5173\u6027\u6392\u5E8F\uFF0C\u5426\u5219\u4E0D\u8981\u8F93\u51FA\u539F\u59CB\u987A\u5E8F
43
+ - \u8F93\u51FA\u683C\u5F0F\uFF1A\u4EC5\u5305\u542B\u7528\u9017\u53F7\u5206\u9694\u7684\u7F16\u53F7\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u5185\u5BB9
43
44
 
44
- \u8BF7\u53EA\u8F93\u51FA\u6392\u5E8F\u540E\u7684\u7F16\u53F7\uFF0C\u4E0D\u8981\u5305\u542B\u5176\u4ED6\u89E3\u91CA\u3002`}}parseRerankResult(t,e,o){let a=[];try{let n=t.match(/\d+/g)?.map(i=>parseInt(i,10)-1).filter(i=>i>=0&&i<e.length)||[],l=[],c=new Set;for(let i of n)c.has(i)||(c.add(i),l.push(i));let u=[];for(let i=0;i<l.length;i++){let g=l[i];if(g!==void 0&&g>=0&&g<e.length){let m=e[g];m&&u.push({index:g,score:this.options.totalScore*(1-i/l.length),document:m});}}let b=new Set(l);for(let i=0;i<e.length;i++)if(!b.has(i)){let g=this.options.totalScore*.1*(1-i/e.length);u.push({index:i,score:g,document:e[i]});}let O=o?u.slice(0,o):u;return this.log.debug("Parsed rerank results",{extractedNumbers:n,uniqueIndices:l,resultCount:O.length,topK:o}),{results:O,warnings:a}}catch(n){return this.log.error("Failed to parse rerank result",{error:n.message,text:t}),{results:e.map((c,u)=>({index:u,score:this.options.totalScore*(1-u/e.length),document:c})),warnings:[`Failed to parse rerank result: ${n.message}`]}}}};function re(s={}){return Object.assign(e=>new I(e,s),{languageModel:e=>new I(e,s),chat:e=>new I(e,s),embeddingModel:e=>new N(e,s),rerankingModel:(e,o)=>new j(e,s,o)})}var se="0.1.0";export{G as ConnectionError,z as ModelNotFoundError,E as OLLAMA_DEFAULT_OPTIONS,te as OLLAMA_SPECIFIC_PARAMS,N as OllamaEmbeddingModel,L as OllamaError,I as OllamaLanguageModel,W as TimeoutError,se as VERSION,B as ValidationError,re as createOllama};
45
+ \u793A\u4F8B\u8F93\u51FA\u683C\u5F0F\uFF1A2,5,1,4,3\uFF08\u5176\u4E2D\u6587\u6863 2 \u4E0E\u67E5\u8BE2\u6700\u76F8\u5173\uFF09`}}parseRerankResult(t,e,o){let a=[];try{let n=t.match(/\d+/g)?.map(l=>parseInt(l,10)-1).filter(l=>l>=0&&l<e.length)||[],i=[],y=new Set;for(let l of n)y.has(l)||(y.add(l),i.push(l));let g=[];for(let l=0;l<i.length;l++){let p=i[l];if(p!==void 0&&p>=0&&p<e.length){let m=e[p];m&&g.push({index:p,score:i.length>1?this.options.totalScore*(1-.9*l/(i.length-1)):this.options.totalScore,document:m});}}let u=new Set(i);for(let l=0;l<e.length;l++)if(!u.has(l)){let p=this.options.totalScore*.1;g.push({index:l,score:p,document:e[l]});}let c=o?g.slice(0,o):g;return this.log.debug("Parsed rerank results",{extractedNumbers:n,uniqueIndices:i,resultCount:c.length,topK:o}),{results:c,warnings:a}}catch(n){return this.log.error("Failed to parse rerank result",{error:n.message,text:t}),{results:e.map((y,g)=>({index:g,score:e.length>1?this.options.totalScore*(1-.9*g/(e.length-1)):this.options.totalScore,document:y})),warnings:[`Failed to parse rerank result: ${n.message}`]}}}};function te(s={}){return Object.assign(e=>new R(e,s),{languageModel:e=>new R(e,s),chat:e=>new R(e,s),embeddingModel:e=>new w(e,s),rerankingModel:(e,o)=>new U(e,s,o)})}var ne="0.1.0";export{j as ConnectionError,q as ModelNotFoundError,A as OLLAMA_DEFAULT_OPTIONS,Z as OLLAMA_SPECIFIC_PARAMS,w as OllamaEmbeddingModel,_ as OllamaError,R as OllamaLanguageModel,J as TimeoutError,ne as VERSION,G as ValidationError,te as createOllama};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easbot/ollama-sdk",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Ollama provider for Vercel AI SDK - Run local LLMs with Ollama in your AI applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -44,8 +44,8 @@
44
44
  "ai": "^6.0.176",
45
45
  "axios": "^1.16.1",
46
46
  "zod": "^4.4.3",
47
- "@easbot/types": "0.3.3",
48
- "@easbot/utils": "0.3.3"
47
+ "@easbot/types": "0.3.5",
48
+ "@easbot/utils": "0.3.5"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@biomejs/biome": "^2.4.14",