UNPKG

@catgirls/openrouter

Version:

Nyaa~! A TypeScript client for OpenRouter that's both kawaii and powerful! 😻

23 lines (17 loc) • 16.3 kB
import {EventEmitter}from'events';import M from'axios';var w=f=>f?typeof f=="string"?{open:f,close:f}:f:null;var _=class extends EventEmitter{client;options;messages=[];normalizedMarkers;constructor(e,t={}){super(),this.client=e;let i={thinking:"<thinking>",action:"<action>",observation:"<observation>",finalAnswer:"<answer>"},a=t.markers??i;this.normalizedMarkers={thinking:w(a.thinking),action:w(a.action),observation:w(a.observation),finalAnswer:w(a.finalAnswer)},this.options={maxIterations:t.maxIterations??5,model:t.model??"anthropic/claude-3.7-sonnet",prependDefaultPrompt:t.prependDefaultPrompt??false,systemPrompt:t.prependDefaultPrompt?this.getDefaultSystemPrompt(a)+` `+t.systemPrompt:t.systemPrompt??this.getDefaultSystemPrompt(a),tools:t.tools??[],verbose:t.verbose??false,markers:a,stopCondition:t.stopCondition??this.defaultStopCondition.bind(this)},this.messages.push({role:"system",content:this.options.systemPrompt});}defaultStopCondition(e){let t=e[e.length-1],i=this.normalizedMarkers.finalAnswer;return i?t.role==="assistant"&&typeof t.content=="string"&&t.content.includes(i.open):false}getDefaultSystemPrompt(e){let t=w(e.thinking),i=w(e.action),a=w(e.observation),p=w(e.finalAnswer);return `You are a helpful assistant that solves tasks step by step. ${t?`When you're analyzing a problem or thinking about it, wrap your thoughts in ${t.open} and ${t.close} tags. Example: ${t.open}I need to break this problem into parts: first calculate X, then Y${t.close}`:""} ${i?`When you decide to use a tool, wrap your reasoning in ${i.open} and ${i.close} tags. Example: ${i.open}I'll search for information about climate change${i.close}`:""} ${a?`After getting information, wrap your observations in ${a.open} and ${a.close} tags. Example: ${a.open}The search revealed that global temperatures have risen by 1.1\xB0C${a.close}`:""} ${p?`When you have a final answer, wrap it in ${p.open} and ${p.close} tags. Example: ${p.open}Based on my analysis, the answer is 42${p.close}`:""} Use ONLY these markers for structuring your responses. Only use ${p?.open} when you're completely done with the task.`}async run(e){this.messages.push({role:"user",content:e});let t=0,i=null;for(;t<this.options.maxIterations&&i===null;){t++,this.options.verbose&&this.emit("iteration",{iteration:t,messages:this.messages});let g=(await this.client.chatCompletion({messages:this.messages,model:this.options.model,tools:this.formatTools()})).choices[0].message;if(this.messages.push(g),this.emit("response",g),this.options.stopCondition(this.messages)){let n=this.messages[this.messages.length-1];if(typeof n.content=="string"){let r=this.normalizedMarkers.finalAnswer;if(r){let o=new RegExp(`${this.escapeRegExp(r.open)}(.*?)${this.escapeRegExp(r.close)}`,"s"),u=n.content.match(o);i=u?u[1].trim():n.content,this.emit("final_answer",i);}else i=n.content;}else i="RECEIVED_NON_STRING_FINAL_ANSWER";break}if(g.tool_calls&&g.tool_calls.length>0){for(let n of g.tool_calls)if(n.type==="function"){let{name:r,arguments:o}=n.function,u=o&&o.trim()?JSON.parse(o):{},b=this.options.tools.find(s=>s.name===r);if(b)try{this.emit("tool_call",{name:r,args:u});let s=await b.execute(u);this.messages.push({role:"tool",tool_call_id:n.id,content:JSON.stringify(s)}),this.emit("tool_result",{tool:r,args:u,result:s});}catch(s){this.messages.push({role:"tool",tool_call_id:n.id,content:JSON.stringify({error:String(s)})});let l={};try{o&&o.trim()&&(l=JSON.parse(o));}catch{}this.emit("tool_error",{tool:r,args:l,error:s});}}}}return i===null&&(i="MAX_ITERATIONS_REACHED_WITHOUT_FINAL_ANSWER"),this.messages[this.messages.length-1]?.role==="tool"&&i==="MAX_ITERATIONS_REACHED_WITHOUT_FINAL_ANSWER"&&(i="ANSWER_AFTER_TOOL_NOT_GIVEN"),this.emit("complete"),i}escapeRegExp(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async runStream(e){let t=new EventEmitter;this.messages.push({role:"user",content:e});let i=0,a=false,p=false,k=new Set,g=async()=>{if(i>=this.options.maxIterations||a){if(i>=this.options.maxIterations&&!a){let n=this.normalizedMarkers.finalAnswer,r=false;n&&this.messages.length>0&&typeof this.messages[this.messages.length-1].content=="string"&&(r=this.messages[this.messages.length-1].content.includes(n.open)),r||t.emit("final_answer","MAX_ITERATIONS_REACHED_WITHOUT_FINAL_ANSWER");}t.emit("complete");return}i++,this.options.verbose&&t.emit("iteration",{iteration:i,messages:this.messages});try{let n=await this.client.chatCompletion({messages:this.messages,model:this.options.model,tools:this.formatTools(),stream:!0}),r="",o=[],u=null,b="";n.on("content",s=>{r+=s,t.emit("token",s);for(let[l,c]of Object.entries(this.normalizedMarkers)){if(!c)continue;let{open:m,close:d}=c;if(r.includes(m)&&!u){let h=r.lastIndexOf(m);h!==-1&&h+m.length<=r.length&&(u=l,b="",t.emit("marker",{type:l,content:""}));}if(u===l&&r.includes(d)){let h=r.indexOf(m),q=r.lastIndexOf(d);h!==-1&&q>h&&(b=r.substring(h+m.length,q),t.emit("marker",{type:l,content:b}),u=null,b="");}}}),n.on("tool_calls",s=>{o=o.concat(s),t.emit("stream_tool_calls",s);}),n.on("error",s=>{if(t.emit("error",s),!a){let l=this.normalizedMarkers.finalAnswer,c=!1;l&&this.messages.length>0&&typeof this.messages[this.messages.length-1].content=="string"&&(c=this.messages[this.messages.length-1].content.includes(l.open)),c||t.emit("final_answer","Encountered an error while processing the request."),t.emit("complete");}a=!0;}),n.on("done",async()=>{let s={role:"assistant",content:r};if(o.length>0&&(s.tool_calls=o),this.messages.push(s),t.emit("response",s),this.options.stopCondition(this.messages)){a=!0;let l=this.normalizedMarkers.finalAnswer;if(l&&typeof s.content=="string"){let c=new RegExp(`${this.escapeRegExp(l.open)}(.*?)${this.escapeRegExp(l.close)}`,"s"),m=s.content.match(c),d=m?m[1].trim():s.content;t.emit("final_answer",d);}t.emit("complete");return}if(o.length>0){for(let l of o)if(l.type==="function"){let{id:c,function:{name:m,arguments:d}}=l;if(k.has(c))continue;k.add(c);try{let h=d&&d.trim()?JSON.parse(d):{},q=this.options.tools.find(y=>y.name===m);if(q){t.emit("tool_call",{name:m,args:h});let y=await q.execute(h);this.messages.push({role:"tool",tool_call_id:c,content:JSON.stringify(y)}),t.emit("tool_result",{tool:m,args:h,result:y});}}catch(h){this.messages.push({role:"tool",tool_call_id:c,content:JSON.stringify({error:String(h)})});let q={};try{d&&d.trim()&&(q=JSON.parse(d));}catch{}t.emit("tool_error",{tool:m,args:q,error:h}),p=!0;}}o=[],p=!0,setTimeout(g,0);}else if(!a&&p)p=!1,setTimeout(g,0);else if(!a){let l=!1,c=this.normalizedMarkers.finalAnswer;c&&this.messages.length>0&&typeof this.messages[this.messages.length-1].content=="string"&&(l=this.messages[this.messages.length-1].content.includes(c.open));let m=this.messages.some(d=>d.role==="tool");!l&&!a&&(m?t.emit("final_answer","ANSWER_AFTER_TOOL_NOT_GIVEN"):t.emit("final_answer","MAX_ITERATIONS_REACHED_WITHOUT_FINAL_ANSWER")),a=!0,t.emit("complete");}});}catch(n){t.emit("error",n),a=true;}};return g(),t}formatTools(){return this.options.tools.map(e=>({type:"function",function:{name:e.name,description:e.description,parameters:e.parameters}}))}};var v=class{static ERROR_MESSAGES={400:"Bad Request: Invalid or missing parameters, or CORS issue",401:"Invalid credentials: OAuth session expired or disabled/invalid API key",402:"Insufficient credits: Add more credits and retry the request",403:"Content moderation: Your input was flagged",408:"Request timeout: Your request took too long to process",429:"Rate limited: You are sending too many requests",502:"Provider error: Model is down or invalid response received",503:"No provider: No available model provider meets your routing requirements"};static handleError(e){throw this.isAxiosError(e)?this.handleAxiosError(e):new Error(`Unexpected error: ${e instanceof Error?e.message:String(e)}`)}static isAxiosError(e){return e instanceof Error&&"isAxiosError"in e}static handleAxiosError(e){if(e.response){let{status:t,data:i}=e.response;return this.isOpenRouterError(i)?this.createDetailedError(i.error):new Error(`HTTP ${t}: ${this.ERROR_MESSAGES[t]||"Unknown error"}`)}return e.request?new Error("No response received from OpenRouter API"):new Error(`Failed to make request: ${e.message}`)}static isOpenRouterError(e){return typeof e=="object"&&e!==null&&"error"in e&&typeof e.error.code=="number"&&typeof e.error.message=="string"}static createDetailedError({code:e,message:t,metadata:i}){let a=`${this.ERROR_MESSAGES[e]||"Unknown error"}: ${t}`;return i?this.isModerationError(i)?new Error(`${a} Flagged by ${i.provider_name} for: ${i.reasons.join(", ")} Flagged content: "${i.flagged_input}"`):this.isProviderError(i)?new Error(`${a} Provider ${i.provider_name} error: ${JSON.stringify(i.raw)}`):new Error(a):new Error(a)}static isModerationError(e){return typeof e=="object"&&e!==null&&"reasons"in e&&"flagged_input"in e&&"provider_name"in e&&"model_slug"in e}static isProviderError(e){return typeof e=="object"&&e!==null&&"provider_name"in e&&"raw"in e}};var x=class{client;constructor(e,t){this.client=M.create({baseURL:e,headers:t});}async post(e,t,i){return this.client.post(e,t,i)}async get(e){return this.client.get(e)}};var E=class{handleStream(e){let t=new EventEmitter,i="";return e.on("data",a=>{i+=a.toString();let p=i.split(` `);i=p.pop()||"";for(let k of p){if(k.trim()===""||k.startsWith(":")){k.includes("OPENROUTER PROCESSING")&&t.emit("processing");continue}try{let g=k.replace(/^data: /,"");if(g==="[DONE]"){t.emit("done");continue}let n=JSON.parse(g);if(t.emit("chunk",n),n.choices?.[0]){let r=n.choices[0];if(r.delta){let{content:o,role:u,tool_calls:b}=r.delta;o&&(t.emit("token",o),t.emit("content",o)),u&&t.emit("role",u),b&&t.emit("tool_calls",b);}r.finish_reason&&t.emit("finish",r.finish_reason);}n.usage&&t.emit("usage",n.usage);}catch(g){t.emit("error",v.handleError(g));}}}),e.on("end",()=>t.emit("done")),e.on("error",a=>{t.emit("error",v.handleError(a));}),t}};var A=class{constructor(e,t={model:"anthropic/claude-3.7-sonnet"},i,a){this.apiKey=e;this.defaultConfig=t;this.httpClient=i||new x(this.baseURL,this.getDefaultHeaders()),this.streamHandler=a||new E;}baseURL="https://openrouter.ai/api/v1";httpClient;streamHandler;getDefaultHeaders(){let e={Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"};return this.defaultConfig.siteUrl&&(e["HTTP-Referer"]=this.defaultConfig.siteUrl),this.defaultConfig.siteName&&(e["X-Title"]=this.defaultConfig.siteName),e}async chatCompletion(e){try{let t={...e,model:e.model||this.defaultConfig.model,stream:e.stream};if(e.stream){let a=await this.httpClient.post("/chat/completions",t,{responseType:"stream",signal:e.signal});return this.streamHandler.handleStream(a.data)}return (await this.httpClient.post("/chat/completions",t)).data}catch(t){throw v.handleError(t)}}async getGenerationStats(e){try{return (await this.httpClient.get(`/generation?id=${e}`)).data.data}catch(t){throw v.handleError(t)}}async getModels(){try{return (await this.httpClient.get("/models")).data.data}catch(e){throw v.handleError(e)}}};var j=["anthropic/claude-sonnet-4.5","deepseek/deepseek-v3.2-exp","google/gemini-2.5-flash-preview-09-2025","google/gemini-2.5-flash-lite-preview-09-2025","qwen/qwen3-vl-235b-a22b-thinking","qwen/qwen3-vl-235b-a22b-instruct","qwen/qwen3-max","qwen/qwen3-coder-plus","openai/gpt-5-codex","deepseek/deepseek-v3.1-terminus","x-ai/grok-4-fast:free","x-ai/grok-4-fast","alibaba/tongyi-deepresearch-30b-a3b","qwen/qwen3-coder-flash","qwen/qwen3-next-80b-a3b-thinking","qwen/qwen3-next-80b-a3b-instruct","meituan/longcat-flash-chat","qwen/qwen-plus-2025-07-28","qwen/qwen-plus-2025-07-28:thinking","nvidia/nemotron-nano-9b-v2:free","nvidia/nemotron-nano-9b-v2","moonshotai/kimi-k2-0905","deepcogito/cogito-v2-preview-llama-109b-moe","stepfun-ai/step3","qwen/qwen3-30b-a3b-thinking-2507","x-ai/grok-code-fast-1","nousresearch/hermes-4-70b","nousresearch/hermes-4-405b","deepseek/deepseek-chat-v3.1:free","deepseek/deepseek-chat-v3.1","openai/gpt-4o-audio-preview","mistralai/mistral-medium-3.1","z-ai/glm-4.5v","ai21/jamba-mini-1.7","ai21/jamba-large-1.7","openai/gpt-5","openai/gpt-5-mini","openai/gpt-5-nano","openai/gpt-oss-120b","openai/gpt-oss-20b","anthropic/claude-opus-4.1","mistralai/codestral-2508","qwen/qwen3-coder-30b-a3b-instruct","qwen/qwen3-30b-a3b-instruct-2507","z-ai/glm-4.5","z-ai/glm-4.5-air:free","z-ai/glm-4.5-air","qwen/qwen3-235b-a22b-thinking-2507","z-ai/glm-4-32b","qwen/qwen3-coder:free","qwen/qwen3-coder","google/gemini-2.5-flash-lite","qwen/qwen3-235b-a22b-2507","moonshotai/kimi-k2","mistralai/devstral-medium","mistralai/devstral-small","x-ai/grok-4","inception/mercury","mistralai/mistral-small-3.2-24b-instruct:free","mistralai/mistral-small-3.2-24b-instruct","minimax/minimax-m1","google/gemini-2.5-flash-lite-preview-06-17","google/gemini-2.5-flash","google/gemini-2.5-pro","openai/o3-pro","x-ai/grok-3-mini","x-ai/grok-3","mistralai/magistral-small-2506","mistralai/magistral-medium-2506","mistralai/magistral-medium-2506:thinking","google/gemini-2.5-pro-preview","deepseek/deepseek-r1-0528","anthropic/claude-opus-4","anthropic/claude-sonnet-4","mistralai/devstral-small-2505:free","mistralai/devstral-small-2505","openai/codex-mini","meta-llama/llama-3.3-8b-instruct:free","mistralai/mistral-medium-3","google/gemini-2.5-pro-preview-05-06","arcee-ai/virtuoso-large","inception/mercury-coder","qwen/qwen3-4b:free","qwen/qwen3-30b-a3b","qwen/qwen3-14b","qwen/qwen3-32b","qwen/qwen3-235b-a22b:free","qwen/qwen3-235b-a22b","openai/o4-mini-high","openai/o3","openai/o4-mini","openai/gpt-4.1","openai/gpt-4.1-mini","openai/gpt-4.1-nano","x-ai/grok-3-mini-beta","x-ai/grok-3-beta","meta-llama/llama-4-maverick:free","meta-llama/llama-4-maverick","meta-llama/llama-4-scout:free","meta-llama/llama-4-scout","deepseek/deepseek-chat-v3-0324:free","deepseek/deepseek-chat-v3-0324","mistralai/mistral-small-3.1-24b-instruct:free","mistralai/mistral-small-3.1-24b-instruct","microsoft/phi-4-multimodal-instruct","qwen/qwq-32b","google/gemini-2.0-flash-lite-001","anthropic/claude-3.7-sonnet","anthropic/claude-3.7-sonnet:thinking","mistralai/mistral-saba","openai/o3-mini-high","google/gemini-2.0-flash-001","qwen/qwen-turbo","qwen/qwen-plus","qwen/qwen-max","openai/o3-mini","mistralai/mistral-small-24b-instruct-2501","deepseek/deepseek-r1-distill-llama-70b","deepseek/deepseek-r1","mistralai/codestral-2501","deepseek/deepseek-chat","openai/o1","google/gemini-2.0-flash-exp:free","meta-llama/llama-3.3-70b-instruct:free","meta-llama/llama-3.3-70b-instruct","amazon/nova-lite-v1","amazon/nova-micro-v1","amazon/nova-pro-v1","openai/gpt-4o-2024-11-20","mistralai/mistral-large-2411","mistralai/mistral-large-2407","mistralai/pixtral-large-2411","thedrummer/unslopnemo-12b","anthropic/claude-3.5-haiku-20241022","anthropic/claude-3.5-haiku","anthropic/claude-3.5-sonnet","mistralai/ministral-8b","nvidia/llama-3.1-nemotron-70b-instruct","thedrummer/rocinante-12b","meta-llama/llama-3.2-3b-instruct","qwen/qwen-2.5-72b-instruct","mistralai/pixtral-12b","cohere/command-r-08-2024","cohere/command-r-plus-08-2024","microsoft/phi-3.5-mini-128k-instruct","nousresearch/hermes-3-llama-3.1-70b","openai/gpt-4o-2024-08-06","meta-llama/llama-3.1-405b-instruct","meta-llama/llama-3.1-70b-instruct","meta-llama/llama-3.1-8b-instruct","mistralai/mistral-nemo","openai/gpt-4o-mini-2024-07-18","openai/gpt-4o-mini","anthropic/claude-3.5-sonnet-20240620","mistralai/mistral-7b-instruct:free","mistralai/mistral-7b-instruct","mistralai/mistral-7b-instruct-v0.3","microsoft/phi-3-mini-128k-instruct","microsoft/phi-3-medium-128k-instruct","openai/gpt-4o","openai/gpt-4o:extended","openai/gpt-4o-2024-05-13","meta-llama/llama-3-8b-instruct","meta-llama/llama-3-70b-instruct","mistralai/mixtral-8x22b-instruct","openai/gpt-4-turbo","anthropic/claude-3-haiku","anthropic/claude-3-opus","mistralai/mistral-large","openai/gpt-4-turbo-preview","openai/gpt-3.5-turbo-0613","mistralai/mistral-small","mistralai/mistral-tiny","mistralai/mixtral-8x7b-instruct","openai/gpt-4-1106-preview","mistralai/mistral-7b-instruct-v0.1","openai/gpt-3.5-turbo-16k","openai/gpt-4","openai/gpt-3.5-turbo","openai/gpt-4-0314"]; export{_ as Agent,x as HttpClient,A as OpenRouterClient,v as OpenRouterErrorAdapter,E as StreamHandler,j as toolCallingModels};//# sourceMappingURL=index.mjs.map //# sourceMappingURL=index.mjs.map