@lunora/ai 1.0.0-alpha.124 → 1.0.0-alpha.126

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/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{default as _}from"./packem_shared/createAi-BfFVXt43.mjs";import{AI_DEFAULT_EMBEDDING_MODEL_ENV as t,AI_DEFAULT_MODEL_ENV as a,AI_GATEWAY_ACCOUNT_ID_ENV as o,AI_GATEWAY_ID_ENV as r,AI_GATEWAY_METADATA_MAX_KEYS as T,AI_GATEWAY_TAGS_ENV as I,AI_GATEWAY_TOKEN_ENV as N,AI_PROXY_TOKEN_ENV as l,AI_PROXY_URL_ENV as m,buildAiGatewayMetadataFields as s,readAiGatewayEnvTags as D,resolveAiGateway as G}from"./packem_shared/AI_DEFAULT_EMBEDDING_MODEL_ENV-IwcHZiKj.mjs";import{DEFAULT_MODEL_PRICES as O,estimateModelCost as d,lookupModelPrice as i}from"./packem_shared/DEFAULT_MODEL_PRICES-CIJLs1Ah.mjs";import{embed as Y,embedMany as x,generateObject as L,generateText as c,hasToolCall as f,jsonSchema as p,streamObject as W,streamText as b,tool as n}from"ai";import{createWorkersAI as U}from"workers-ai-provider";export{t as AI_DEFAULT_EMBEDDING_MODEL_ENV,a as AI_DEFAULT_MODEL_ENV,o as AI_GATEWAY_ACCOUNT_ID_ENV,r as AI_GATEWAY_ID_ENV,T as AI_GATEWAY_METADATA_MAX_KEYS,I as AI_GATEWAY_TAGS_ENV,N as AI_GATEWAY_TOKEN_ENV,l as AI_PROXY_TOKEN_ENV,m as AI_PROXY_URL_ENV,O as DEFAULT_MODEL_PRICES,s as buildAiGatewayMetadataFields,_ as createAi,U as createWorkersAI,Y as embed,x as embedMany,d as estimateModelCost,L as generateObject,c as generateText,f as hasToolCall,p as jsonSchema,i as lookupModelPrice,D as readAiGatewayEnvTags,G as resolveAiGateway,W as streamObject,b as streamText,n as tool};
1
+ import{default as _}from"./packem_shared/createAi-DDJthsZF.mjs";import{AI_DEFAULT_EMBEDDING_MODEL_ENV as t,AI_DEFAULT_MODEL_ENV as a,AI_GATEWAY_ACCOUNT_ID_ENV as o,AI_GATEWAY_ID_ENV as r,AI_GATEWAY_METADATA_MAX_KEYS as T,AI_GATEWAY_TAGS_ENV as I,AI_GATEWAY_TOKEN_ENV as N,AI_PROXY_TOKEN_ENV as l,AI_PROXY_URL_ENV as m,buildAiGatewayMetadataFields as s,readAiGatewayEnvTags as D,resolveAiGateway as G}from"./packem_shared/AI_DEFAULT_EMBEDDING_MODEL_ENV-IwcHZiKj.mjs";import{DEFAULT_MODEL_PRICES as O,estimateModelCost as d,lookupModelPrice as i}from"./packem_shared/DEFAULT_MODEL_PRICES-CIJLs1Ah.mjs";import{embed as Y,embedMany as x,generateObject as L,generateText as c,hasToolCall as f,jsonSchema as p,streamObject as W,streamText as b,tool as n}from"ai";import{createWorkersAI as U}from"workers-ai-provider";export{t as AI_DEFAULT_EMBEDDING_MODEL_ENV,a as AI_DEFAULT_MODEL_ENV,o as AI_GATEWAY_ACCOUNT_ID_ENV,r as AI_GATEWAY_ID_ENV,T as AI_GATEWAY_METADATA_MAX_KEYS,I as AI_GATEWAY_TAGS_ENV,N as AI_GATEWAY_TOKEN_ENV,l as AI_PROXY_TOKEN_ENV,m as AI_PROXY_URL_ENV,O as DEFAULT_MODEL_PRICES,s as buildAiGatewayMetadataFields,_ as createAi,U as createWorkersAI,Y as embed,x as embedMany,d as estimateModelCost,L as generateObject,c as generateText,f as hasToolCall,p as jsonSchema,i as lookupModelPrice,D as readAiGatewayEnvTags,G as resolveAiGateway,W as streamObject,b as streamText,n as tool};
@@ -0,0 +1 @@
1
+ import{LunoraError as g}from"@lunora/errors";import{createWorkersAI as D}from"workers-ai-provider";import{buildAiGatewayMetadataFields as k,readEnv as y,AI_DEFAULT_MODEL_ENV as O,AI_DEFAULT_EMBEDDING_MODEL_ENV as P,AI_PROXY_URL_ENV as _,AI_PROXY_TOKEN_ENV as S,readAiGatewayEnvTags as H,AI_GATEWAY_ID_ENV as K,warnIgnoredBindingToken as Y}from"./AI_DEFAULT_EMBEDDING_MODEL_ENV-IwcHZiKj.mjs";import{wrapLanguageModel as q}from"ai";import{m as X,r as U}from"./usage-GPnTSqR8.mjs";import"./DEFAULT_MODEL_PRICES-CIJLs1Ah.mjs";const z={setAttribute:()=>{},setAttributes:()=>{}},G=async(e,t)=>t(G,z),Z=(e,{metrics:t,trace:a=G})=>{const r={"gen_ai.operation.name":"chat","gen_ai.request.model":e};return{wrapGenerate:async({doGenerate:n})=>a("ai.generate",async(h,u)=>{const i=await n();return U(e,i,u,t),i},r),wrapStream:async({doStream:n})=>{const h=(l,w)=>{const E=l.stream.getReader();let c={};return{...l,stream:new ReadableStream({cancel:async v=>{w({outcome:c}),await E.cancel(v)},pull:async v=>{let m;try{m=await E.read()}catch(b){w({error:b,failed:!0,outcome:c}),v.error(b);return}if(m.done){w({outcome:c}),v.close();return}m.value.type==="finish"&&(c={providerMetadata:m.value.providerMetadata,usage:m.value.usage}),v.enqueue(m.value)}})}},u=Promise.withResolvers(),i=Promise.withResolvers();return a("ai.stream",async(l,w)=>{u.resolve(h(await n(),i.resolve));const E=await i.promise;if(U(e,E.outcome,w,t),E.failed===!0)throw E.error},r).catch(u.reject),u.promise}}},x=(e,t,a)=>{let r=e;return t!==void 0&&typeof e!="string"&&(r=q({middleware:Z(a??X(e)??"unknown",t),model:e})),r};let T;const W=()=>(T??=Promise.all([import("@ai-sdk/openai"),import("workers-ai-provider/openai"),import("workers-ai-provider/anthropic")]).then(([e,{openai:t},{anthropic:a}])=>({createOpenAI:e.createOpenAI,plugins:[t,a]}),e=>{throw T=void 0,e}),T),$=(e,t,a)=>({doGenerate:async r=>a().then(n=>n.doGenerate(r)),doStream:async r=>a().then(n=>n.doStream(r)),modelId:e,provider:t,specificationVersion:"v4",get supportedUrls(){return a().then(r=>r.supportedUrls)}}),J=(e,t,a)=>({doEmbed:async r=>a().then(n=>n.doEmbed(r)),get maxEmbeddingsPerCall(){return a().then(r=>r.maxEmbeddingsPerCall)},modelId:e,provider:t,specificationVersion:"v4",get supportsParallelCalls(){return a().then(r=>r.supportsParallelCalls)}}),C=e=>!e.startsWith("@")&&e.includes("/"),I=e=>{throw new g("INTERNAL",`@lunora/ai: ${e} needs the \`AI\` binding (env.AI). In dev, Lunora leaves it out when there is no Cloudflare login: run \`wrangler login\` or set CLOUDFLARE_API_TOKEN. Elsewhere, add an \`ai\` binding to wrangler.jsonc. Or set ${_} to an OpenAI-compatible proxy for "<provider>/<model>" slugs.`)},B=()=>I("this model id");B.textEmbeddingModel=()=>I("this embedding model id");const Q=3040,ee=/^(?:[A-Z]\w*:\s*)?3040\s*:/iu,te=e=>e?.code===Q||e instanceof Error&&ee.test(e.message),re="default",ae=new Map([[401,"UNAUTHORIZED"],[403,"FORBIDDEN"],[404,"NOT_FOUND"],[408,"SERVICE_UNAVAILABLE"],[429,"RATE_LIMITED"]]),ne=e=>ae.get(e)??(e>=400&&e<500?"BAD_REQUEST":"SERVICE_UNAVAILABLE"),oe=300,ie=new Set(["127.0.0.1","[::1]","localhost"]),se=e=>{const t=y(e,_);if(t===void 0)return;const a=y(e,S),r=URL.canParse(t)?new URL(t):void 0;let n;if(r===void 0?n=`${_} is not a valid URL`:a!==void 0&&r.protocol!=="https:"&&!ie.has(r.hostname)&&(n=`${_} (${r.origin}) is not HTTPS, so ${S} would travel in cleartext — use an https:// URL`),n!==void 0){const i=()=>{throw new g("INTERNAL",`@lunora/ai: ${n}`)};return{chat:i,embedding:i}}let h;const u=()=>(h??=W().then(({createOpenAI:i})=>i({apiKey:a??"",baseURL:t,name:"lunora-proxy"})),h);return{chat:i=>$(i,"lunora-proxy",async()=>u().then(l=>l.chat(i))),embedding:i=>J(i,"lunora-proxy",async()=>u().then(l=>l.embedding(i)))}},de=(e,t)=>{const a=e===void 0?void 0:H(e);return a===void 0?t:{...t,tags:{...a,...t?.tags}}},ce=(e,t,a)=>{const r=k(a);if(e!==void 0)return r!==void 0&&e.metadata===void 0?{...e,metadata:r}:e;if(t===void 0)return;const n=y(t,K);if(n!==void 0)return Y(t),r===void 0?{id:n}:{id:n,metadata:r}},Ae=e=>{const{binding:t,defaultEmbeddingModel:a,defaultModel:r,env:n,gateway:h,metadata:u,provider:i,telemetry:l}=e,w=se(n),E=de(n,u),c=ce(h,n,E),v=h?.metadata===void 0?k(E):void 0,m=r??y(n,O),b=a??y(n,P),p=i??(t?D({binding:t,gateway:c}):B);let L;const M=()=>(L??=W().then(({plugins:o})=>D({binding:t,gateway:c,providers:o})),L);i===void 0&&t&&c!==void 0&&M().catch(()=>{});const V=(o,d)=>{if(!C(o))return d?.rejectIfBusy===void 0?p(o):p(o,{rejectIfBusy:d.rejectIfBusy});if(w!==void 0)return w.chat(o);const s=v===void 0?void 0:{metadata:v};return i!==void 0||!t?p(o,s):$(o,"workersai",async()=>M().then(A=>A(o,s)))},F=(o,d)=>{const s=o??m;if(s===void 0||s==="")throw new g("INTERNAL",`@lunora/ai: no model supplied and no default configured — pass a model id, or set ${O} in the Worker env (wrangler \`vars\` / \`.dev.vars\`)`);return typeof s=="string"?x(V(s,d),l,s):x(s,l)},j=o=>{if(w!==void 0&&C(o))return w.embedding(o);const d=p.textEmbeddingModel;if(typeof d!="function")throw new g("INTERNAL","@lunora/ai: the Workers AI provider does not expose `textEmbeddingModel`; pass an AI SDK EmbeddingModel (e.g. from @ai-sdk/openai) to embed()");return d.call(p,o)};return{embeddingModel:o=>{if(typeof o=="object")return o;const d=o??b;if(!d)throw new g("INTERNAL",`@lunora/ai: no embedding model supplied and no default configured — pass an embedding model id or an AI SDK EmbeddingModel, or set ${P} in the Worker env (wrangler \`vars\` / \`.dev.vars\`)`);return j(d)},model:F,run:async(o,d,s)=>{if(!t)return I("ai.run");const A=c!==void 0&&s?.gateway===void 0?{...s,gateway:c}:s;try{return await t.run(o,d,A)}catch(f){throw te(f)?new g("RATE_LIMITED",`@lunora/ai: Workers AI has no free capacity for ${o} (error 3040) — retry later`,{cause:f}):f}},websearch:async(o,d)=>{if(!t)return I("ai.websearch");if(typeof t.websearch!="function")throw new g("NOT_IMPLEMENTED","@lunora/ai: this Workers runtime's `AI` binding has no websearch() — update wrangler / @cloudflare/vite-plugin to a release that ships the Web Search API");const{gatewayId:s,...A}=d??{},f=await t.websearch({...A,gatewayId:s??c?.id??re,query:o});if(!f.ok){const R=(await f.text().catch(()=>"")).slice(0,oe);throw new g(ne(f.status),`@lunora/ai: web search failed with HTTP ${String(f.status)}${R===""?"":`: ${R}`}`)}const N=await f.json().catch(()=>{});if(!Array.isArray(N?.items))throw new g("INTERNAL","@lunora/ai: web search returned a body that is not JSON with an `items` array");return N},workersai:p}};export{Ae as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/ai",
3
- "version": "1.0.0-alpha.124",
3
+ "version": "1.0.0-alpha.126",
4
4
  "description": "Workers AI helper for Lunora: provider-agnostic AI SDK access from functions, Workers AI by default",
5
5
  "keywords": [
6
6
  "ai",
@@ -1 +0,0 @@
1
- import{createOpenAI as W}from"@ai-sdk/openai";import{LunoraError as E}from"@lunora/errors";import{createWorkersAI as $}from"workers-ai-provider";import{anthropic as B}from"workers-ai-provider/anthropic";import{openai as V}from"workers-ai-provider/openai";import{buildAiGatewayMetadataFields as U,readEnv as p,AI_DEFAULT_MODEL_ENV as N,AI_DEFAULT_EMBEDDING_MODEL_ENV as M,AI_PROXY_URL_ENV as _,AI_PROXY_TOKEN_ENV as R,readAiGatewayEnvTags as F,AI_GATEWAY_ID_ENV as j,warnIgnoredBindingToken as H}from"./AI_DEFAULT_EMBEDDING_MODEL_ENV-IwcHZiKj.mjs";import{wrapLanguageModel as Y}from"ai";import{m as K,r as D}from"./usage-GPnTSqR8.mjs";import"./DEFAULT_MODEL_PRICES-CIJLs1Ah.mjs";const q={setAttribute:()=>{},setAttributes:()=>{}},P=async(e,t)=>t(P,q),X=(e,{metrics:t,trace:n=P})=>{const o={"gen_ai.operation.name":"chat","gen_ai.request.model":e};return{wrapGenerate:async({doGenerate:a})=>n("ai.generate",async(f,A)=>{const m=await a();return D(e,m,A,t),m},o),wrapStream:async({doStream:a})=>{const f=(h,d)=>{const c=h.stream.getReader();let u={};return{...h,stream:new ReadableStream({cancel:async w=>{d({outcome:u}),await c.cancel(w)},pull:async w=>{let v;try{v=await c.read()}catch(y){d({error:y,failed:!0,outcome:u}),w.error(y);return}if(v.done){d({outcome:u}),w.close();return}v.value.type==="finish"&&(u={providerMetadata:v.value.providerMetadata,usage:v.value.usage}),w.enqueue(v.value)}})}},A=Promise.withResolvers(),m=Promise.withResolvers();return n("ai.stream",async(h,d)=>{A.resolve(f(await a(),m.resolve));const c=await m.promise;if(D(e,c.outcome,d,t),c.failed===!0)throw c.error},o).catch(A.reject),A.promise}}},O=(e,t,n)=>{let o=e;return t!==void 0&&typeof e!="string"&&(o=Y({middleware:X(n??K(e)??"unknown",t),model:e})),o},Z=[V,B],S=e=>!e.startsWith("@")&&e.includes("/"),I=e=>{throw new E("INTERNAL",`@lunora/ai: ${e} needs the \`AI\` binding (env.AI). In dev, Lunora leaves it out when there is no Cloudflare login: run \`wrangler login\` or set CLOUDFLARE_API_TOKEN. Elsewhere, add an \`ai\` binding to wrangler.jsonc. Or set ${_} to an OpenAI-compatible proxy for "<provider>/<model>" slugs.`)},C=()=>I("this model id");C.textEmbeddingModel=()=>I("this embedding model id");const J=3040,Q=/^(?:[A-Z]\w*:\s*)?3040\s*:/iu,z=e=>e?.code===J||e instanceof Error&&Q.test(e.message),ee="default",te=new Map([[401,"UNAUTHORIZED"],[403,"FORBIDDEN"],[404,"NOT_FOUND"],[408,"SERVICE_UNAVAILABLE"],[429,"RATE_LIMITED"]]),re=e=>te.get(e)??(e>=400&&e<500?"BAD_REQUEST":"SERVICE_UNAVAILABLE"),ae=300,oe=new Set(["127.0.0.1","[::1]","localhost"]),ne=e=>{const t=p(e,_);if(t===void 0)return;const n=p(e,R),o=URL.canParse(t)?new URL(t):void 0;let a;if(o===void 0?a=`${_} is not a valid URL`:n!==void 0&&o.protocol!=="https:"&&!oe.has(o.hostname)&&(a=`${_} (${o.origin}) is not HTTPS, so ${R} would travel in cleartext — use an https:// URL`),a!==void 0){const f=()=>{throw new E("INTERNAL",`@lunora/ai: ${a}`)};return{chat:f,embedding:f}}return W({apiKey:n??"",baseURL:t,name:"lunora-proxy"})},ie=(e,t)=>{const n=e===void 0?void 0:F(e);return n===void 0?t:{...t,tags:{...n,...t?.tags}}},se=(e,t,n)=>{const o=U(n);if(e!==void 0)return o!==void 0&&e.metadata===void 0?{...e,metadata:o}:e;if(t===void 0)return;const a=p(t,j);if(a!==void 0)return H(t),o===void 0?{id:a}:{id:a,metadata:o}},ye=e=>{const{binding:t,defaultEmbeddingModel:n,defaultModel:o,env:a,gateway:f,metadata:A,provider:m,telemetry:h}=e,d=ne(a),c=ie(a,A),u=se(f,a,c),w=f?.metadata===void 0?U(c):void 0,v=o??p(a,N),y=n??p(a,M),g=m??(t?$({binding:t,gateway:u,providers:Z}):C),x=(r,i)=>S(r)?d!==void 0?d.chat(r):w===void 0?g(r):g(r,{metadata:w}):i?.rejectIfBusy===void 0?g(r):g(r,{rejectIfBusy:i.rejectIfBusy}),k=(r,i)=>{const s=r??v;if(s===void 0||s==="")throw new E("INTERNAL",`@lunora/ai: no model supplied and no default configured — pass a model id, or set ${N} in the Worker env (wrangler \`vars\` / \`.dev.vars\`)`);return typeof s=="string"?O(x(s,i),h,s):O(s,h)},G=r=>{if(d!==void 0&&S(r))return d.embedding(r);const i=g.textEmbeddingModel;if(typeof i!="function")throw new E("INTERNAL","@lunora/ai: the Workers AI provider does not expose `textEmbeddingModel`; pass an AI SDK EmbeddingModel (e.g. from @ai-sdk/openai) to embed()");return i.call(g,r)};return{embeddingModel:r=>{if(typeof r=="object")return r;const i=r??y;if(!i)throw new E("INTERNAL",`@lunora/ai: no embedding model supplied and no default configured — pass an embedding model id or an AI SDK EmbeddingModel, or set ${M} in the Worker env (wrangler \`vars\` / \`.dev.vars\`)`);return G(i)},model:k,run:async(r,i,s)=>{if(!t)return I("ai.run");const b=u!==void 0&&s?.gateway===void 0?{...s,gateway:u}:s;try{return await t.run(r,i,b)}catch(l){throw z(l)?new E("RATE_LIMITED",`@lunora/ai: Workers AI has no free capacity for ${r} (error 3040) — retry later`,{cause:l}):l}},websearch:async(r,i)=>{if(!t)return I("ai.websearch");if(typeof t.websearch!="function")throw new E("NOT_IMPLEMENTED","@lunora/ai: this Workers runtime's `AI` binding has no websearch() — update wrangler / @cloudflare/vite-plugin to a release that ships the Web Search API");const{gatewayId:s,...b}=i??{},l=await t.websearch({...b,gatewayId:s??u?.id??ee,query:r});if(!l.ok){const L=(await l.text().catch(()=>"")).slice(0,ae);throw new E(re(l.status),`@lunora/ai: web search failed with HTTP ${String(l.status)}${L===""?"":`: ${L}`}`)}const T=await l.json().catch(()=>{});if(!Array.isArray(T?.items))throw new E("INTERNAL","@lunora/ai: web search returned a body that is not JSON with an `items` array");return T},workersai:g}};export{ye as default};