UNPKG

@ahmedrowaihi/flowise-vercel-ai-sdk-provider

Version:
11 lines (10 loc) 12.6 kB
/** * @ahmedrowaihi/flowise-vercel-ai-sdk-provider v2.1.1 * Flowise AI SDK Provider * Ahmed Rowaihi * MIT * git+https://github.com/ahmedrowaihi/flowise-ai-sdk-provider.git * https://github.com/ahmedrowaihi/flowise-ai-sdk-provider#readme */ "use strict";const flowiseSdk=require("flowise-sdk"),node_buffer=require("node:buffer"),l={allowMessageTypes:["userMessage","apiMessage"]};function convertToAiSdkMessage(n,a=l){const i=new Set(a.allowMessageTypes||[]);return n.filter(e=>i.has(e.role)).map(e=>{const r=[];if(e.content&&typeof e.content=="string"){const d={type:"text",text:e.content};r.push(d)}if(e.agentReasoning)try{const d=JSON.parse(e.agentReasoning);Array.isArray(d)&&d.forEach(s=>{s&&s.type==="text"&&typeof s.text=="string"&&r.push({type:"text",text:`[Reasoning: ${s.text}]`})})}catch{r.push({type:"text",text:`[Reasoning: ${String(e.agentReasoning)}]`})}if(e.usedTools)try{const d=JSON.parse(e.usedTools);Array.isArray(d)&&d.forEach(s=>{s&&s.toolCallId&&s.toolName&&r.push({type:`tool-${s.toolName}`,toolCallId:s.toolCallId,state:"input-available",input:s.args||{},providerExecuted:!1})})}catch{}return{role:e.role==="userMessage"?"user":"assistant",id:e.id,parts:r.length>0?r:[]}}).sort((e,r)=>{const d=n.find(f=>f.id===e.id),s=n.find(f=>f.id===r.id);return d&&s&&d.createdDate&&s.createdDate?new Date(d.createdDate).getTime()-new Date(s.createdDate).getTime():0})}function convertToFlowiseMessage(n){if(n.length===1&&n[0]?.role==="user"){const a=n[0];return Array.isArray(a.content)?a.content.map(i=>i&&i.type==="text"&&typeof i.text=="string"?i.text:"").join(""):a.content}return n.map(a=>{let i;return Array.isArray(a.content)?i=a.content.map(e=>e&&e.type==="text"&&typeof e.text=="string"?e.text:"").join(""):i=a.content,`${a.role==="user"?"User":"Assistant"}: ${i}`}).join(` `)}function buildStreamPart(n){return n}function estimateTokens(n){return Math.ceil(n.trim().split(/\s+/).length*1.3)}function generateUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{const a=Math.random()*16|0;return(n==="x"?a:a&3|8).toString(16)})}async function fetchFileAsBase64(n,a){const i=await fetch(n);if(!i.ok)throw new Error(`Failed to fetch file: ${n}`);const e=await i.arrayBuffer(),r=Buffer.from(e).toString("base64");return`data:${a||i.headers.get("content-type")||"application/octet-stream"};base64,${r}`}const silentLogger={debug:()=>{},warn:()=>{},error:()=>{}};let o=silentLogger;function getLogger(){return o}function flowiseEventToAiSdkStream(n,a){const{args:i,logger:e}=a,r=estimateTokens(i.question),d=generateUUID();let s=0;return new ReadableStream({async start(f){let h=!1,p=!1,g=!1;try{for await(const t of n)if(t)switch(t.event){case"start":p||(f.enqueue({type:"stream-start",warnings:[]}),p=!0);break;case"token":g||(f.enqueue({type:"text-start",id:d}),g=!0),typeof t.data=="string"&&t.data.length>0&&(s+=estimateTokens(t.data),f.enqueue({type:"text-delta",id:d,delta:t.data}));break;case"end":g&&(f.enqueue({type:"text-end",id:d}),g=!1),f.enqueue({type:"finish",usage:{inputTokens:r,outputTokens:s,totalTokens:r+s},finishReason:"stop"}),h=!0,f.close();break;case"sourceDocuments":e.debug("Flowise provider: sourceDocuments event received",t);break;case"artifacts":e.debug("Flowise provider: artifacts event received",t);break;case"usedTools":e.debug("Flowise provider: usedTools event received",t);break;case"fileAnnotations":e.debug("Flowise provider: fileAnnotations event received",t);break;case"tool":e.debug("Flowise provider: tool event received",t);break;case"agentReasoning":e.debug("Flowise provider: agentReasoning event received",t);break;case"nextAgent":e.debug("Flowise provider: nextAgent event received",t);break;case"agentFlowEvent":e.debug("Flowise provider: agentFlowEvent event received",t);break;case"agentFlowExecutedData":e.debug("Flowise provider: agentFlowExecutedData event received",t);break;case"nextAgentFlow":e.debug("Flowise provider: nextAgentFlow event received",t);break;case"action":e.debug("Flowise provider: action event received",t);break;case"abort":e.debug("Flowise provider: abort event received",t);break;case"error":e.error("Flowise provider: error event received",t);break;case"metadata":e.debug("Flowise provider: metadata event received",t);break;default:e.warn("Flowise provider: Skipping unknown event type:",t)}h||(g&&f.enqueue({type:"text-end",id:d}),f.close())}catch(t){e.error("Flowise provider: stream error",t),f.error(t)}}})}async function getUploadConfig({chatflowId:n,fetchWithAuth:a,logger:i}){try{const e=await a(`/api/v1/chatflows/${n}`);if(e.ok){const r=await e.json();if(typeof r.chatbotConfig=="string")return JSON.parse(r.chatbotConfig)}}catch(e){i.warn("Failed to fetch authenticated chatflow config:",e)}try{const e=await a(`/api/v1/public-chatbotConfig/${n}`);if(e.ok){const r=await e.json();return r.chatbotConfig||r}}catch(e){i.warn("Failed to fetch public chatflow config:",e)}try{const e=await a(`/api/v1/chatflows-uploads/${n}`);if(e.ok)return await e.json()}catch(e){i.warn("Failed to fetch uploads config:",e)}return{}}async function transformAttachmentToFlowise({file:n,chatflowId:a,chatId:i,fetchWithAuth:e,logger:r}){let d=n.data;d.startsWith("data:")&&(d=d.substring(d.indexOf(",")+1));const s=node_buffer.Buffer.from(d,"base64");let f;try{f=new FormData,f.append("files",new Blob([s],{type:n.mime}),n.name)}catch{f=new FormData,f.append("files",s,{filename:n.name,contentType:n.mime})}r.debug(`[transformAttachmentToFlowise] Sending file to attachment API: name=${n.name}, mime=${n.mime}, size=${s.length}`);try{const h=await e(`/api/v1/attachments/${a}/${i}`,{method:"POST",body:f});if(r.debug(`[transformAttachmentToFlowise] Attachment API response status: ${h.status} ${h.statusText}`),!h.ok){r.warn(`[transformAttachmentToFlowise] Attachment API failed for file ${n.name}: ${h.statusText}`);return}const p=await h.json();r.debug(`[transformAttachmentToFlowise] Attachment API response for file ${n.name}: ${JSON.stringify(p).slice(0,500)}`);let g;if(Array.isArray(p)){const t=p.find(u=>u.name===n.name&&u.mimeType===n.mime);t&&typeof t.content=="string"&&(g=t.content)}else p&&typeof p.content=="string"&&(g=p.content);if(g)return g;r.warn(`[transformAttachmentToFlowise] Attachment API response missing 'content' for file ${n.name}`);return}catch(h){r.warn(`[transformAttachmentToFlowise] Attachment API error for file ${n.name}:`,h);return}}async function transformUploadsFromPrompt({prompt:n,userChatId:a,getUploadConfig:i,chatflowId:e,logger:r}){const d=await i(e),s=d.uploads??{},f=d.chatbotConfig??d;r.debug("[transformUploadsFromPrompt] Upload config:",f);const h=[],p=[];let g=a;for(const t of n)if(t.role==="user"&&Array.isArray(t.content)){for(const u of t.content)if(u.type==="file"){const c=u;let w,y;if(c.data){if(typeof c.data=="string")c.data.startsWith("data:")?(w=c.data,y=c.data.length):/^[A-Za-z0-9+/=]+$/.test(c.data)?(w=`data:${c.mediaType||"application/octet-stream"};base64,${c.data}`,y=c.data.length):c.data.startsWith("http")&&(w=await fetchFileAsBase64(c.data,c.mediaType),y=w.length);else if(c.data instanceof Uint8Array||c.data instanceof ArrayBuffer||typeof Buffer<"u"&&c.data instanceof Buffer){let m;if(c.data instanceof Buffer)m=c.data;else if(c.data instanceof Uint8Array)m=Buffer.from(c.data);else if(c.data instanceof ArrayBuffer)m=Buffer.from(new Uint8Array(c.data));else continue;const A=m.toString("base64");w=`data:${c.mediaType||"application/octet-stream"};base64,${A}`,y=m.length}}let b="file";const x=m=>!!m&&typeof m=="object"&&"status"in m;x(s.fullFileUpload)&&s.fullFileUpload.status||x(f.fullFileUpload)&&f.fullFileUpload.status?b="file:full":s.isRAGFileUploadAllowed||f.isRAGFileUploadAllowed?b="file:rag":(s.isImageUploadAllowed||f.isImageUploadAllowed)&&c.mediaType?.startsWith("image/")&&(b="file"),r.debug(`[transformUploadsFromPrompt] Chosen uploadType for file '${c.filename??"file"}':`,b),s.fileUploadSizeAndTypes&&y!==void 0&&(s.fileUploadSizeAndTypes.some(m=>(!m.types||m.types.includes(c.mediaType))&&(!m.maxSize||y<=m.maxSize))||p.push(`[transformUploadsFromPrompt] File type or size not allowed by chatflow config: ${c.mediaType}, size: ${y}`)),w&&h.push({type:b,name:c.filename??"file",data:w,mime:c.mediaType||"application/octet-stream"})}}return h.some(t=>t.type==="file:full")&&!g&&(g=generateUUID()),{uploads:h,warnings:p,chatId:g}}class FlowiseChatModel{specificationVersion="v2";defaultObjectGenerationMode="json";supportsImageUrls=!1;modelId="flowise-chatflow";supportedUrls={"http:":[/.*/],"https:":[/.*/]};client;logger;chatflowId;uploadConfigCache={};baseUrl;apiKey;constructor(a){this.client=a.client,this.logger=a.logger??getLogger(),this.chatflowId=a.chatflowId,this.baseUrl=a.baseUrl,this.apiKey=a.apiKey}get provider(){return"@ahmedrowaihi/flowise-ai"}supportsUrl(a){return a.protocol==="https:"||a.protocol==="http:"}async fetchWithAuth(a,i){let e=a;typeof a=="string"&&(a.startsWith("/")?e=this.baseUrl.replace(/\/$/,"")+a:a.startsWith("http")||(e=`${this.baseUrl.replace(/\/$/,"")}/${a}`));const r={...i?.headers||{}};return this.apiKey&&(r.Authorization=`Bearer ${this.apiKey}`),fetch(e,{...i,headers:r})}async getArgs(a){const i=a.providerOptions?.chatId,{prompt:e}=a,r=[],d=await getUploadConfig({chatflowId:this.chatflowId,fetchWithAuth:this.fetchWithAuth.bind(this),logger:this.logger}),{uploads:s,warnings:f,chatId:h}=await transformUploadsFromPrompt({prompt:e,userChatId:i,getUploadConfig:async()=>d,chatflowId:this.chatflowId,logger:this.logger});let p=s,g=h;if(this.logger.debug(`[FlowiseChatModel] Uploads before attachment API: ${JSON.stringify(p.map(t=>({name:t.name,type:t.type,mime:t.mime,dataLength:t.data.length})))} `),p.some(t=>t.type==="file:full")&&g)for(let t=0;t<p.length;++t){const u=p[t];if(u&&u.type==="file:full"){const c=await transformAttachmentToFlowise({file:{name:u.name,data:u.data,mime:u.mime},chatflowId:this.chatflowId,chatId:g,fetchWithAuth:this.fetchWithAuth.bind(this),logger:this.logger});c?p[t]={type:u.type,name:u.name,data:c,mime:u.mime}:r.push({type:"other",message:`[FlowiseChatModel] Failed to extract content for file: ${u.name}`})}}return this.logger.debug(`[FlowiseChatModel] Uploads after attachment API: ${JSON.stringify(p.map(t=>({name:t.name,type:t.type,mime:t.mime,dataLength:t.data.length})))} `),p.length>0&&this.logger.debug("[FlowiseChatModel] Extracted uploads:",p.map(t=>({name:t.name,type:t.type,mime:t.mime,dataLength:t.data.length}))),{args:{question:convertToFlowiseMessage(e),chatId:g,uploads:p.length>0?p:void 0},warnings:[...r,...f.map(t=>({type:"other",message:t}))]}}async doGenerate(a){const{args:i,warnings:e}=await this.getArgs(a);try{const r=await this.client.createPrediction({...i,chatflowId:this.chatflowId,streaming:!1});if(typeof r=="object"&&"text"in r)return r.text||(r.text="",e.push({message:"No response from Flowise",type:"other"})),{content:[{type:"text",text:r.text}],finishReason:"stop",usage:{inputTokens:estimateTokens(i.question),outputTokens:estimateTokens(r.text),totalTokens:estimateTokens(i.question)+estimateTokens(r.text)},warnings:e};throw new Error("Unexpected streaming response for non-streaming call")}catch(r){throw new Error(`Flowise API error: ${r instanceof Error?r.message:String(r)}`)}}async doStream(a){const{args:i}=await this.getArgs(a);try{const e=await this.client.createPrediction({...i,chatflowId:this.chatflowId,streaming:!0});if(typeof e=="object"&&Symbol.asyncIterator in e)return{stream:flowiseEventToAiSdkStream(e,{args:i,logger:this.logger})};throw new Error("Expected streaming response for doStream")}catch(e){throw new Error(`Flowise streaming error: ${e instanceof Error?e.message:String(e)}`)}}}function createFlowiseProvider(n){const{logger:a,...i}=n,e=new flowiseSdk.FlowiseClient(i),r=s=>new FlowiseChatModel({client:e,chatflowId:s,logger:a,baseUrl:i.baseUrl,apiKey:i.apiKey});function d(s){if(new.target)throw new Error("The Flowise model function cannot be called with the new keyword.");return r(s)}return Object.assign(d,{languageModel:r,textEmbeddingModel:s=>{throw new Error(`Text embedding model '${s}' not supported by Flowise`)},imageModel:s=>{throw new Error(`Image model '${s}' not supported by Flowise`)},transcriptionModel:s=>{throw new Error(`Transcription model '${s}' not supported by Flowise`)},speechModel:s=>{throw new Error(`Speech model '${s}' not supported by Flowise`)},chat:r,client:e})}function createFlowiseModel(n){const{chatflowId:a,...i}=n;return createFlowiseProvider(i)(a)}exports.convertToAiSdkMessage=convertToAiSdkMessage,exports.convertToFlowiseMessage=convertToFlowiseMessage,exports.createFlowiseModel=createFlowiseModel,exports.createFlowiseProvider=createFlowiseProvider;