UNPKG

@catgirls/openrouter

Version:

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

23 lines (17 loc) • 15.2 kB
'use strict';var events=require('events'),S=require('axios');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var S__default=/*#__PURE__*/_interopDefault(S);var v=b=>b?typeof b=="string"?{open:b,close:b}:b:null;var A=class extends events.EventEmitter{constructor(a,e={}){super();this.messages=[];this.client=a;let r={thinking:"<thinking>",action:"<action>",observation:"<observation>",finalAnswer:"<answer>"},i=e.markers??r;this.normalizedMarkers={thinking:v(i.thinking),action:v(i.action),observation:v(i.observation),finalAnswer:v(i.finalAnswer)},this.options={maxIterations:e.maxIterations??5,model:e.model??"anthropic/claude-3.7-sonnet",prependDefaultPrompt:e.prependDefaultPrompt??false,systemPrompt:e.prependDefaultPrompt?this.getDefaultSystemPrompt(i)+` `+e.systemPrompt:e.systemPrompt??this.getDefaultSystemPrompt(i),tools:e.tools??[],verbose:e.verbose??false,markers:i,stopCondition:e.stopCondition??this.defaultStopCondition.bind(this)},this.messages.push({role:"system",content:this.options.systemPrompt});}defaultStopCondition(a){let e=a[a.length-1],r=this.normalizedMarkers.finalAnswer;return r?e.role==="assistant"&&typeof e.content=="string"&&e.content.includes(r.open):false}getDefaultSystemPrompt(a){let e=v(a.thinking),r=v(a.action),i=v(a.observation),p=v(a.finalAnswer);return `You are a helpful assistant that solves tasks step by step. ${e?`When you're analyzing a problem or thinking about it, wrap your thoughts in ${e.open} and ${e.close} tags. Example: ${e.open}I need to break this problem into parts: first calculate X, then Y${e.close}`:""} ${r?`When you decide to use a tool, wrap your reasoning in ${r.open} and ${r.close} tags. Example: ${r.open}I'll search for information about climate change${r.close}`:""} ${i?`After getting information, wrap your observations in ${i.open} and ${i.close} tags. Example: ${i.open}The search revealed that global temperatures have risen by 1.1\xB0C${i.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(a){this.messages.push({role:"user",content:a});let e=0,r=null;for(;e<this.options.maxIterations&&r===null;){e++,this.options.verbose&&this.emit("iteration",{iteration:e,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 s=this.messages[this.messages.length-1];if(typeof s.content=="string"){let n=this.normalizedMarkers.finalAnswer;if(n){let l=new RegExp(`${this.escapeRegExp(n.open)}(.*?)${this.escapeRegExp(n.close)}`,"s"),h=s.content.match(l);r=h?h[1].trim():s.content,this.emit("final_answer",r);}else r=s.content;}else r="RECEIVED_NON_STRING_FINAL_ANSWER";break}if(g.tool_calls&&g.tool_calls.length>0){for(let s of g.tool_calls)if(s.type==="function"){let{name:n,arguments:l}=s.function,h=l&&l.trim()?JSON.parse(l):{},y=this.options.tools.find(o=>o.name===n);if(y)try{this.emit("tool_call",{name:n,args:h});let o=await y.execute(h);this.messages.push({role:"tool",tool_call_id:s.id,content:JSON.stringify(o)}),this.emit("tool_result",{tool:n,args:h,result:o});}catch(o){this.messages.push({role:"tool",tool_call_id:s.id,content:JSON.stringify({error:String(o)})});let m={};try{l&&l.trim()&&(m=JSON.parse(l));}catch{}this.emit("tool_error",{tool:n,args:m,error:o});}}}}return r===null&&(r="MAX_ITERATIONS_REACHED_WITHOUT_FINAL_ANSWER"),this.messages[this.messages.length-1]?.role==="tool"&&r==="MAX_ITERATIONS_REACHED_WITHOUT_FINAL_ANSWER"&&(r="ANSWER_AFTER_TOOL_NOT_GIVEN"),this.emit("complete"),r}escapeRegExp(a){return a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async runStream(a){let e=new events.EventEmitter;this.messages.push({role:"user",content:a});let r=0,i=false,p=false,k=new Set,g=async()=>{if(r>=this.options.maxIterations||i){if(r>=this.options.maxIterations&&!i){let s=this.normalizedMarkers.finalAnswer,n=false;s&&this.messages.length>0&&typeof this.messages[this.messages.length-1].content=="string"&&(n=this.messages[this.messages.length-1].content.includes(s.open)),n||e.emit("final_answer","MAX_ITERATIONS_REACHED_WITHOUT_FINAL_ANSWER");}e.emit("complete");return}r++,this.options.verbose&&e.emit("iteration",{iteration:r,messages:this.messages});try{let s=await this.client.chatCompletion({messages:this.messages,model:this.options.model,tools:this.formatTools(),stream:!0}),n="",l=[],h=null,y="";s.on("content",o=>{n+=o,e.emit("token",o);for(let[m,u]of Object.entries(this.normalizedMarkers)){if(!u)continue;let{open:c,close:d}=u;if(n.includes(c)&&!h){let f=n.lastIndexOf(c);f!==-1&&f+c.length<=n.length&&(h=m,y="",e.emit("marker",{type:m,content:""}));}if(h===m&&n.includes(d)){let f=n.indexOf(c),q=n.lastIndexOf(d);f!==-1&&q>f&&(y=n.substring(f+c.length,q),e.emit("marker",{type:m,content:y}),h=null,y="");}}}),s.on("tool_calls",o=>{l=l.concat(o),e.emit("stream_tool_calls",o);}),s.on("error",o=>{if(e.emit("error",o),!i){let m=this.normalizedMarkers.finalAnswer,u=!1;m&&this.messages.length>0&&typeof this.messages[this.messages.length-1].content=="string"&&(u=this.messages[this.messages.length-1].content.includes(m.open)),u||e.emit("final_answer","Encountered an error while processing the request."),e.emit("complete");}i=!0;}),s.on("done",async()=>{let o={role:"assistant",content:n};if(l.length>0&&(o.tool_calls=l),this.messages.push(o),e.emit("response",o),this.options.stopCondition(this.messages)){i=!0;let m=this.normalizedMarkers.finalAnswer;if(m&&typeof o.content=="string"){let u=new RegExp(`${this.escapeRegExp(m.open)}(.*?)${this.escapeRegExp(m.close)}`,"s"),c=o.content.match(u),d=c?c[1].trim():o.content;e.emit("final_answer",d);}e.emit("complete");return}if(l.length>0){for(let m of l)if(m.type==="function"){let{id:u,function:{name:c,arguments:d}}=m;if(k.has(u))continue;k.add(u);try{let f=d&&d.trim()?JSON.parse(d):{},q=this.options.tools.find(E=>E.name===c);if(q){e.emit("tool_call",{name:c,args:f});let E=await q.execute(f);this.messages.push({role:"tool",tool_call_id:u,content:JSON.stringify(E)}),e.emit("tool_result",{tool:c,args:f,result:E});}}catch(f){this.messages.push({role:"tool",tool_call_id:u,content:JSON.stringify({error:String(f)})});let q={};try{d&&d.trim()&&(q=JSON.parse(d));}catch{}e.emit("tool_error",{tool:c,args:q,error:f}),p=!0;}}l=[],p=!0,setTimeout(g,0);}else if(!i&&p)p=!1,setTimeout(g,0);else if(!i){let m=!1,u=this.normalizedMarkers.finalAnswer;u&&this.messages.length>0&&typeof this.messages[this.messages.length-1].content=="string"&&(m=this.messages[this.messages.length-1].content.includes(u.open));let c=this.messages.some(d=>d.role==="tool");!m&&!i&&(c?e.emit("final_answer","ANSWER_AFTER_TOOL_NOT_GIVEN"):e.emit("final_answer","MAX_ITERATIONS_REACHED_WITHOUT_FINAL_ANSWER")),i=!0,e.emit("complete");}});}catch(s){e.emit("error",s),i=true;}};return g(),e}formatTools(){return this.options.tools.map(a=>({type:"function",function:{name:a.name,description:a.description,parameters:a.parameters}}))}};var w=class{static handleError(t){throw this.isAxiosError(t)?this.handleAxiosError(t):new Error(`Unexpected error: ${t instanceof Error?t.message:String(t)}`)}static isAxiosError(t){return t instanceof Error&&"isAxiosError"in t}static handleAxiosError(t){if(t.response){let{status:a,data:e}=t.response;return this.isOpenRouterError(e)?this.createDetailedError(e.error):new Error(`HTTP ${a}: ${this.ERROR_MESSAGES[a]||"Unknown error"}`)}return t.request?new Error("No response received from OpenRouter API"):new Error(`Failed to make request: ${t.message}`)}static isOpenRouterError(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error.code=="number"&&typeof t.error.message=="string"}static createDetailedError({code:t,message:a,metadata:e}){let r=`${this.ERROR_MESSAGES[t]||"Unknown error"}: ${a}`;return e?this.isModerationError(e)?new Error(`${r} Flagged by ${e.provider_name} for: ${e.reasons.join(", ")} Flagged content: "${e.flagged_input}"`):this.isProviderError(e)?new Error(`${r} Provider ${e.provider_name} error: ${JSON.stringify(e.raw)}`):new Error(r):new Error(r)}static isModerationError(t){return typeof t=="object"&&t!==null&&"reasons"in t&&"flagged_input"in t&&"provider_name"in t&&"model_slug"in t}static isProviderError(t){return typeof t=="object"&&t!==null&&"provider_name"in t&&"raw"in t}};w.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"};var x=class{constructor(t,a){this.client=S__default.default.create({baseURL:t,headers:a});}async post(t,a,e){return this.client.post(t,a,e)}async get(t){return this.client.get(t)}};var R=class{handleStream(t){let a=new events.EventEmitter,e="";return t.on("data",r=>{e+=r.toString();let i=e.split(` `);e=i.pop()||"";for(let p of i){if(p.trim()===""||p.startsWith(":")){p.includes("OPENROUTER PROCESSING")&&a.emit("processing");continue}try{let k=p.replace(/^data: /,"");if(k==="[DONE]"){a.emit("done");continue}let g=JSON.parse(k);if(a.emit("chunk",g),g.choices?.[0]){let s=g.choices[0];if(s.delta){let{content:n,role:l,tool_calls:h}=s.delta;n&&(a.emit("token",n),a.emit("content",n)),l&&a.emit("role",l),h&&a.emit("tool_calls",h);}s.finish_reason&&a.emit("finish",s.finish_reason);}g.usage&&a.emit("usage",g.usage);}catch(k){a.emit("error",w.handleError(k));}}}),t.on("end",()=>a.emit("done")),t.on("error",r=>{a.emit("error",w.handleError(r));}),a}};var M=class{constructor(t,a={model:"anthropic/claude-3.7-sonnet"},e,r){this.apiKey=t;this.defaultConfig=a;this.baseURL="https://openrouter.ai/api/v1";this.httpClient=e||new x(this.baseURL,this.getDefaultHeaders()),this.streamHandler=r||new R;}getDefaultHeaders(){let t={Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"};return this.defaultConfig.siteUrl&&(t["HTTP-Referer"]=this.defaultConfig.siteUrl),this.defaultConfig.siteName&&(t["X-Title"]=this.defaultConfig.siteName),t}async chatCompletion(t){try{let a={...t,model:t.model||this.defaultConfig.model,stream:t.stream};if(t.stream){let r=await this.httpClient.post("/chat/completions",a,{responseType:"stream",signal:t.signal});return this.streamHandler.handleStream(r.data)}return (await this.httpClient.post("/chat/completions",a)).data}catch(a){throw w.handleError(a)}}async getGenerationStats(t){try{return (await this.httpClient.get(`/generation?id=${t}`)).data.data}catch(a){throw w.handleError(a)}}async getModels(){try{return (await this.httpClient.get("/models")).data.data}catch(t){throw w.handleError(t)}}};var J=["anthropic/claude-opus-4","anthropic/claude-sonnet-4","mistralai/devstral-small","google/gemini-2.5-flash-preview-05-20","google/gemini-2.5-flash-preview-05-20:thinking","openai/codex-mini","meta-llama/llama-3.3-8b-instruct:free","mistralai/mistral-medium-3","google/gemini-2.5-pro-preview","arcee-ai/caller-large","qwen/qwen3-30b-a3b","qwen/qwen3-14b","qwen/qwen3-32b","qwen/qwen3-235b-a22b","google/gemini-2.5-flash-preview","google/gemini-2.5-flash-preview:thinking","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","all-hands/openhands-lm-32b-v0.1","google/gemini-2.5-pro-exp-03-25","deepseek/deepseek-chat-v3-0324","mistralai/mistral-small-3.1-24b-instruct:free","mistralai/mistral-small-3.1-24b-instruct","ai21/jamba-1.6-large","ai21/jamba-1.6-mini","qwen/qwq-32b","openai/gpt-4.5-preview","google/gemini-2.0-flash-lite-001","anthropic/claude-3.7-sonnet","anthropic/claude-3.7-sonnet:thinking","anthropic/claude-3.7-sonnet:beta","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","x-ai/grok-2-1212","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","anthropic/claude-3.5-haiku:beta","anthropic/claude-3.5-haiku","anthropic/claude-3.5-haiku-20241022:beta","anthropic/claude-3.5-haiku-20241022","anthropic/claude-3.5-sonnet:beta","anthropic/claude-3.5-sonnet","x-ai/grok-beta","mistralai/ministral-8b","mistralai/ministral-3b","nvidia/llama-3.1-nemotron-70b-instruct","google/gemini-flash-1.5-8b","meta-llama/llama-3.2-3b-instruct","meta-llama/llama-3.2-11b-vision-instruct","qwen/qwen-2.5-72b-instruct","mistralai/pixtral-12b","cohere/command-r-plus-08-2024","cohere/command-r-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-8b-instruct","meta-llama/llama-3.1-405b-instruct","meta-llama/llama-3.1-70b-instruct","mistralai/codestral-mamba","mistralai/mistral-nemo","openai/gpt-4o-mini","openai/gpt-4o-mini-2024-07-18","anthropic/claude-3.5-sonnet-20240620:beta","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","google/gemini-flash-1.5","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","google/gemini-pro-1.5","openai/gpt-4-turbo","cohere/command-r-plus","cohere/command-r-plus-04-2024","cohere/command-r","anthropic/claude-3-haiku:beta","anthropic/claude-3-haiku","anthropic/claude-3-opus:beta","anthropic/claude-3-opus","anthropic/claude-3-sonnet:beta","anthropic/claude-3-sonnet","cohere/command-r-03-2024","mistralai/mistral-large","openai/gpt-3.5-turbo-0613","openai/gpt-4-turbo-preview","mistralai/mistral-medium","mistralai/mistral-small","mistralai/mistral-tiny","mistralai/mixtral-8x7b-instruct","openai/gpt-3.5-turbo-1106","openai/gpt-4-1106-preview","mistralai/mistral-7b-instruct-v0.1","openai/gpt-3.5-turbo-16k","openai/gpt-4-32k","openai/gpt-4-32k-0314","openai/gpt-3.5-turbo","openai/gpt-3.5-turbo-0125","openai/gpt-4","openai/gpt-4-0314"]; exports.Agent=A;exports.HttpClient=x;exports.OpenRouterClient=M;exports.OpenRouterErrorAdapter=w;exports.StreamHandler=R;exports.toolCallingModels=J;//# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map