UNPKG

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

Version:
11 lines (10 loc) 12 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 */ import{FlowiseClient as $}from"flowise-sdk";import{Buffer as I}from"node:buffer";const T={allowMessageTypes:["userMessage","apiMessage"]};function U(r,a=T){const i=new Set(a.allowMessageTypes||[]);return r.filter(e=>i.has(e.role)).map(e=>{const o=[];if(e.content&&typeof e.content=="string"){const l={type:"text",text:e.content};o.push(l)}if(e.agentReasoning)try{const l=JSON.parse(e.agentReasoning);Array.isArray(l)&&l.forEach(n=>{n&&n.type==="text"&&typeof n.text=="string"&&o.push({type:"text",text:`[Reasoning: ${n.text}]`})})}catch{o.push({type:"text",text:`[Reasoning: ${String(e.agentReasoning)}]`})}if(e.usedTools)try{const l=JSON.parse(e.usedTools);Array.isArray(l)&&l.forEach(n=>{n&&n.toolCallId&&n.toolName&&o.push({type:`tool-${n.toolName}`,toolCallId:n.toolCallId,state:"input-available",input:n.args||{},providerExecuted:!1})})}catch{}return{role:e.role==="userMessage"?"user":"assistant",id:e.id,parts:o.length>0?o:[]}}).sort((e,o)=>{const l=r.find(c=>c.id===e.id),n=r.find(c=>c.id===o.id);return l&&n&&l.createdDate&&n.createdDate?new Date(l.createdDate).getTime()-new Date(n.createdDate).getTime():0})}function x(r){if(r.length===1&&r[0]?.role==="user"){const a=r[0];return Array.isArray(a.content)?a.content.map(i=>i&&i.type==="text"&&typeof i.text=="string"?i.text:"").join(""):a.content}return r.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 q(r){return r}function w(r){return Math.ceil(r.trim().split(/\s+/).length*1.3)}function A(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{const a=Math.random()*16|0;return(r==="x"?a:a&3|8).toString(16)})}async function k(r,a){const i=await fetch(r);if(!i.ok)throw new Error(`Failed to fetch file: ${r}`);const e=await i.arrayBuffer(),o=Buffer.from(e).toString("base64");return`data:${a||i.headers.get("content-type")||"application/octet-stream"};base64,${o}`}const S={debug:()=>{},warn:()=>{},error:()=>{}};let E=S;function C(){return E}function M(r,a){const{args:i,logger:e}=a,o=w(i.question),l=A();let n=0;return new ReadableStream({async start(c){let p=!1,d=!1,f=!1;try{for await(const t of r)if(t)switch(t.event){case"start":d||(c.enqueue({type:"stream-start",warnings:[]}),d=!0);break;case"token":f||(c.enqueue({type:"text-start",id:l}),f=!0),typeof t.data=="string"&&t.data.length>0&&(n+=w(t.data),c.enqueue({type:"text-delta",id:l,delta:t.data}));break;case"end":f&&(c.enqueue({type:"text-end",id:l}),f=!1),c.enqueue({type:"finish",usage:{inputTokens:o,outputTokens:n,totalTokens:o+n},finishReason:"stop"}),p=!0,c.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)}p||(f&&c.enqueue({type:"text-end",id:l}),c.close())}catch(t){e.error("Flowise provider: stream error",t),c.error(t)}}})}async function P({chatflowId:r,fetchWithAuth:a,logger:i}){try{const e=await a(`/api/v1/chatflows/${r}`);if(e.ok){const o=await e.json();if(typeof o.chatbotConfig=="string")return JSON.parse(o.chatbotConfig)}}catch(e){i.warn("Failed to fetch authenticated chatflow config:",e)}try{const e=await a(`/api/v1/public-chatbotConfig/${r}`);if(e.ok){const o=await e.json();return o.chatbotConfig||o}}catch(e){i.warn("Failed to fetch public chatflow config:",e)}try{const e=await a(`/api/v1/chatflows-uploads/${r}`);if(e.ok)return await e.json()}catch(e){i.warn("Failed to fetch uploads config:",e)}return{}}async function B({file:r,chatflowId:a,chatId:i,fetchWithAuth:e,logger:o}){let l=r.data;l.startsWith("data:")&&(l=l.substring(l.indexOf(",")+1));const n=I.from(l,"base64");let c;try{c=new FormData,c.append("files",new Blob([n],{type:r.mime}),r.name)}catch{c=new FormData,c.append("files",n,{filename:r.name,contentType:r.mime})}o.debug(`[transformAttachmentToFlowise] Sending file to attachment API: name=${r.name}, mime=${r.mime}, size=${n.length}`);try{const p=await e(`/api/v1/attachments/${a}/${i}`,{method:"POST",body:c});if(o.debug(`[transformAttachmentToFlowise] Attachment API response status: ${p.status} ${p.statusText}`),!p.ok){o.warn(`[transformAttachmentToFlowise] Attachment API failed for file ${r.name}: ${p.statusText}`);return}const d=await p.json();o.debug(`[transformAttachmentToFlowise] Attachment API response for file ${r.name}: ${JSON.stringify(d).slice(0,500)}`);let f;if(Array.isArray(d)){const t=d.find(g=>g.name===r.name&&g.mimeType===r.mime);t&&typeof t.content=="string"&&(f=t.content)}else d&&typeof d.content=="string"&&(f=d.content);if(f)return f;o.warn(`[transformAttachmentToFlowise] Attachment API response missing 'content' for file ${r.name}`);return}catch(p){o.warn(`[transformAttachmentToFlowise] Attachment API error for file ${r.name}:`,p);return}}async function j({prompt:r,userChatId:a,getUploadConfig:i,chatflowId:e,logger:o}){const l=await i(e),n=l.uploads??{},c=l.chatbotConfig??l;o.debug("[transformUploadsFromPrompt] Upload config:",c);const p=[],d=[];let f=a;for(const t of r)if(t.role==="user"&&Array.isArray(t.content)){for(const g of t.content)if(g.type==="file"){const s=g;let u,m;if(s.data){if(typeof s.data=="string")s.data.startsWith("data:")?(u=s.data,m=s.data.length):/^[A-Za-z0-9+/=]+$/.test(s.data)?(u=`data:${s.mediaType||"application/octet-stream"};base64,${s.data}`,m=s.data.length):s.data.startsWith("http")&&(u=await k(s.data,s.mediaType),m=u.length);else if(s.data instanceof Uint8Array||s.data instanceof ArrayBuffer||typeof Buffer<"u"&&s.data instanceof Buffer){let h;if(s.data instanceof Buffer)h=s.data;else if(s.data instanceof Uint8Array)h=Buffer.from(s.data);else if(s.data instanceof ArrayBuffer)h=Buffer.from(new Uint8Array(s.data));else continue;const v=h.toString("base64");u=`data:${s.mediaType||"application/octet-stream"};base64,${v}`,m=h.length}}let y="file";const b=h=>!!h&&typeof h=="object"&&"status"in h;b(n.fullFileUpload)&&n.fullFileUpload.status||b(c.fullFileUpload)&&c.fullFileUpload.status?y="file:full":n.isRAGFileUploadAllowed||c.isRAGFileUploadAllowed?y="file:rag":(n.isImageUploadAllowed||c.isImageUploadAllowed)&&s.mediaType?.startsWith("image/")&&(y="file"),o.debug(`[transformUploadsFromPrompt] Chosen uploadType for file '${s.filename??"file"}':`,y),n.fileUploadSizeAndTypes&&m!==void 0&&(n.fileUploadSizeAndTypes.some(h=>(!h.types||h.types.includes(s.mediaType))&&(!h.maxSize||m<=h.maxSize))||d.push(`[transformUploadsFromPrompt] File type or size not allowed by chatflow config: ${s.mediaType}, size: ${m}`)),u&&p.push({type:y,name:s.filename??"file",data:u,mime:s.mediaType||"application/octet-stream"})}}return p.some(t=>t.type==="file:full")&&!f&&(f=A()),{uploads:p,warnings:d,chatId:f}}class D{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??C(),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 o={...i?.headers||{}};return this.apiKey&&(o.Authorization=`Bearer ${this.apiKey}`),fetch(e,{...i,headers:o})}async getArgs(a){const i=a.providerOptions?.chatId,{prompt:e}=a,o=[],l=await P({chatflowId:this.chatflowId,fetchWithAuth:this.fetchWithAuth.bind(this),logger:this.logger}),{uploads:n,warnings:c,chatId:p}=await j({prompt:e,userChatId:i,getUploadConfig:async()=>l,chatflowId:this.chatflowId,logger:this.logger});let d=n,f=p;if(this.logger.debug(`[FlowiseChatModel] Uploads before attachment API: ${JSON.stringify(d.map(t=>({name:t.name,type:t.type,mime:t.mime,dataLength:t.data.length})))} `),d.some(t=>t.type==="file:full")&&f)for(let t=0;t<d.length;++t){const g=d[t];if(g&&g.type==="file:full"){const s=await B({file:{name:g.name,data:g.data,mime:g.mime},chatflowId:this.chatflowId,chatId:f,fetchWithAuth:this.fetchWithAuth.bind(this),logger:this.logger});s?d[t]={type:g.type,name:g.name,data:s,mime:g.mime}:o.push({type:"other",message:`[FlowiseChatModel] Failed to extract content for file: ${g.name}`})}}return this.logger.debug(`[FlowiseChatModel] Uploads after attachment API: ${JSON.stringify(d.map(t=>({name:t.name,type:t.type,mime:t.mime,dataLength:t.data.length})))} `),d.length>0&&this.logger.debug("[FlowiseChatModel] Extracted uploads:",d.map(t=>({name:t.name,type:t.type,mime:t.mime,dataLength:t.data.length}))),{args:{question:x(e),chatId:f,uploads:d.length>0?d:void 0},warnings:[...o,...c.map(t=>({type:"other",message:t}))]}}async doGenerate(a){const{args:i,warnings:e}=await this.getArgs(a);try{const o=await this.client.createPrediction({...i,chatflowId:this.chatflowId,streaming:!1});if(typeof o=="object"&&"text"in o)return o.text||(o.text="",e.push({message:"No response from Flowise",type:"other"})),{content:[{type:"text",text:o.text}],finishReason:"stop",usage:{inputTokens:w(i.question),outputTokens:w(o.text),totalTokens:w(i.question)+w(o.text)},warnings:e};throw new Error("Unexpected streaming response for non-streaming call")}catch(o){throw new Error(`Flowise API error: ${o instanceof Error?o.message:String(o)}`)}}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:M(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 F(r){const{logger:a,...i}=r,e=new $(i),o=n=>new D({client:e,chatflowId:n,logger:a,baseUrl:i.baseUrl,apiKey:i.apiKey});function l(n){if(new.target)throw new Error("The Flowise model function cannot be called with the new keyword.");return o(n)}return Object.assign(l,{languageModel:o,textEmbeddingModel:n=>{throw new Error(`Text embedding model '${n}' not supported by Flowise`)},imageModel:n=>{throw new Error(`Image model '${n}' not supported by Flowise`)},transcriptionModel:n=>{throw new Error(`Transcription model '${n}' not supported by Flowise`)},speechModel:n=>{throw new Error(`Speech model '${n}' not supported by Flowise`)},chat:o,client:e})}function W(r){const{chatflowId:a,...i}=r;return F(i)(a)}export{U as convertToAiSdkMessage,x as convertToFlowiseMessage,W as createFlowiseModel,F as createFlowiseProvider};