@lunora/ai 1.0.0-alpha.129 → 1.0.0-alpha.130
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/create.d.mts +22 -9
- package/dist/create.d.ts +22 -9
- package/dist/create.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/DEFAULT_MODEL_PRICES-C5oaHRiN.mjs +1 -0
- package/dist/packem_shared/createAi-GYEHZ8tt.mjs +1 -0
- package/dist/packem_shared/{defineRag-CJudmmcj.mjs → defineRag-BOS3IQeP.mjs} +1 -1
- package/dist/packem_shared/{types.d-DL0j0z-W.d.ts → types.d-BG8Olmuo.d.mts} +129 -1
- package/dist/packem_shared/{types.d-DL0j0z-W.d.mts → types.d-BG8Olmuo.d.ts} +129 -1
- package/dist/packem_shared/usage-C5GJNFt9.mjs +1 -0
- package/dist/rag/index.d.mts +1 -1
- package/dist/rag/index.d.ts +1 -1
- package/dist/rag/index.mjs +1 -1
- package/package.json +2 -1
- package/dist/packem_shared/DEFAULT_MODEL_PRICES-CIJLs1Ah.mjs +0 -1
- package/dist/packem_shared/createAi-Bufv-O1b.mjs +0 -1
- package/dist/packem_shared/usage-GPnTSqR8.mjs +0 -1
package/dist/create.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as LunoraAiOptions, L as LunoraAi } from "./packem_shared/types.d-
|
|
2
|
-
export type { A as AiBindingLike, b as
|
|
1
|
+
import { a as LunoraAiOptions, L as LunoraAi } from "./packem_shared/types.d-BG8Olmuo.mjs";
|
|
2
|
+
export type { A as AiBindingLike, b as AiImage, c as AiImageGenerateInput, d as AiImageGenerateResult, e as AiToMarkdownInput, f as AiToMarkdownOptions, g as AiToMarkdownResult, h as AiWebSearchItem, i as AiWebSearchProvider } from "./packem_shared/types.d-BG8Olmuo.mjs";
|
|
3
3
|
import 'ai';
|
|
4
4
|
import 'workers-ai-provider';
|
|
5
5
|
/**
|
|
@@ -52,12 +52,14 @@ declare const createAi: (options: LunoraAiOptions) => LunoraAi;
|
|
|
52
52
|
* Pass your own `prices` for anything you are actually invoicing against.
|
|
53
53
|
* @experimental
|
|
54
54
|
*/
|
|
55
|
-
/** What one model costs
|
|
55
|
+
/** What one model costs. Token models price per **one million** tokens; image models price per generated image. */
|
|
56
56
|
interface ModelPrice {
|
|
57
|
-
/** Price per million input (prompt) tokens. */
|
|
58
|
-
input
|
|
57
|
+
/** Price per million input (prompt) tokens. Token-billed models only. */
|
|
58
|
+
input?: number;
|
|
59
59
|
/** Price per million output (completion) tokens. Omit for an embedding model. */
|
|
60
60
|
output?: number;
|
|
61
|
+
/** Price per generated image, in USD. Image models only. */
|
|
62
|
+
perImage?: number;
|
|
61
63
|
}
|
|
62
64
|
/** Token counts to price. Either may be absent. */
|
|
63
65
|
interface ModelUsage {
|
|
@@ -73,9 +75,10 @@ interface ModelUsage {
|
|
|
73
75
|
* and OpenAI — and everything else returns `undefined` rather than a guess.
|
|
74
76
|
*
|
|
75
77
|
* Generation models carry an `output` price; embedding models do not (they have
|
|
76
|
-
* no completion).
|
|
77
|
-
*
|
|
78
|
-
*
|
|
78
|
+
* no completion). Image models carry a `perImage` price instead — the unit an
|
|
79
|
+
* image call is billed in, not tokens. A generation model missing from here is
|
|
80
|
+
* why a chat span would carry no cost at all off an AI Gateway, so the ids the
|
|
81
|
+
* docs teach are the ones that have to be in the table.
|
|
79
82
|
*/
|
|
80
83
|
declare const DEFAULT_MODEL_PRICES: Readonly<Record<string, ModelPrice>>;
|
|
81
84
|
/**
|
|
@@ -93,4 +96,14 @@ declare const lookupModelPrice: (modelId: string, prices?: Readonly<Record<strin
|
|
|
93
96
|
* @experimental
|
|
94
97
|
*/
|
|
95
98
|
declare const estimateModelCost: (modelId: string | undefined, usage: ModelUsage, prices?: Readonly<Record<string, ModelPrice>>) => number | undefined;
|
|
96
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Estimate the cost of generating `count` images in USD, or `undefined` when the
|
|
101
|
+
* model is not priced or carries no image price.
|
|
102
|
+
*
|
|
103
|
+
* Like {@link estimateModelCost}, returns `undefined` rather than `0` for an
|
|
104
|
+
* unpriced model, so a missing price shows up as absent instead of folding a
|
|
105
|
+
* false zero into a total.
|
|
106
|
+
* @experimental
|
|
107
|
+
*/
|
|
108
|
+
declare const estimateImageCost: (modelId: string | undefined, count?: number, prices?: Readonly<Record<string, ModelPrice>>) => number | undefined;
|
|
109
|
+
export { DEFAULT_MODEL_PRICES as D, type LunoraAi, ModelPrice as M, ModelUsage as a, createAi, estimateImageCost, estimateModelCost, lookupModelPrice as l };
|
package/dist/create.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as LunoraAiOptions, L as LunoraAi } from "./packem_shared/types.d-
|
|
2
|
-
export type { A as AiBindingLike, b as
|
|
1
|
+
import { a as LunoraAiOptions, L as LunoraAi } from "./packem_shared/types.d-BG8Olmuo.js";
|
|
2
|
+
export type { A as AiBindingLike, b as AiImage, c as AiImageGenerateInput, d as AiImageGenerateResult, e as AiToMarkdownInput, f as AiToMarkdownOptions, g as AiToMarkdownResult, h as AiWebSearchItem, i as AiWebSearchProvider } from "./packem_shared/types.d-BG8Olmuo.js";
|
|
3
3
|
import 'ai';
|
|
4
4
|
import 'workers-ai-provider';
|
|
5
5
|
/**
|
|
@@ -52,12 +52,14 @@ declare const createAi: (options: LunoraAiOptions) => LunoraAi;
|
|
|
52
52
|
* Pass your own `prices` for anything you are actually invoicing against.
|
|
53
53
|
* @experimental
|
|
54
54
|
*/
|
|
55
|
-
/** What one model costs
|
|
55
|
+
/** What one model costs. Token models price per **one million** tokens; image models price per generated image. */
|
|
56
56
|
interface ModelPrice {
|
|
57
|
-
/** Price per million input (prompt) tokens. */
|
|
58
|
-
input
|
|
57
|
+
/** Price per million input (prompt) tokens. Token-billed models only. */
|
|
58
|
+
input?: number;
|
|
59
59
|
/** Price per million output (completion) tokens. Omit for an embedding model. */
|
|
60
60
|
output?: number;
|
|
61
|
+
/** Price per generated image, in USD. Image models only. */
|
|
62
|
+
perImage?: number;
|
|
61
63
|
}
|
|
62
64
|
/** Token counts to price. Either may be absent. */
|
|
63
65
|
interface ModelUsage {
|
|
@@ -73,9 +75,10 @@ interface ModelUsage {
|
|
|
73
75
|
* and OpenAI — and everything else returns `undefined` rather than a guess.
|
|
74
76
|
*
|
|
75
77
|
* Generation models carry an `output` price; embedding models do not (they have
|
|
76
|
-
* no completion).
|
|
77
|
-
*
|
|
78
|
-
*
|
|
78
|
+
* no completion). Image models carry a `perImage` price instead — the unit an
|
|
79
|
+
* image call is billed in, not tokens. A generation model missing from here is
|
|
80
|
+
* why a chat span would carry no cost at all off an AI Gateway, so the ids the
|
|
81
|
+
* docs teach are the ones that have to be in the table.
|
|
79
82
|
*/
|
|
80
83
|
declare const DEFAULT_MODEL_PRICES: Readonly<Record<string, ModelPrice>>;
|
|
81
84
|
/**
|
|
@@ -93,4 +96,14 @@ declare const lookupModelPrice: (modelId: string, prices?: Readonly<Record<strin
|
|
|
93
96
|
* @experimental
|
|
94
97
|
*/
|
|
95
98
|
declare const estimateModelCost: (modelId: string | undefined, usage: ModelUsage, prices?: Readonly<Record<string, ModelPrice>>) => number | undefined;
|
|
96
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Estimate the cost of generating `count` images in USD, or `undefined` when the
|
|
101
|
+
* model is not priced or carries no image price.
|
|
102
|
+
*
|
|
103
|
+
* Like {@link estimateModelCost}, returns `undefined` rather than `0` for an
|
|
104
|
+
* unpriced model, so a missing price shows up as absent instead of folding a
|
|
105
|
+
* false zero into a total.
|
|
106
|
+
* @experimental
|
|
107
|
+
*/
|
|
108
|
+
declare const estimateImageCost: (modelId: string | undefined, count?: number, prices?: Readonly<Record<string, ModelPrice>>) => number | undefined;
|
|
109
|
+
export { DEFAULT_MODEL_PRICES as D, type LunoraAi, ModelPrice as M, ModelUsage as a, createAi, estimateImageCost, estimateModelCost, lookupModelPrice as l };
|
package/dist/create.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as o}from"./packem_shared/createAi-
|
|
1
|
+
import{default as o}from"./packem_shared/createAi-GYEHZ8tt.mjs";import{estimateImageCost as m,estimateModelCost as r}from"./packem_shared/DEFAULT_MODEL_PRICES-C5oaHRiN.mjs";export{o as createAi,m as estimateImageCost,r as estimateModelCost};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { D as DEFAULT_MODEL_PRICES, type M as ModelPrice, type a as ModelUsage, createAi, estimateModelCost, l as lookupModelPrice } from "./create.mjs";
|
|
2
|
-
export {
|
|
1
|
+
export { D as DEFAULT_MODEL_PRICES, type M as ModelPrice, type a as ModelUsage, createAi, estimateImageCost, estimateModelCost, l as lookupModelPrice } from "./create.mjs";
|
|
2
|
+
export { j as AI_DEFAULT_EMBEDDING_MODEL_ENV, k as AI_DEFAULT_MODEL_ENV, l as AI_GATEWAY_ACCOUNT_ID_ENV, m as AI_GATEWAY_ID_ENV, n as AI_GATEWAY_METADATA_MAX_KEYS, o as AI_GATEWAY_TAGS_ENV, p as AI_GATEWAY_TOKEN_ENV, q as AI_PROXY_TOKEN_ENV, r as AI_PROXY_URL_ENV, type A as AiBindingLike, type s as AiGatewayMetadata, type t as AiGatewayOptions, type b as AiImage, type c as AiImageGenerateInput, type d as AiImageGenerateResult, type u as AiMarkdownConversionResult, type v as AiMarkdownDocument, type w as AiMetrics, type x as AiModelOptions, type y as AiRunOptions, type z as AiSpan, type B as AiTelemetry, type e as AiToMarkdownInput, type f as AiToMarkdownOptions, type g as AiToMarkdownResult, type C as AiTracer, type h as AiWebSearchItem, type D as AiWebSearchOptions, type i as AiWebSearchProvider, type F as AiWebSearchResult, type E as EmbeddingModelInput, type L as LunoraAi, type a as LunoraAiOptions, type M as ModelInput, type R as ResolvedAiGateway, type W as WorkersAiProviderLike, G as buildAiGatewayMetadataFields, H as readAiGatewayEnvTags, I as resolveAiGateway } from "./packem_shared/types.d-BG8Olmuo.mjs";
|
|
3
3
|
export { type EmbeddingModel, type LanguageModel, embed, embedMany, generateObject, generateText, hasToolCall, jsonSchema, streamObject, streamText, tool } from 'ai';
|
|
4
4
|
export { createWorkersAI } from 'workers-ai-provider';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { D as DEFAULT_MODEL_PRICES, type M as ModelPrice, type a as ModelUsage, createAi, estimateModelCost, l as lookupModelPrice } from "./create.js";
|
|
2
|
-
export {
|
|
1
|
+
export { D as DEFAULT_MODEL_PRICES, type M as ModelPrice, type a as ModelUsage, createAi, estimateImageCost, estimateModelCost, l as lookupModelPrice } from "./create.js";
|
|
2
|
+
export { j as AI_DEFAULT_EMBEDDING_MODEL_ENV, k as AI_DEFAULT_MODEL_ENV, l as AI_GATEWAY_ACCOUNT_ID_ENV, m as AI_GATEWAY_ID_ENV, n as AI_GATEWAY_METADATA_MAX_KEYS, o as AI_GATEWAY_TAGS_ENV, p as AI_GATEWAY_TOKEN_ENV, q as AI_PROXY_TOKEN_ENV, r as AI_PROXY_URL_ENV, type A as AiBindingLike, type s as AiGatewayMetadata, type t as AiGatewayOptions, type b as AiImage, type c as AiImageGenerateInput, type d as AiImageGenerateResult, type u as AiMarkdownConversionResult, type v as AiMarkdownDocument, type w as AiMetrics, type x as AiModelOptions, type y as AiRunOptions, type z as AiSpan, type B as AiTelemetry, type e as AiToMarkdownInput, type f as AiToMarkdownOptions, type g as AiToMarkdownResult, type C as AiTracer, type h as AiWebSearchItem, type D as AiWebSearchOptions, type i as AiWebSearchProvider, type F as AiWebSearchResult, type E as EmbeddingModelInput, type L as LunoraAi, type a as LunoraAiOptions, type M as ModelInput, type R as ResolvedAiGateway, type W as WorkersAiProviderLike, G as buildAiGatewayMetadataFields, H as readAiGatewayEnvTags, I as resolveAiGateway } from "./packem_shared/types.d-BG8Olmuo.js";
|
|
3
3
|
export { type EmbeddingModel, type LanguageModel, embed, embedMany, generateObject, generateText, hasToolCall, jsonSchema, streamObject, streamText, tool } from 'ai';
|
|
4
4
|
export { createWorkersAI } from 'workers-ai-provider';
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as _}from"./packem_shared/createAi-
|
|
1
|
+
import{default as _}from"./packem_shared/createAi-GYEHZ8tt.mjs";import{AI_DEFAULT_EMBEDDING_MODEL_ENV as E,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 m,AI_PROXY_TOKEN_ENV as s,AI_PROXY_URL_ENV as N,buildAiGatewayMetadataFields as l,readAiGatewayEnvTags as D,resolveAiGateway as G}from"./packem_shared/AI_DEFAULT_EMBEDDING_MODEL_ENV-IwcHZiKj.mjs";import{DEFAULT_MODEL_PRICES as O,estimateImageCost as d,estimateModelCost as i,lookupModelPrice as V}from"./packem_shared/DEFAULT_MODEL_PRICES-C5oaHRiN.mjs";import{embed as x,embedMany as L,generateObject as c,generateText as f,hasToolCall as p,jsonSchema as C,streamObject as W,streamText as b,tool as n}from"ai";import{createWorkersAI as g}from"workers-ai-provider";export{E 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,m as AI_GATEWAY_TOKEN_ENV,s as AI_PROXY_TOKEN_ENV,N as AI_PROXY_URL_ENV,O as DEFAULT_MODEL_PRICES,l as buildAiGatewayMetadataFields,_ as createAi,g as createWorkersAI,x as embed,L as embedMany,d as estimateImageCost,i as estimateModelCost,c as generateObject,f as generateText,p as hasToolCall,C as jsonSchema,V as lookupModelPrice,D as readAiGatewayEnvTags,G as resolveAiGateway,W as streamObject,b as streamText,n as tool};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const p=/^(.*)-\d{4}-\d{2}-\d{2}$/u,c={"@cf/baai/bge-base-en-v1.5":{input:.067},"@cf/baai/bge-large-en-v1.5":{input:.204},"@cf/baai/bge-m3":{input:.012},"@cf/baai/bge-small-en-v1.5":{input:.02},"@cf/meta/llama-3.1-8b-instruct":{input:.28,output:.83},"@cf/meta/llama-3.3-70b-instruct-fp8-fast":{input:.29,output:2.25},"@cf/black-forest-labs/flux-1-schnell":{perImage:6336e-7},"dall-e-3":{perImage:.04},"gpt-image-1":{perImage:.04},"text-embedding-3-large":{input:.13},"text-embedding-3-small":{input:.02},"gpt-4o":{input:2.5,output:10},"gpt-4o-mini":{input:.15,output:.6},"gpt-5":{input:1.25,output:10}},m=i=>{const t=i.trim(),n=t.indexOf("/@"),e=n===-1?t:t.slice(n+1),o=e.lastIndexOf("/");return(o!==-1&&!e.startsWith("@")?[e,e.slice(o+1)]:[e]).flatMap(s=>{const r=p.exec(s)?.[1];return r===void 0?[s]:[s,r]})},a=(i,t=c)=>{for(const n of m(i))if(Object.hasOwn(t,n))return t[n]},l=(i,t,n)=>{if(i===void 0||i.length===0)return;const e=a(i,n);if(e?.input===void 0&&e?.output===void 0)return;const o=Number.isFinite(t.inputTokens)?Math.max(0,t.inputTokens):0,u=Number.isFinite(t.outputTokens)?Math.max(0,t.outputTokens):0;if(o<=0&&u<=0)return;const s=(o*(e.input??0)+u*(e.output??0))/1e6;return Number.isFinite(s)?s:void 0},f=(i,t=1,n)=>{if(i===void 0||i.length===0)return;const e=a(i,n);if(e?.perImage===void 0)return;const o=Number.isFinite(t)?Math.max(0,t):0;if(o<=0)return;const u=o*e.perImage;return Number.isFinite(u)?u:void 0};export{c as DEFAULT_MODEL_PRICES,f as estimateImageCost,l as estimateModelCost,a as lookupModelPrice};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./DEFAULT_MODEL_PRICES-C5oaHRiN.mjs";import{LunoraError as m}from"@lunora/errors";import{buildAiGatewayMetadataFields as J,readEnv as N,AI_DEFAULT_MODEL_ENV as F,AI_DEFAULT_EMBEDDING_MODEL_ENV as H,AI_PROXY_URL_ENV as D,AI_PROXY_TOKEN_ENV as ee,readAiGatewayEnvTags as pe,AI_GATEWAY_ID_ENV as te,warnIgnoredBindingToken as be,resolveAiGateway as Ie}from"./AI_DEFAULT_EMBEDDING_MODEL_ENV-IwcHZiKj.mjs";import{m as _e,r as z,a as Te}from"./usage-C5GJNFt9.mjs";const L=e=>{let t="";for(let n=0;n<e.length;n+=32768)t+=String.fromCharCode(...e.subarray(n,n+32768));return btoa(t)},Me=e=>{const t=atob(e),r=new Uint8Array(t.length);for(let n=0;n<t.length;n+=1)r[n]=t.codePointAt(n)??0;return r},Ne=/^\d{1,3}$/u,Pe=/^::ffff:([\da-f]{1,4}):([\da-f]{1,4})$/u,Le=/^::ffff:0:([\da-f]{1,4}):([\da-f]{1,4})$/u,Re=/^::[\da-f]{1,4}$/u,De=/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/u,Oe=/^::(\d{1,3}(?:\.\d{1,3}){3})$/u,Ue=/^::([\da-f]{1,4}):([\da-f]{1,4})$/u,ke=/^64:ff9b::[\da-f]{1,4}:[\da-f]{1,4}$/u,Se=/^[\da-f]{1,4}$/u,xe=[[[8193],23],[[16383],20],[[24320],16],[[256,0,0,1],64]],We=[[[8193,1,0,0,0,0,0,1],128],[[8193,1,0,0,0,0,0,2],128],[[8193,1,0,0,0,0,0,3],128],[[8193,3],32],[[8193,4,274],48],[[8193,5],32],[[8193,32],28],[[8193,48],28]],$e=e=>{const t=e.split("::");if(t.length>2)return;const r=l=>l===void 0||l===""?[]:l.split(":").map(u=>Se.test(u)?Number.parseInt(u,16):Number.NaN),n=r(t[0]),a=r(t[1]),s=8-n.length-a.length;if(s<0||t.length===1&&s!==0)return;const d=[...n,...Array.from({length:s},()=>0),...a];return d.some(Number.isNaN)?void 0:d},K=(e,[t,r])=>{for(let n=0;n*16<r;n+=1){const s=65535<<16-Math.min(16,r-n*16)&65535;if(((e[n]??0)&s)!==((t[n]??0)&s))return!1}return!0},Ce=/^\[|\]$/gu,Be=/\.$/u,B=e=>{const t=e.split(".");if(t.length!==4)return;const r=t.map(n=>Ne.test(n)?Number(n):-1);if(!r.some(n=>n<0||n>255))return[r[0],r[1],r[2],r[3]]},O=([e,t,r])=>e===0||e===10||e===127||e===100&&t>=64&&t<=127||e===169&&t===254||e===172&&t>=16&&t<=31||e===192&&t===0&&(r===0||r===2)||e===192&&t===88&&r===99||e===192&&t===168||e===198&&(t===18||t===19)||e===198&&t===51&&r===100||e===203&&t===0&&r===113||e>=224,S=(e,t)=>{const r=Number.parseInt(e??"",16),n=Number.parseInt(t??"",16);return!Number.isFinite(r)||!Number.isFinite(n)?!0:O([Math.floor(r/256),r%256,Math.floor(n/256),n%256])},Ge=e=>{const t=e.toLowerCase(),r=Pe.exec(t);if(r)return S(r[1],r[2]);const n=Le.exec(t);if(n)return S(n[1],n[2]);const a=De.exec(t);if(a){const u=B(a[1]??"");return u===void 0||O(u)}const s=Oe.exec(t);if(s){const u=B(s[1]??"");return u===void 0||O(u)}const d=Ue.exec(t);if(d)return S(d[1],d[2]);if(Re.test(t)||ke.test(t)||t.startsWith("64:ff9b:1:")||t.startsWith("2002:"))return!0;const l=$e(t);return l!==void 0&&xe.some(u=>K(l,u))&&!We.some(u=>K(l,u))?!0:t==="::"||t==="::1"||t.startsWith("fc")||t.startsWith("fd")||t.startsWith("fe8")||t.startsWith("fe9")||t.startsWith("fea")||t.startsWith("feb")||t.startsWith("fec")||t.startsWith("fed")||t.startsWith("fee")||t.startsWith("fef")||t.startsWith("ff")||t.startsWith("2001:db8:")||t.startsWith("100::")},Ve=e=>e==="localhost"||e.endsWith(".localhost")||e.endsWith(".local")||e.endsWith(".internal")||e.endsWith(".home.arpa"),Fe=e=>e.replaceAll(Ce,"").replace(Be,"").toLowerCase(),He=e=>{const t=Fe(e);if(t.includes(":"))return Ge(t);const r=B(t);return r===void 0?Ve(t):O(r)},ze={setAttribute:()=>{},setAttributes:()=>{}},re=async(e,t)=>t(re,ze),Ke=(e,{metrics:t,trace:r=re})=>{const n={"gen_ai.operation.name":"chat","gen_ai.request.model":e};return{wrapGenerate:async({doGenerate:a})=>r("ai.generate",async(s,d)=>{const l=await a();return z(e,l,d,t),l},n),wrapStream:async({doStream:a})=>{const s=(u,v)=>{const y=u.stream.getReader();let A={};return{...u,stream:new ReadableStream({cancel:async E=>{v({outcome:A}),await y.cancel(E)},pull:async E=>{let p;try{p=await y.read()}catch(P){v({error:P,failed:!0,outcome:A}),E.error(P);return}if(p.done){v({outcome:A}),E.close();return}p.value.type==="finish"&&(A={providerMetadata:p.value.providerMetadata,usage:p.value.usage}),E.enqueue(p.value)}})}},d=Promise.withResolvers(),l=Promise.withResolvers();return r("ai.stream",async(u,v)=>{d.resolve(s(await a(),l.resolve));const y=await l.promise;if(z(e,y.outcome,v,t),y.failed===!0)throw y.error},n).catch(d.reject),d.promise}}},Y=(e,t,r)=>{if(t===void 0||typeof e=="string")return e;const n=Ke(r??_e(e)??"unknown",t);let a;const s=async()=>(a??=import("ai").then(({wrapLanguageModel:l})=>l({middleware:n,model:e}),l=>{throw a=void 0,l}),a),d=e;return{doGenerate:async l=>s().then(u=>u.doGenerate(l)),doStream:async l=>s().then(u=>u.doStream(l)),modelId:d.modelId,provider:d.provider,specificationVersion:"v4",get supportedUrls(){return s().then(l=>l.supportedUrls)}}};let x;const ne=()=>(x??=Promise.all([import("@ai-sdk/openai"),import("workers-ai-provider/openai"),import("workers-ai-provider/anthropic")]).then(([e,{openai:t},{anthropic:r}])=>({createOpenAI:e.createOpenAI,plugins:[t,r]}),e=>{throw x=void 0,e}),x),oe=(e,t,r)=>({doGenerate:async n=>r().then(a=>a.doGenerate(n)),doStream:async n=>r().then(a=>a.doStream(n)),modelId:e,provider:t,specificationVersion:"v4",get supportedUrls(){return r().then(n=>n.supportedUrls)}}),Ye={maxEmbeddingsPerCall:3e3,supportsParallelCalls:!0},ae=(e,t,r,n)=>({doEmbed:async a=>r().then(s=>s.doEmbed(a)),get maxEmbeddingsPerCall(){return n?.maxEmbeddingsPerCall??r().then(a=>a.maxEmbeddingsPerCall)},modelId:e,provider:t,specificationVersion:"v4",get supportsParallelCalls(){return n?.supportsParallelCalls??r().then(a=>a.supportsParallelCalls)}});let W;const X=()=>(W??=import("workers-ai-provider").catch(e=>{throw W=void 0,e}),W),R=e=>!e.startsWith("@")&&e.includes("/"),Xe=e=>{const t=(r,n)=>oe(r,"workersai.chat",async()=>e(r).then(a=>a(r,n)));return t.textEmbeddingModel=r=>ae(r,"workersai.embedding",async()=>e(r).then(n=>n.textEmbeddingModel?.(r)),R(r)?void 0:Ye),t},_=e=>{throw new m("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 ${D} to an OpenAI-compatible proxy for "<provider>/<model>" slugs.`)},se=()=>_("this model id");se.textEmbeddingModel=()=>_("this embedding model id");const je=3040,Qe=/^(?:[A-Z]\w*:\s*)?3040\s*:/iu,qe=e=>e?.code===je||e instanceof Error&&Qe.test(e.message),Ze="default",Je=new Map([[401,"UNAUTHORIZED"],[403,"FORBIDDEN"],[404,"NOT_FOUND"],[408,"SERVICE_UNAVAILABLE"],[429,"RATE_LIMITED"]]),ie=e=>Je.get(e)??(e>=400&&e<500?"BAD_REQUEST":"SERVICE_UNAVAILABLE"),de=300,et=new Set(["127.0.0.1","[::1]","localhost"]),tt=(e,t)=>{const r=URL.canParse(e)?new URL(e):void 0;if(r===void 0)return`${D} is not a valid URL`;if(t!==void 0&&r.protocol!=="https:"&&!et.has(r.hostname))return`${D} (${r.origin}) is not HTTPS, so ${ee} would travel in cleartext — use an https:// URL`},rt=e=>{const t=N(e,D);if(t===void 0)return;const r=N(e,ee),n=tt(t,r);if(n!==void 0){const d=()=>{throw new m("INTERNAL",`@lunora/ai: ${n}`)};return{chat:d,embedding:d,image:d}}let a;const s=()=>(a??=ne().then(({createOpenAI:d})=>d({apiKey:r??"",baseURL:t,name:"lunora-proxy"}),d=>{throw a=void 0,d}),a);return{chat:d=>oe(d,"lunora-proxy",async()=>s().then(l=>l.chat(d))),embedding:d=>ae(d,"lunora-proxy",async()=>s().then(l=>l.embedding(d))),image:async d=>s().then(l=>l.image(d))}},nt=(e,t)=>{const r=e===void 0?void 0:pe(e);return r===void 0?t:{...t,tags:{...r,...t?.tags}}},ot=(e,t,r)=>{const n=J(r);if(e!==void 0)return n!==void 0&&e.metadata===void 0?{...e,metadata:n}:e;if(t===void 0)return;const a=N(t,te);if(a!==void 0)return be(t),n===void 0?{id:a}:{id:a,metadata:n}};let $;const at=()=>($??=Promise.all([import("@ai-sdk/openai"),import("@ai-sdk/google")]).then(([e,t])=>({createGoogle:t.createGoogle,createOpenAI:e.createOpenAI}),e=>{throw $=void 0,e}),$),j=8,st=/^[1-9]\d*x[1-9]\d*$/u,it=20*1024*1024,Q="unused",q=(e,t)=>{const r=new Headers(t?.headers);return r.delete("authorization"),r.delete("x-goog-api-key"),fetch(e,{...t,headers:r})},dt=e=>{const t=e.pathname.split("/").findLast(r=>r.length>0);return t===void 0||t===""?"document":t},C=(e,t)=>{if(e>t)throw new m("PAYLOAD_TOO_LARGE",`@lunora/ai: toMarkdown document is ${String(e)} bytes, over the ${String(t)}-byte limit — raise \`maxBytes\` to convert it`)},ct=async(e,t)=>{if(e.body===null)return new Uint8Array;let r=0;const n=e.body.pipeThrough(new TransformStream({transform(a,s){if(r+=a.byteLength,r>t){s.error(new m("PAYLOAD_TOO_LARGE",`@lunora/ai: toMarkdown document is over the ${String(t)}-byte limit — raise \`maxBytes\` to convert it`));return}s.enqueue(a)}}));return new Uint8Array(await new Response(n).arrayBuffer())},lt=e=>{if(e.protocol!=="https:"&&e.protocol!=="http:"||He(e.hostname))throw new m("BAD_REQUEST",`@lunora/ai: toMarkdown will not fetch ${e.href} — only public http(s) hosts; pass its bytes or a Blob instead`)},ut=async(e,t,r)=>{if(e instanceof URL){lt(e);const s=await fetch(e,{redirect:"manual"});if(!s.ok){const v=s.status>=300&&s.status<400?"; redirects are not followed, so pass the final URL":"";throw new m("BAD_REQUEST",`@lunora/ai: toMarkdown could not fetch ${e.href} (HTTP ${String(s.status)})${v}`)}const d=Number(s.headers.get("content-length"));s.headers.has("content-length")&&Number.isFinite(d)&&C(d,r);const l=await ct(s,r),u=s.headers.get("content-type");return{blob:new Blob([l],u===null||u===""?void 0:{type:u}),name:t?.name??dt(e)}}if(e instanceof Blob)return C(e.size,r),{blob:e,name:t?.name??"document"};const n=e instanceof Uint8Array?Uint8Array.from(e):new Uint8Array(e);C(n.byteLength,r);const a=t?.mimeType;return{blob:new Blob([n],a===void 0?void 0:{type:a}),name:t?.name??"document"}},Z=e=>{if(e===void 0||e==="")return;const t=Number(e);return Number.isInteger(t)?t:void 0},ft=e=>{if(e===void 0)return{};const[t,r]=e.split("x");return{height:Z(r),width:Z(t)}},T=e=>{const t=(r,n=0)=>r.every((a,s)=>e[n+s]===a);return t([137,80,78,71])?"image/png":t([255,216,255])?"image/jpeg":t([71,73,70,56])?"image/gif":t([82,73,70,70])&&t([87,69,66,80],8)?"image/webp":void 0},mt=e=>{try{return Me(e.slice(0,16))}catch{return new Uint8Array}},M=(e,t)=>t===void 0?{base64:e}:{base64:e,mimeType:t},ce=async e=>{if(typeof e=="string")return M(e,T(mt(e)));if(e instanceof Uint8Array)return M(L(e),T(e));if(e instanceof ArrayBuffer){const t=new Uint8Array(e);return M(L(t),T(t))}if(e instanceof Response){if(!e.ok){const n=(await e.text().catch(()=>"")).slice(0,de);throw new m(ie(e.status),`@lunora/ai: image generation failed with HTTP ${String(e.status)}${n===""?"":`: ${n}`}`)}const t=new Uint8Array(await e.arrayBuffer());return M(L(t),T(t))}if(e instanceof ReadableStream){const t=new Uint8Array(await new Response(e).arrayBuffer());return M(L(t),T(t))}if(typeof e=="object"&&e!==null&&"image"in e)return ce(e.image)},pt=e=>{const{binding:t,defaultEmbeddingModel:r,defaultModel:n,env:a,gateway:s,metadata:d,provider:l,telemetry:u}=e,v=rt(a),y=nt(a,d),A=ot(s,a,y),E=s?.metadata===void 0?J(y):void 0,p=n??N(a,F),P=r??N(a,H);let U;const le=()=>(U??=X().then(({createWorkersAI:o})=>o({binding:t,gateway:A})).catch(o=>{throw U=void 0,o}),U);let k;const ue=()=>(k??=Promise.all([X(),ne()]).then(([{createWorkersAI:o},{plugins:c}])=>o({binding:t,gateway:A,providers:c})).catch(o=>{throw k=void 0,o}),k),b=l??(t?Xe(async o=>R(o)?ue():le()):se),fe=(o,c)=>R(o)?v!==void 0?v.chat(o):E===void 0?b(o):b(o,{metadata:E}):c?.rejectIfBusy===void 0?b(o):b(o,{rejectIfBusy:c.rejectIfBusy}),me=(o,c)=>{const i=o??p;if(i===void 0||i==="")throw new m("INTERNAL",`@lunora/ai: no model supplied and no default configured — pass a model id, or set ${F} in the Worker env (wrangler \`vars\` / \`.dev.vars\`)`);return typeof i=="string"?Y(fe(i,c),u,i):Y(i,u)},he=o=>{if(v!==void 0&&R(o))return v.embedding(o);const c=b.textEmbeddingModel;if(typeof c!="function")throw new m("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 c.call(b,o)},ge=o=>{if(typeof o=="object")return o;const c=o??P;if(!c)throw new m("INTERNAL",`@lunora/ai: no embedding model supplied and no default configured — pass an embedding model id or an AI SDK EmbeddingModel, or set ${H} in the Worker env (wrangler \`vars\` / \`.dev.vars\`)`);return he(c)},G=async(o,c,i)=>{if(!t)return _("ai.run");const g=A!==void 0&&i?.gateway===void 0?{...i,gateway:A}:i;try{return await t.run(o,c,g)}catch(h){throw qe(h)?new m("RATE_LIMITED",`@lunora/ai: Workers AI has no free capacity for ${o} (error 3040) — retry later`,{cause:h}):h}},we=async(o,c)=>{if(!t)return _("ai.websearch");if(typeof t.websearch!="function")throw new m("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:i,...g}=c??{},h=await t.websearch({...g,gatewayId:i??A?.id??Ze,query:o});if(!h.ok){const I=(await h.text().catch(()=>"")).slice(0,de);throw new m(ie(h.status),`@lunora/ai: web search failed with HTTP ${String(h.status)}${I===""?"":`: ${I}`}`)}const w=await h.json().catch(()=>{});if(!Array.isArray(w?.items))throw new m("INTERNAL","@lunora/ai: web search returned a body that is not JSON with an `items` array");return w},ve=async(o,c)=>{if(!t)return _("ai.toMarkdown");if(typeof t.toMarkdown!="function")throw new m("NOT_IMPLEMENTED","@lunora/ai: this Workers runtime's `AI` binding has no toMarkdown() — update wrangler / @cloudflare/vite-plugin to a release that ships the Markdown Conversion API");const i=c?.maxBytes??it;if(!Number.isFinite(i)||i<=0)throw new m("BAD_REQUEST","@lunora/ai: toMarkdown maxBytes must be a positive number of bytes");const{blob:g,name:h}=await ut(o,c,i),w=await t.toMarkdown({blob:g,name:h},c?.conversionOptions),f=Array.isArray(w)?w[0]:w;if(f===void 0)throw new m("INTERNAL","@lunora/ai: toMarkdown returned no conversion result");if(f.format==="error")throw new m("BAD_REQUEST",`@lunora/ai: toMarkdown could not convert "${h}": ${f.error??"unknown error"}`);if(typeof f.data!="string")throw new m("INTERNAL",`@lunora/ai: toMarkdown returned a "${f.format}" result with no data`);return{markdown:f.data,...f.mimetype===void 0?{}:{mimeType:f.mimetype},...f.pages===void 0?{}:{pages:f.pages},...f.tokens===void 0?{}:{tokens:f.tokens}}},Ae=async o=>{if(!t)return _("ai.images.generate");const{height:c,width:i}=ft(o.size),g={prompt:o.prompt,...c===void 0?{}:{height:c},...i===void 0?{}:{width:i},...o.seed===void 0?{}:{seed:o.seed}},h=async(w,f)=>{if(w>=(o.count??1))return f;const I=await ce(await G(o.model,g));if(I===void 0)throw new m("INTERNAL",`@lunora/ai: ${o.model} returned no image data`);return h(w+1,[...f,I])};return{images:await h(0,[]),model:o.model}},ye=async(o,c,i)=>{const{createGoogle:g,createOpenAI:h}=await at(),w=A===void 0?void 0:Ie({...a,[te]:A.id});let f;o==="openai"?v!==void 0?f=await v.image(i.model):w===void 0?f=h().image(c):f=h({apiKey:Q,baseURL:`${w.baseURL}/openai`,fetch:q,headers:w.headers}).image(c):w===void 0?f=g().image(c):f=g({apiKey:Q,baseURL:`${w.baseURL}/google-ai-studio/v1beta`,fetch:q,headers:w.headers}).image(c);const{generateImage:I}=await import("ai");return{images:(await I({model:f,prompt:i.prompt,...i.count===void 0?{}:{n:i.count},...i.seed===void 0?{}:{seed:i.seed},...i.size===void 0?{}:{size:i.size}})).images.map(V=>({base64:V.base64,mimeType:V.mediaType})),model:i.model}},Ee=async(o,c)=>{if(o.startsWith("@"))return Ae(c);const i=o.indexOf("/"),g=i===-1?"":o.slice(0,i),h=i===-1?"":o.slice(i+1);if((g==="openai"||g==="google")&&h.length>0)return ye(g,h,c);throw new m("AI_UNKNOWN_IMAGE_MODEL",`@lunora/ai: no image provider for "${o}" — pass a Workers AI image id (@cf/…), or an "openai/<model>" or "google/<model>" slug`)};return{embeddingModel:ge,images:{generate:async o=>{const c=o.model,i=o.count??1;if(!Number.isInteger(i)||i<1||i>j)throw new m("BAD_REQUEST",`@lunora/ai: count must be an integer from 1 to ${String(j)}`,{status:400});if(o.size!==void 0&&!st.test(o.size))throw new m("BAD_REQUEST",`@lunora/ai: size must be "<width>x<height>" with positive integers, e.g. "1024x1024" (got "${o.size}")`);const g=await Ee(c,o);return Te(c,g.images.length,u?.metrics),g}},model:me,run:G,toMarkdown:ve,websearch:we,workersai:b}};export{pt as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{tool as Re,jsonSchema as De}from"@ai-sdk/provider-utils";import{LunoraError as w,isLunoraError as Ce}from"@lunora/errors";import{stableStringify as Be}from"@lunora/wire";import{r as de,m as L}from"./usage-
|
|
1
|
+
import{tool as Re,jsonSchema as De}from"@ai-sdk/provider-utils";import{LunoraError as w,isLunoraError as Ce}from"@lunora/errors";import{stableStringify as Be}from"@lunora/wire";import{r as de,m as L}from"./usage-C5GJNFt9.mjs";import $e from"./fixedWindowChunks-C461ahRE.mjs";import{c as Oe,I as Ke}from"./concurrent-C6nqBv41.mjs";import{contentHash as Ue}from"./contentHash-BIn6ECP8.mjs";import{hybridRank as Qe}from"./hybridRank-B4skyCLx.mjs";import{VECTORIZE_CAPABILITIES as le,vectorizeStore as Ve}from"./VECTORIZE_CAPABILITIES-l9ceRO5j.mjs";const Fe=1e3,Le=200,ze=5,ue=1e3,Pe=4,me=le.maxMetadataBytes===!1?Number.POSITIVE_INFINITY:le.maxMetadataBytes,je=2*1024,we="__ragChunk",be="__ragSource",K="__ragText",q="__ragHash",W="__ragChunks",V="__ragImportance",ve="__ragModel",He=new Set([we,W,q,V,ve,be,K]),Ye=async(e,c,u,m)=>{if(c.length===0)return;const h=c.slice(1);if(!e.upsertMany){await Oe(h,Ke,async(E,A)=>{await e.upsert(E,m),u(A+1)}),await e.upsert(c[0],m),u(0);return}for(let E=0;E<h.length;E+=ue){const A=Math.min(E+ue,h.length);await e.upsertMany(h.slice(E,A),m);for(let U=E;U<A;U+=1)u(U+1)}await e.upsertMany([c[0]],m),u(0)},qe=(e,c,u,m)=>{if(m===!1)return;const h=new TextEncoder().encode(JSON.stringify(e)).length;if(h<=m)return;const A=(typeof e[K]=="string"?new TextEncoder().encode(e[K]).length:0)*2>h?"lower `chunkSize` (it counts characters, not bytes — multibyte text costs up to 3 bytes each), or supply `textStore` to move chunk text out of metadata entirely":"attach less per-source `metadata`";throw new w("BAD_REQUEST",`@lunora/ai/rag: chunk ${String(c)} of "${u}" carries ${String(h)} bytes of metadata, over the store's ${String(m)}-byte per-vector ceiling — ${A}`)},We=(e,c,u)=>{if(u===!1)return;const m=new TextEncoder().encode(e).length;if(!(m<=u))throw new w("BAD_REQUEST",`@lunora/ai/rag: chunk id "${e}" for source "${c}" is ${String(m)} bytes, over the store's ${String(u)}-byte per-vector id ceiling — shorten the source id (hash long keys before indexing them) or shorten the \`namespace\`, which is prefixed onto every chunk id`)},Ze=/^[\w.-]{1,40}$/,ye=e=>e===void 0?"":`${encodeURIComponent(e)}#`,O=(e,c,u)=>`${ye(e)}${c}#${String(u)}`,he=(e,c)=>{const u=ye(c),m=u!==""&&e.startsWith(u)?e.slice(u.length):e,h=m.lastIndexOf("#"),E=h===-1?Number.NaN:Number(m.slice(h+1));return h===-1||!Number.isInteger(E)||E<0?{chunkIndex:0,sourceId:m}:{chunkIndex:E,sourceId:m.slice(0,h)}},Xe=async e=>Ue(new TextEncoder().encode(e)),Je=e=>{try{return Be([e.text,e.metadata,e.importance])}catch{return}},Ge=(e,c)=>{const u=[],m=[];for(const h of e)h.score>=c?u.push(h):m.push(h.id);return{kept:u,rejectedIds:m}},et=e=>e!==void 0&&Object.keys(e).length>0,H=e=>{if(!e)return;const c=Object.entries(e).filter(([u])=>!He.has(u));return c.length>0?Object.fromEntries(c):void 0},pe=new Set,tt=e=>{pe.has(e)||(pe.add(e),console.warn(`[@lunora/ai/rag] index "${e}" is used without a namespace — in a multi-tenant/sharded
|
|
2
2
|
app this shares one tenant's chunks (text included) with every other tenant, since
|
|
3
3
|
Vectorize indexes are account-global. Pass \`namespace\` (the shard/tenant key) on both
|
|
4
4
|
index() and retrieve(). Single-tenant apps suppress this via { allowSharedNamespace: true }.`))},nt=e=>e.map(c=>`[source:${c.sourceId}#${String(c.chunkIndex)}]
|
|
@@ -188,6 +188,14 @@ declare const resolveAiGateway: (env: Record<string, unknown>, metadata?: AiGate
|
|
|
188
188
|
*/
|
|
189
189
|
interface AiBindingLike {
|
|
190
190
|
run: (model: string, inputs: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
|
|
191
|
+
/**
|
|
192
|
+
* Markdown Conversion API (beta) — converts a document (PDF, Office file,
|
|
193
|
+
* HTML, image) to Markdown. Optional because runtimes and test doubles that
|
|
194
|
+
* predate it have no such method; `ctx.ai.toMarkdown` reports that instead
|
|
195
|
+
* of a bare `TypeError`. Mirrors `env.AI.toMarkdown(files, options)` at
|
|
196
|
+
* https://developers.cloudflare.com/workers-ai/features/markdown-conversion/.
|
|
197
|
+
*/
|
|
198
|
+
toMarkdown?: (files: AiMarkdownDocument | AiMarkdownDocument[], options?: Record<string, unknown>) => Promise<AiMarkdownConversionResult | AiMarkdownConversionResult[]>;
|
|
191
199
|
/**
|
|
192
200
|
* Web Search API (beta). Optional because runtimes and test doubles that
|
|
193
201
|
* predate it have no such method; `ctx.ai.websearch` reports that instead of
|
|
@@ -198,6 +206,109 @@ interface AiBindingLike {
|
|
|
198
206
|
query: string;
|
|
199
207
|
}) => Promise<Response>;
|
|
200
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* One document handed to `ctx.ai.toMarkdown(...)`: the Workers AI
|
|
211
|
+
* `MarkdownDocument` shape (a name plus a Blob carrying the bytes).
|
|
212
|
+
* @experimental
|
|
213
|
+
*/
|
|
214
|
+
interface AiMarkdownDocument {
|
|
215
|
+
blob: Blob;
|
|
216
|
+
name: string;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* One Workers AI `ConversionResult`. `data` and `tokens` are present only when
|
|
220
|
+
* `format` is `"markdown"` (or `"text"`); an `"error"` result carries `error`
|
|
221
|
+
* instead. `pages` is not in the current binding contract — carried here so a
|
|
222
|
+
* binding that starts reporting it passes through unchanged.
|
|
223
|
+
* @experimental
|
|
224
|
+
*/
|
|
225
|
+
interface AiMarkdownConversionResult {
|
|
226
|
+
data?: string;
|
|
227
|
+
error?: string;
|
|
228
|
+
format: "error" | "markdown" | "text";
|
|
229
|
+
id?: string;
|
|
230
|
+
/** The detected document mime type (spelled `mimetype` by the binding). */
|
|
231
|
+
mimetype?: string;
|
|
232
|
+
name?: string;
|
|
233
|
+
/** Page count, when the binding reports it. */
|
|
234
|
+
pages?: number;
|
|
235
|
+
tokens?: number;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* What `ctx.ai.toMarkdown(...)` accepts: a `Blob`, raw bytes, or a `URL` that
|
|
239
|
+
* is fetched first.
|
|
240
|
+
* @experimental
|
|
241
|
+
*/
|
|
242
|
+
type AiToMarkdownInput = ArrayBuffer | Blob | Uint8Array | URL;
|
|
243
|
+
/**
|
|
244
|
+
* Options for `ctx.ai.toMarkdown(...)`.
|
|
245
|
+
* @experimental
|
|
246
|
+
*/
|
|
247
|
+
interface AiToMarkdownOptions {
|
|
248
|
+
/** Conversion options forwarded verbatim to the binding's `toMarkdown(files, options)`. */
|
|
249
|
+
conversionOptions?: Record<string, unknown>;
|
|
250
|
+
/**
|
|
251
|
+
* Byte cap for a `URL`/raw-bytes input, guarding against an accidental huge
|
|
252
|
+
* download. Defaults to 20 MiB. A `Blob` is checked against the same cap.
|
|
253
|
+
*/
|
|
254
|
+
maxBytes?: number;
|
|
255
|
+
/** MIME type for a raw `ArrayBuffer`/`Uint8Array` input (a `Blob`/`URL` carries its own). */
|
|
256
|
+
mimeType?: string;
|
|
257
|
+
/** Document name sent to the binding; defaults to the URL's last path segment, else `"document"`. */
|
|
258
|
+
name?: string;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* What `ctx.ai.toMarkdown(...)` resolves to — the first (and only) converted
|
|
262
|
+
* document, normalised across the binding's single/array return.
|
|
263
|
+
* @experimental
|
|
264
|
+
*/
|
|
265
|
+
interface AiToMarkdownResult {
|
|
266
|
+
markdown: string;
|
|
267
|
+
/** The detected mime type of the source document, when the binding reports it. */
|
|
268
|
+
mimeType?: string;
|
|
269
|
+
/** Page count, when the binding reports it. */
|
|
270
|
+
pages?: number;
|
|
271
|
+
/** The binding's estimated token count for the converted Markdown, when reported. */
|
|
272
|
+
tokens?: number;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* One generated image: base64 bytes (`base64`), a URL, or the provider's media
|
|
276
|
+
* type. Which fields are present depends on the provider.
|
|
277
|
+
* @experimental
|
|
278
|
+
*/
|
|
279
|
+
interface AiImage {
|
|
280
|
+
base64?: string;
|
|
281
|
+
mimeType?: string;
|
|
282
|
+
url?: string;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Input for `ctx.ai.images.generate(...)`.
|
|
286
|
+
* @experimental
|
|
287
|
+
*/
|
|
288
|
+
interface AiImageGenerateInput {
|
|
289
|
+
/** How many images to generate. Defaults to 1. */
|
|
290
|
+
count?: number;
|
|
291
|
+
/**
|
|
292
|
+
* A model id: a Workers AI image model (`@cf/black-forest-labs/flux-1-schnell`),
|
|
293
|
+
* or an `"openai/<model>"` / `"google/<model>"` slug routed through the AI
|
|
294
|
+
* Gateway. Any other slug throws `AI_UNKNOWN_IMAGE_MODEL`.
|
|
295
|
+
*/
|
|
296
|
+
model: string;
|
|
297
|
+
prompt: string;
|
|
298
|
+
/** Seed for reproducible generation, for providers that support it. */
|
|
299
|
+
seed?: number;
|
|
300
|
+
/** Image size as `"<width>x<height>"`, e.g. `"1024x1024"`. */
|
|
301
|
+
size?: `${number}x${number}`;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* What `ctx.ai.images.generate(...)` resolves to.
|
|
305
|
+
* @experimental
|
|
306
|
+
*/
|
|
307
|
+
interface AiImageGenerateResult {
|
|
308
|
+
images: AiImage[];
|
|
309
|
+
/** The model id the caller asked for (the unmodified `input.model`). */
|
|
310
|
+
model: string;
|
|
311
|
+
}
|
|
201
312
|
/**
|
|
202
313
|
* A Workers AI provider instance — the value returned by `createWorkersAI(...)`.
|
|
203
314
|
* Calling it with a model id yields an AI SDK {@link LanguageModel}; the
|
|
@@ -447,6 +558,16 @@ type EmbeddingModelInput = EmbeddingModel | string;
|
|
|
447
558
|
interface LunoraAi {
|
|
448
559
|
/** Resolve an {@link EmbeddingModel}: a string → Workers AI, an object → passthrough. */
|
|
449
560
|
embeddingModel: (model?: EmbeddingModelInput) => EmbeddingModel;
|
|
561
|
+
/**
|
|
562
|
+
* Generate images. A Workers AI image model id (`@cf/…`) runs through the
|
|
563
|
+
* binding (and the AI Gateway when configured); an `"openai/<model>"` or
|
|
564
|
+
* `"google/<model>"` slug routes to that provider's image endpoint through
|
|
565
|
+
* the AI Gateway. Any other slug throws `AI_UNKNOWN_IMAGE_MODEL`; a
|
|
566
|
+
* Workers AI id without a binding throws the same directed error `run` does.
|
|
567
|
+
*/
|
|
568
|
+
images: {
|
|
569
|
+
generate: (input: AiImageGenerateInput) => Promise<AiImageGenerateResult>;
|
|
570
|
+
};
|
|
450
571
|
/**
|
|
451
572
|
* Resolve a {@link LanguageModel}: a `@cf/…` id → Workers AI, a
|
|
452
573
|
* `"<provider>/<model>"` slug or `dynamic/<route>` → Cloudflare AI Gateway
|
|
@@ -462,6 +583,13 @@ interface LunoraAi {
|
|
|
462
583
|
* supplied.
|
|
463
584
|
*/
|
|
464
585
|
run: (model: string, inputs: Record<string, unknown>, options?: AiRunOptions) => Promise<unknown>;
|
|
586
|
+
/**
|
|
587
|
+
* Convert a document (PDF, Office file, HTML, image) to Markdown through the
|
|
588
|
+
* binding's Markdown Conversion API (beta). Accepts a `Blob`, raw bytes or a
|
|
589
|
+
* `URL` (fetched first). Throws if no binding was supplied, or if the
|
|
590
|
+
* runtime's binding has no `toMarkdown()`.
|
|
591
|
+
*/
|
|
592
|
+
toMarkdown: (input: AiToMarkdownInput, options?: AiToMarkdownOptions) => Promise<AiToMarkdownResult>;
|
|
465
593
|
/**
|
|
466
594
|
* Search the web through the binding's Web Search API (beta) to ground a
|
|
467
595
|
* response in live information. Billed to a stored provider key or AI Gateway
|
|
@@ -479,4 +607,4 @@ interface LunoraAi {
|
|
|
479
607
|
*/
|
|
480
608
|
workersai: WorkersAiProviderLike;
|
|
481
609
|
}
|
|
482
|
-
export { AiBindingLike as A, EmbeddingModelInput as E, LunoraAi as L, ModelInput as M, ResolvedAiGateway as R, WorkersAiProviderLike as W, LunoraAiOptions as a,
|
|
610
|
+
export { AiBindingLike as A, AiTelemetry as B, AiTracer as C, AiWebSearchOptions as D, EmbeddingModelInput as E, AiWebSearchResult as F, buildAiGatewayMetadataFields as G, readAiGatewayEnvTags as H, resolveAiGateway as I, LunoraAi as L, ModelInput as M, ResolvedAiGateway as R, WorkersAiProviderLike as W, LunoraAiOptions as a, AiImage as b, AiImageGenerateInput as c, AiImageGenerateResult as d, AiToMarkdownInput as e, AiToMarkdownOptions as f, AiToMarkdownResult as g, AiWebSearchItem as h, AiWebSearchProvider as i, AI_DEFAULT_EMBEDDING_MODEL_ENV as j, AI_DEFAULT_MODEL_ENV as k, AI_GATEWAY_ACCOUNT_ID_ENV as l, AI_GATEWAY_ID_ENV as m, AI_GATEWAY_METADATA_MAX_KEYS as n, AI_GATEWAY_TAGS_ENV as o, AI_GATEWAY_TOKEN_ENV as p, AI_PROXY_TOKEN_ENV as q, AI_PROXY_URL_ENV as r, AiGatewayMetadata as s, AiGatewayOptions as t, AiMarkdownConversionResult as u, AiMarkdownDocument as v, AiMetrics as w, AiModelOptions as x, AiRunOptions as y, AiSpan as z };
|
|
@@ -188,6 +188,14 @@ declare const resolveAiGateway: (env: Record<string, unknown>, metadata?: AiGate
|
|
|
188
188
|
*/
|
|
189
189
|
interface AiBindingLike {
|
|
190
190
|
run: (model: string, inputs: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
|
|
191
|
+
/**
|
|
192
|
+
* Markdown Conversion API (beta) — converts a document (PDF, Office file,
|
|
193
|
+
* HTML, image) to Markdown. Optional because runtimes and test doubles that
|
|
194
|
+
* predate it have no such method; `ctx.ai.toMarkdown` reports that instead
|
|
195
|
+
* of a bare `TypeError`. Mirrors `env.AI.toMarkdown(files, options)` at
|
|
196
|
+
* https://developers.cloudflare.com/workers-ai/features/markdown-conversion/.
|
|
197
|
+
*/
|
|
198
|
+
toMarkdown?: (files: AiMarkdownDocument | AiMarkdownDocument[], options?: Record<string, unknown>) => Promise<AiMarkdownConversionResult | AiMarkdownConversionResult[]>;
|
|
191
199
|
/**
|
|
192
200
|
* Web Search API (beta). Optional because runtimes and test doubles that
|
|
193
201
|
* predate it have no such method; `ctx.ai.websearch` reports that instead of
|
|
@@ -198,6 +206,109 @@ interface AiBindingLike {
|
|
|
198
206
|
query: string;
|
|
199
207
|
}) => Promise<Response>;
|
|
200
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* One document handed to `ctx.ai.toMarkdown(...)`: the Workers AI
|
|
211
|
+
* `MarkdownDocument` shape (a name plus a Blob carrying the bytes).
|
|
212
|
+
* @experimental
|
|
213
|
+
*/
|
|
214
|
+
interface AiMarkdownDocument {
|
|
215
|
+
blob: Blob;
|
|
216
|
+
name: string;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* One Workers AI `ConversionResult`. `data` and `tokens` are present only when
|
|
220
|
+
* `format` is `"markdown"` (or `"text"`); an `"error"` result carries `error`
|
|
221
|
+
* instead. `pages` is not in the current binding contract — carried here so a
|
|
222
|
+
* binding that starts reporting it passes through unchanged.
|
|
223
|
+
* @experimental
|
|
224
|
+
*/
|
|
225
|
+
interface AiMarkdownConversionResult {
|
|
226
|
+
data?: string;
|
|
227
|
+
error?: string;
|
|
228
|
+
format: "error" | "markdown" | "text";
|
|
229
|
+
id?: string;
|
|
230
|
+
/** The detected document mime type (spelled `mimetype` by the binding). */
|
|
231
|
+
mimetype?: string;
|
|
232
|
+
name?: string;
|
|
233
|
+
/** Page count, when the binding reports it. */
|
|
234
|
+
pages?: number;
|
|
235
|
+
tokens?: number;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* What `ctx.ai.toMarkdown(...)` accepts: a `Blob`, raw bytes, or a `URL` that
|
|
239
|
+
* is fetched first.
|
|
240
|
+
* @experimental
|
|
241
|
+
*/
|
|
242
|
+
type AiToMarkdownInput = ArrayBuffer | Blob | Uint8Array | URL;
|
|
243
|
+
/**
|
|
244
|
+
* Options for `ctx.ai.toMarkdown(...)`.
|
|
245
|
+
* @experimental
|
|
246
|
+
*/
|
|
247
|
+
interface AiToMarkdownOptions {
|
|
248
|
+
/** Conversion options forwarded verbatim to the binding's `toMarkdown(files, options)`. */
|
|
249
|
+
conversionOptions?: Record<string, unknown>;
|
|
250
|
+
/**
|
|
251
|
+
* Byte cap for a `URL`/raw-bytes input, guarding against an accidental huge
|
|
252
|
+
* download. Defaults to 20 MiB. A `Blob` is checked against the same cap.
|
|
253
|
+
*/
|
|
254
|
+
maxBytes?: number;
|
|
255
|
+
/** MIME type for a raw `ArrayBuffer`/`Uint8Array` input (a `Blob`/`URL` carries its own). */
|
|
256
|
+
mimeType?: string;
|
|
257
|
+
/** Document name sent to the binding; defaults to the URL's last path segment, else `"document"`. */
|
|
258
|
+
name?: string;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* What `ctx.ai.toMarkdown(...)` resolves to — the first (and only) converted
|
|
262
|
+
* document, normalised across the binding's single/array return.
|
|
263
|
+
* @experimental
|
|
264
|
+
*/
|
|
265
|
+
interface AiToMarkdownResult {
|
|
266
|
+
markdown: string;
|
|
267
|
+
/** The detected mime type of the source document, when the binding reports it. */
|
|
268
|
+
mimeType?: string;
|
|
269
|
+
/** Page count, when the binding reports it. */
|
|
270
|
+
pages?: number;
|
|
271
|
+
/** The binding's estimated token count for the converted Markdown, when reported. */
|
|
272
|
+
tokens?: number;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* One generated image: base64 bytes (`base64`), a URL, or the provider's media
|
|
276
|
+
* type. Which fields are present depends on the provider.
|
|
277
|
+
* @experimental
|
|
278
|
+
*/
|
|
279
|
+
interface AiImage {
|
|
280
|
+
base64?: string;
|
|
281
|
+
mimeType?: string;
|
|
282
|
+
url?: string;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Input for `ctx.ai.images.generate(...)`.
|
|
286
|
+
* @experimental
|
|
287
|
+
*/
|
|
288
|
+
interface AiImageGenerateInput {
|
|
289
|
+
/** How many images to generate. Defaults to 1. */
|
|
290
|
+
count?: number;
|
|
291
|
+
/**
|
|
292
|
+
* A model id: a Workers AI image model (`@cf/black-forest-labs/flux-1-schnell`),
|
|
293
|
+
* or an `"openai/<model>"` / `"google/<model>"` slug routed through the AI
|
|
294
|
+
* Gateway. Any other slug throws `AI_UNKNOWN_IMAGE_MODEL`.
|
|
295
|
+
*/
|
|
296
|
+
model: string;
|
|
297
|
+
prompt: string;
|
|
298
|
+
/** Seed for reproducible generation, for providers that support it. */
|
|
299
|
+
seed?: number;
|
|
300
|
+
/** Image size as `"<width>x<height>"`, e.g. `"1024x1024"`. */
|
|
301
|
+
size?: `${number}x${number}`;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* What `ctx.ai.images.generate(...)` resolves to.
|
|
305
|
+
* @experimental
|
|
306
|
+
*/
|
|
307
|
+
interface AiImageGenerateResult {
|
|
308
|
+
images: AiImage[];
|
|
309
|
+
/** The model id the caller asked for (the unmodified `input.model`). */
|
|
310
|
+
model: string;
|
|
311
|
+
}
|
|
201
312
|
/**
|
|
202
313
|
* A Workers AI provider instance — the value returned by `createWorkersAI(...)`.
|
|
203
314
|
* Calling it with a model id yields an AI SDK {@link LanguageModel}; the
|
|
@@ -447,6 +558,16 @@ type EmbeddingModelInput = EmbeddingModel | string;
|
|
|
447
558
|
interface LunoraAi {
|
|
448
559
|
/** Resolve an {@link EmbeddingModel}: a string → Workers AI, an object → passthrough. */
|
|
449
560
|
embeddingModel: (model?: EmbeddingModelInput) => EmbeddingModel;
|
|
561
|
+
/**
|
|
562
|
+
* Generate images. A Workers AI image model id (`@cf/…`) runs through the
|
|
563
|
+
* binding (and the AI Gateway when configured); an `"openai/<model>"` or
|
|
564
|
+
* `"google/<model>"` slug routes to that provider's image endpoint through
|
|
565
|
+
* the AI Gateway. Any other slug throws `AI_UNKNOWN_IMAGE_MODEL`; a
|
|
566
|
+
* Workers AI id without a binding throws the same directed error `run` does.
|
|
567
|
+
*/
|
|
568
|
+
images: {
|
|
569
|
+
generate: (input: AiImageGenerateInput) => Promise<AiImageGenerateResult>;
|
|
570
|
+
};
|
|
450
571
|
/**
|
|
451
572
|
* Resolve a {@link LanguageModel}: a `@cf/…` id → Workers AI, a
|
|
452
573
|
* `"<provider>/<model>"` slug or `dynamic/<route>` → Cloudflare AI Gateway
|
|
@@ -462,6 +583,13 @@ interface LunoraAi {
|
|
|
462
583
|
* supplied.
|
|
463
584
|
*/
|
|
464
585
|
run: (model: string, inputs: Record<string, unknown>, options?: AiRunOptions) => Promise<unknown>;
|
|
586
|
+
/**
|
|
587
|
+
* Convert a document (PDF, Office file, HTML, image) to Markdown through the
|
|
588
|
+
* binding's Markdown Conversion API (beta). Accepts a `Blob`, raw bytes or a
|
|
589
|
+
* `URL` (fetched first). Throws if no binding was supplied, or if the
|
|
590
|
+
* runtime's binding has no `toMarkdown()`.
|
|
591
|
+
*/
|
|
592
|
+
toMarkdown: (input: AiToMarkdownInput, options?: AiToMarkdownOptions) => Promise<AiToMarkdownResult>;
|
|
465
593
|
/**
|
|
466
594
|
* Search the web through the binding's Web Search API (beta) to ground a
|
|
467
595
|
* response in live information. Billed to a stored provider key or AI Gateway
|
|
@@ -479,4 +607,4 @@ interface LunoraAi {
|
|
|
479
607
|
*/
|
|
480
608
|
workersai: WorkersAiProviderLike;
|
|
481
609
|
}
|
|
482
|
-
export { AiBindingLike as A, EmbeddingModelInput as E, LunoraAi as L, ModelInput as M, ResolvedAiGateway as R, WorkersAiProviderLike as W, LunoraAiOptions as a,
|
|
610
|
+
export { AiBindingLike as A, AiTelemetry as B, AiTracer as C, AiWebSearchOptions as D, EmbeddingModelInput as E, AiWebSearchResult as F, buildAiGatewayMetadataFields as G, readAiGatewayEnvTags as H, resolveAiGateway as I, LunoraAi as L, ModelInput as M, ResolvedAiGateway as R, WorkersAiProviderLike as W, LunoraAiOptions as a, AiImage as b, AiImageGenerateInput as c, AiImageGenerateResult as d, AiToMarkdownInput as e, AiToMarkdownOptions as f, AiToMarkdownResult as g, AiWebSearchItem as h, AiWebSearchProvider as i, AI_DEFAULT_EMBEDDING_MODEL_ENV as j, AI_DEFAULT_MODEL_ENV as k, AI_GATEWAY_ACCOUNT_ID_ENV as l, AI_GATEWAY_ID_ENV as m, AI_GATEWAY_METADATA_MAX_KEYS as n, AI_GATEWAY_TAGS_ENV as o, AI_GATEWAY_TOKEN_ENV as p, AI_PROXY_TOKEN_ENV as q, AI_PROXY_URL_ENV as r, AiGatewayMetadata as s, AiGatewayOptions as t, AiMarkdownConversionResult as u, AiMarkdownDocument as v, AiMetrics as w, AiModelOptions as x, AiRunOptions as y, AiSpan as z };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{estimateImageCost as d,estimateModelCost as f}from"./DEFAULT_MODEL_PRICES-C5oaHRiN.mjs";const g=t=>typeof t=="number"&&Number.isFinite(t)&&t>=0?t:void 0,p=t=>{if(!(typeof t!="object"||t===null)){for(const o of Object.values(t))if(typeof o=="object"&&o!==null){const{cost:e}=o;if(typeof e=="number"&&Number.isFinite(e))return e}}},b=t=>{const o=t.modelId;return typeof o=="string"&&o.length>0?o:void 0},l=(t,o,e,s)=>{const n=g(o.usage?.inputTokens?.total),u=g(o.usage?.outputTokens?.total),c=p(o.providerMetadata),i=c??f(t,{inputTokens:n,outputTokens:u}),a=c===void 0?"estimated":"provider",r={"gen_ai.request.model":t??"unknown"};n!==void 0&&(e?.setAttribute("gen_ai.usage.input_tokens",n),s?.count("gen_ai.usage.input_tokens",n,r)),u!==void 0&&(e?.setAttribute("gen_ai.usage.output_tokens",u),s?.count("gen_ai.usage.output_tokens",u,r)),i!==void 0&&(e?.setAttributes({"gen_ai.usage.cost":i,"lunora.usage.cost.source":a}),s?.count("gen_ai.usage.cost",i,{...r,"lunora.usage.cost.source":a}))},m=(t,o,e)=>{const s=d(t,o);s!==void 0&&e?.count("gen_ai.usage.cost",s,{"gen_ai.request.model":t,"lunora.usage.cost.source":"estimated"})};export{m as a,b as m,l as r};
|
package/dist/rag/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Tool } from 'ai';
|
|
2
|
-
import { E as EmbeddingModelInput, L as LunoraAi } from "../packem_shared/types.d-
|
|
2
|
+
import { E as EmbeddingModelInput, L as LunoraAi } from "../packem_shared/types.d-BG8Olmuo.mjs";
|
|
3
3
|
/**
|
|
4
4
|
* Built-in fixed-window chunker: split into `size`-char windows overlapping by
|
|
5
5
|
* `overlap` chars. Deliberately simple and deterministic — the zero-config
|
package/dist/rag/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Tool } from 'ai';
|
|
2
|
-
import { E as EmbeddingModelInput, L as LunoraAi } from "../packem_shared/types.d-
|
|
2
|
+
import { E as EmbeddingModelInput, L as LunoraAi } from "../packem_shared/types.d-BG8Olmuo.js";
|
|
3
3
|
/**
|
|
4
4
|
* Built-in fixed-window chunker: split into `size`-char windows overlapping by
|
|
5
5
|
* `overlap` chars. Deliberately simple and deterministic — the zero-config
|
package/dist/rag/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as o}from"../packem_shared/fixedWindowChunks-C461ahRE.mjs";import{markdownChunker as a,sentenceChunker as f,tokenChunker as n}from"../packem_shared/markdownChunker-iW8V4klY.mjs";import{default as x}from"../packem_shared/defineRag-
|
|
1
|
+
import{default as o}from"../packem_shared/fixedWindowChunks-C461ahRE.mjs";import{markdownChunker as a,sentenceChunker as f,tokenChunker as n}from"../packem_shared/markdownChunker-iW8V4klY.mjs";import{default as x}from"../packem_shared/defineRag-BOS3IQeP.mjs";import{contentHash as p,guessMimeTypeFromExtension as i}from"../packem_shared/contentHash-BIn6ECP8.mjs";import{hybridRank as d}from"../packem_shared/hybridRank-B4skyCLx.mjs";import{default as k}from"../packem_shared/bm25LexicalStore-DMUzAL0O.mjs";import{default as h}from"../packem_shared/matchesMetadataFilter-BbIOyA5g.mjs";import{batchReranker as g,scoreReranker as C}from"../packem_shared/batchReranker-Bc38FBLH.mjs";import{defineRagSource as E}from"../packem_shared/defineRagSource-Q3f3niU8.mjs";import{sqlLexicalStore as T}from"../packem_shared/sqlLexicalStore-4C_cIwef.mjs";import{sqliteVectorStore as y}from"../packem_shared/sqliteVectorStore-f24ySqtJ.mjs";import{ragSyncTriggers as q}from"../packem_shared/ragSyncTriggers-D933fCwt.mjs";import{VECTORIZE_CAPABILITIES as A,vectorizeStore as F}from"../packem_shared/VECTORIZE_CAPABILITIES-l9ceRO5j.mjs";export{A as VECTORIZE_CAPABILITIES,g as batchReranker,k as bm25LexicalStore,p as contentHash,x as defineRag,E as defineRagSource,o as fixedWindowChunks,i as guessMimeTypeFromExtension,d as hybridRank,a as markdownChunker,h as matchesMetadataFilter,q as ragSyncTriggers,C as scoreReranker,f as sentenceChunker,T as sqlLexicalStore,y as sqliteVectorStore,n as tokenChunker,F as vectorizeStore};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/ai",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.130",
|
|
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",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@ai-sdk/anthropic": "4.0.71",
|
|
61
|
+
"@ai-sdk/google": "4.0.87",
|
|
61
62
|
"@ai-sdk/openai": "4.0.83",
|
|
62
63
|
"@ai-sdk/provider-utils": "5.0.53",
|
|
63
64
|
"@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const p=/^(.*)-\d{4}-\d{2}-\d{2}$/u,a={"@cf/baai/bge-base-en-v1.5":{input:.067},"@cf/baai/bge-large-en-v1.5":{input:.204},"@cf/baai/bge-m3":{input:.012},"@cf/baai/bge-small-en-v1.5":{input:.02},"@cf/meta/llama-3.1-8b-instruct":{input:.28,output:.83},"@cf/meta/llama-3.3-70b-instruct-fp8-fast":{input:.29,output:2.25},"text-embedding-3-large":{input:.13},"text-embedding-3-small":{input:.02},"gpt-4o":{input:2.5,output:10},"gpt-4o-mini":{input:.15,output:.6},"gpt-5":{input:1.25,output:10}},c=e=>{const t=e.trim(),i=t.indexOf("/@"),n=i===-1?t:t.slice(i+1),u=n.lastIndexOf("/");return(u!==-1&&!n.startsWith("@")?[n,n.slice(u+1)]:[n]).flatMap(o=>{const r=p.exec(o)?.[1];return r===void 0?[o]:[o,r]})},b=(e,t=a)=>{for(const i of c(e))if(Object.hasOwn(t,i))return t[i]},d=(e,t,i)=>{if(e===void 0||e.length===0)return;const n=b(e,i);if(n===void 0)return;const u=Number.isFinite(t.inputTokens)?Math.max(0,t.inputTokens):0,s=Number.isFinite(t.outputTokens)?Math.max(0,t.outputTokens):0;if(u<=0&&s<=0)return;const o=(u*n.input+s*(n.output??0))/1e6;return Number.isFinite(o)?o:void 0};export{a as DEFAULT_MODEL_PRICES,d as estimateModelCost,b as lookupModelPrice};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./DEFAULT_MODEL_PRICES-CIJLs1Ah.mjs";import{LunoraError as g}from"@lunora/errors";import{buildAiGatewayMetadataFields as W,readEnv as A,AI_DEFAULT_MODEL_ENV as S,AI_DEFAULT_EMBEDDING_MODEL_ENV as x,AI_PROXY_URL_ENV as b,AI_PROXY_TOKEN_ENV as C,readAiGatewayEnvTags as X,AI_GATEWAY_ID_ENV as z,warnIgnoredBindingToken as Z}from"./AI_DEFAULT_EMBEDDING_MODEL_ENV-IwcHZiKj.mjs";import{m as J,r as O}from"./usage-GPnTSqR8.mjs";const Q={setAttribute:()=>{},setAttributes:()=>{}},G=async(e,t)=>t(G,Q),ee=(e,{metrics:t,trace:a=G})=>{const s={"gen_ai.operation.name":"chat","gen_ai.request.model":e};return{wrapGenerate:async({doGenerate:r})=>a("ai.generate",async(d,l)=>{const o=await r();return O(e,o,l,t),o},s),wrapStream:async({doStream:r})=>{const d=(c,w)=>{const E=c.stream.getReader();let h={};return{...c,stream:new ReadableStream({cancel:async v=>{w({outcome:h}),await E.cancel(v)},pull:async v=>{let f;try{f=await E.read()}catch(y){w({error:y,failed:!0,outcome:h}),v.error(y);return}if(f.done){w({outcome:h}),v.close();return}f.value.type==="finish"&&(h={providerMetadata:f.value.providerMetadata,usage:f.value.usage}),v.enqueue(f.value)}})}},l=Promise.withResolvers(),o=Promise.withResolvers();return a("ai.stream",async(c,w)=>{l.resolve(d(await r(),o.resolve));const E=await o.promise;if(O(e,E.outcome,w,t),E.failed===!0)throw E.error},s).catch(l.reject),l.promise}}},U=(e,t,a)=>{if(t===void 0||typeof e=="string")return e;const s=ee(a??J(e)??"unknown",t);let r;const d=async()=>(r??=import("ai").then(({wrapLanguageModel:o})=>o({middleware:s,model:e}),o=>{throw r=void 0,o}),r),l=e;return{doGenerate:async o=>d().then(c=>c.doGenerate(o)),doStream:async o=>d().then(c=>c.doStream(o)),modelId:l.modelId,provider:l.provider,specificationVersion:"v4",get supportedUrls(){return d().then(o=>o.supportedUrls)}}};let N;const B=()=>(N??=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 N=void 0,e}),N),$=(e,t,a)=>({doGenerate:async s=>a().then(r=>r.doGenerate(s)),doStream:async s=>a().then(r=>r.doStream(s)),modelId:e,provider:t,specificationVersion:"v4",get supportedUrls(){return a().then(s=>s.supportedUrls)}}),te={maxEmbeddingsPerCall:3e3,supportsParallelCalls:!0},V=(e,t,a,s)=>({doEmbed:async r=>a().then(d=>d.doEmbed(r)),get maxEmbeddingsPerCall(){return s?.maxEmbeddingsPerCall??a().then(r=>r.maxEmbeddingsPerCall)},modelId:e,provider:t,specificationVersion:"v4",get supportsParallelCalls(){return s?.supportsParallelCalls??a().then(r=>r.supportsParallelCalls)}});let R;const k=()=>(R??=import("workers-ai-provider").catch(e=>{throw R=void 0,e}),R),_=e=>!e.startsWith("@")&&e.includes("/"),re=e=>{const t=(a,s)=>$(a,"workersai.chat",async()=>e(a).then(r=>r(a,s)));return t.textEmbeddingModel=a=>V(a,"workersai.embedding",async()=>e(a).then(s=>s.textEmbeddingModel?.(a)),_(a)?void 0:te),t},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 ${b} to an OpenAI-compatible proxy for "<provider>/<model>" slugs.`)},F=()=>I("this model id");F.textEmbeddingModel=()=>I("this embedding model id");const ae=3040,ne=/^(?:[A-Z]\w*:\s*)?3040\s*:/iu,oe=e=>e?.code===ae||e instanceof Error&&ne.test(e.message),se="default",ie=new Map([[401,"UNAUTHORIZED"],[403,"FORBIDDEN"],[404,"NOT_FOUND"],[408,"SERVICE_UNAVAILABLE"],[429,"RATE_LIMITED"]]),de=e=>ie.get(e)??(e>=400&&e<500?"BAD_REQUEST":"SERVICE_UNAVAILABLE"),ce=300,le=new Set(["127.0.0.1","[::1]","localhost"]),ue=e=>{const t=A(e,b);if(t===void 0)return;const a=A(e,C),s=URL.canParse(t)?new URL(t):void 0;let r;if(s===void 0?r=`${b} is not a valid URL`:a!==void 0&&s.protocol!=="https:"&&!le.has(s.hostname)&&(r=`${b} (${s.origin}) is not HTTPS, so ${C} would travel in cleartext — use an https:// URL`),r!==void 0){const o=()=>{throw new g("INTERNAL",`@lunora/ai: ${r}`)};return{chat:o,embedding:o}}let d;const l=()=>(d??=B().then(({createOpenAI:o})=>o({apiKey:a??"",baseURL:t,name:"lunora-proxy"}),o=>{throw d=void 0,o}),d);return{chat:o=>$(o,"lunora-proxy",async()=>l().then(c=>c.chat(o))),embedding:o=>V(o,"lunora-proxy",async()=>l().then(c=>c.embedding(o)))}},we=(e,t)=>{const a=e===void 0?void 0:X(e);return a===void 0?t:{...t,tags:{...a,...t?.tags}}},he=(e,t,a)=>{const s=W(a);if(e!==void 0)return s!==void 0&&e.metadata===void 0?{...e,metadata:s}:e;if(t===void 0)return;const r=A(t,z);if(r!==void 0)return Z(t),s===void 0?{id:r}:{id:r,metadata:s}},be=e=>{const{binding:t,defaultEmbeddingModel:a,defaultModel:s,env:r,gateway:d,metadata:l,provider:o,telemetry:c}=e,w=ue(r),E=we(r,l),h=he(d,r,E),v=d?.metadata===void 0?W(E):void 0,f=s??A(r,S),y=a??A(r,x);let T;const j=()=>(T??=k().then(({createWorkersAI:n})=>n({binding:t,gateway:h})).catch(n=>{throw T=void 0,n}),T);let M;const H=()=>(M??=Promise.all([k(),B()]).then(([{createWorkersAI:n},{plugins:i}])=>n({binding:t,gateway:h,providers:i})).catch(n=>{throw M=void 0,n}),M),m=o??(t?re(async n=>_(n)?H():j()):F),K=(n,i)=>_(n)?w!==void 0?w.chat(n):v===void 0?m(n):m(n,{metadata:v}):i?.rejectIfBusy===void 0?m(n):m(n,{rejectIfBusy:i.rejectIfBusy}),Y=(n,i)=>{const u=n??f;if(u===void 0||u==="")throw new g("INTERNAL",`@lunora/ai: no model supplied and no default configured — pass a model id, or set ${S} in the Worker env (wrangler \`vars\` / \`.dev.vars\`)`);return typeof u=="string"?U(K(u,i),c,u):U(u,c)},q=n=>{if(w!==void 0&&_(n))return w.embedding(n);const i=m.textEmbeddingModel;if(typeof i!="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 i.call(m,n)};return{embeddingModel:n=>{if(typeof n=="object")return n;const i=n??y;if(!i)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 ${x} in the Worker env (wrangler \`vars\` / \`.dev.vars\`)`);return q(i)},model:Y,run:async(n,i,u)=>{if(!t)return I("ai.run");const L=h!==void 0&&u?.gateway===void 0?{...u,gateway:h}:u;try{return await t.run(n,i,L)}catch(p){throw oe(p)?new g("RATE_LIMITED",`@lunora/ai: Workers AI has no free capacity for ${n} (error 3040) — retry later`,{cause:p}):p}},websearch:async(n,i)=>{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:u,...L}=i??{},p=await t.websearch({...L,gatewayId:u??h?.id??se,query:n});if(!p.ok){const P=(await p.text().catch(()=>"")).slice(0,ce);throw new g(de(p.status),`@lunora/ai: web search failed with HTTP ${String(p.status)}${P===""?"":`: ${P}`}`)}const D=await p.json().catch(()=>{});if(!Array.isArray(D?.items))throw new g("INTERNAL","@lunora/ai: web search returned a body that is not JSON with an `items` array");return D},workersai:m}};export{be as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{estimateModelCost as d}from"./DEFAULT_MODEL_PRICES-CIJLs1Ah.mjs";const a=t=>typeof t=="number"&&Number.isFinite(t)&&t>=0?t:void 0,f=t=>{if(!(typeof t!="object"||t===null)){for(const o of Object.values(t))if(typeof o=="object"&&o!==null){const{cost:e}=o;if(typeof e=="number"&&Number.isFinite(e))return e}}},b=t=>{const o=t.modelId;return typeof o=="string"&&o.length>0?o:void 0},l=(t,o,e,u)=>{const s=a(o.usage?.inputTokens?.total),n=a(o.usage?.outputTokens?.total),c=f(o.providerMetadata),i=c??d(t,{inputTokens:s,outputTokens:n}),g=c===void 0?"estimated":"provider",r={"gen_ai.request.model":t??"unknown"};s!==void 0&&(e?.setAttribute("gen_ai.usage.input_tokens",s),u?.count("gen_ai.usage.input_tokens",s,r)),n!==void 0&&(e?.setAttribute("gen_ai.usage.output_tokens",n),u?.count("gen_ai.usage.output_tokens",n,r)),i!==void 0&&(e?.setAttributes({"gen_ai.usage.cost":i,"lunora.usage.cost.source":g}),u?.count("gen_ai.usage.cost",i,{...r,"lunora.usage.cost.source":g}))};export{b as m,l as r};
|