feat(ts-sdk): add FastEmbed embedding provider (#5862)

Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
This commit is contained in:
Rod Boev
2026-07-06 21:56:11 +05:30
committed by GitHub
co-authored by kartik-mem0
parent 03b41ab00f
commit fec7cdf118
12 changed files with 680 additions and 7 deletions
+1
View File
@@ -122,6 +122,7 @@
"@types/pg": "8.11.0",
"better-sqlite3": "^12.6.2",
"cloudflare": "^4.2.0",
"fastembed": "^2.1.0",
"groq-sdk": "0.3.0",
"ollama": "^0.5.14",
"pg": "8.11.3",
+340
View File
@@ -80,6 +80,9 @@ importers:
compromise:
specifier: ^14.0.0
version: 14.15.1
fastembed:
specifier: ^2.1.0
version: 2.1.0
groq-sdk:
specifier: 0.3.0
version: 0.3.0
@@ -153,6 +156,27 @@ packages:
'@anthropic-ai/sdk@0.40.1':
resolution: {integrity: sha512-DJMWm8lTEM9Lk/MSFL+V+ugF7jKOn0M2Ujvb5fN8r2nY14aHbGPZ1k6sgjL+tpJ3VuOGJNG+4R83jEpOuYPv8w==}
'@anush008/tokenizers-darwin-universal@0.0.0':
resolution: {integrity: sha512-SACpWEooTjFX89dFKRVUhivMxxcZRtA3nJGVepdLyrwTkQ1TZQ8581B5JoXp0TcTMHfgnDaagifvVoBiFEdNCQ==}
engines: {node: '>= 10'}
os: [darwin]
'@anush008/tokenizers-linux-x64-gnu@0.0.0':
resolution: {integrity: sha512-TLjByOPWUEq51L3EJkS+slyH57HKJ7lAz/aBtEt7TIPq4QsE2owOPGovByOLIq1x5Wgh9b+a4q2JasrEFSDDhg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@anush008/tokenizers-win32-x64-msvc@0.0.0':
resolution: {integrity: sha512-/5kP0G96+Cr6947F0ZetXnmL31YCaN15dbNbh2NHg7TXXRwfqk95+JtPP5Q7v4jbR2xxAmuseBqB4H/V7zKWuw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@anush008/tokenizers@0.0.0':
resolution: {integrity: sha512-IQD9wkVReKAhsEAbDjh/0KrBGTEXelqZLpOBRDaIRvlzZ9sjmUP+gKbpvzyJnei2JHQiE8JAgj7YcNloINbGBw==}
engines: {node: '>= 10'}
'@aws-crypto/crc32@5.2.0':
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
engines: {node: '>=16.0.0'}
@@ -677,6 +701,20 @@ packages:
'@modelcontextprotocol/sdk':
optional: true
'@huggingface/blake3-jit@0.0.2':
resolution: {integrity: sha512-Bq7B5qabyjrJfhBsl85Jd2QBtf+HzRD7h7A9GfN2lzrrsABhOa5evVPgzoCTxR7Ub0QFj7YDK1YkYRWBU25+2w==}
'@huggingface/hub@2.13.2':
resolution: {integrity: sha512-I1Pbn9UcBHCt0M/SbZkKzYr3JVnbJp4YIFv74eVRH2Y22yT9Jpm6DtfM74tcjNLbC7SyFZ433znUw0szdFd4Fg==}
engines: {node: '>=18'}
hasBin: true
'@huggingface/tasks@0.21.21':
resolution: {integrity: sha512-6cHtkeMvgbudI6p4+gPP/+xvcAG2uumF+ntojCSNnHy5xcSzuHXCMJHzh+rlX0TSZ/QdDZ4uWkPLQikbphNyEw==}
'@huggingface/xetchunk-wasm@0.1.0':
resolution: {integrity: sha512-wWpp2qwPgf9kv1KLJjcDUk/OrpDOsFoQ3Qpz0U5LGn20csoymBf8eneOv6wm/GzPBzlWac1OYiR0aa1vT6aM2Q==}
'@iovalkey/commands@0.1.0':
resolution: {integrity: sha512-/B9W4qKSSITDii5nkBCHyPkIkAi+ealUtr1oqBJsLxjSRLka4pxun2VvMNSmcwgAMxgXtQfl0qRv7TE+udPJzg==}
@@ -684,6 +722,10 @@ packages:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
'@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
engines: {node: '>=18.0.0'}
'@istanbuljs/load-nyc-config@1.1.0':
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
engines: {node: '>=8'}
@@ -1434,6 +1476,10 @@ packages:
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
boolean@3.2.0:
resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==}
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
bowser@2.14.1:
resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==}
@@ -1535,6 +1581,14 @@ packages:
chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
engines: {node: '>=10'}
chownr@3.0.0:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
ci-info@3.9.0:
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
engines: {node: '>=8'}
@@ -1542,6 +1596,10 @@ packages:
cjs-module-lexer@1.4.3:
resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
cli-progress@3.12.0:
resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==}
engines: {node: '>=4'}
cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
@@ -1648,10 +1706,18 @@ packages:
resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==}
engines: {node: '>=18'}
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
define-lazy-prop@3.0.0:
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
engines: {node: '>=12'}
define-properties@1.2.1:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -1668,6 +1734,9 @@ packages:
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
engines: {node: '>=8'}
detect-node@2.1.0:
resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
diff-sequences@29.6.3:
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -1736,6 +1805,9 @@ packages:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
es6-error@4.1.1:
resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
esbuild@0.28.1:
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
engines: {node: '>=18'}
@@ -1749,6 +1821,10 @@ packages:
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
engines: {node: '>=8'}
escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
@@ -1790,6 +1866,9 @@ packages:
resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==}
hasBin: true
fastembed@2.1.0:
resolution: {integrity: sha512-oQkpcRHBppJ3+a3w9dU0uytSY0N1cnEa/iVMc8AXEd+tvT529GekOEFhNviJy89R3lvQXF6cdIMTXHj1Gi00xQ==}
fastq@1.20.1:
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
@@ -1858,6 +1937,10 @@ packages:
fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
fs-minipass@2.1.0:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'}
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@@ -1877,6 +1960,9 @@ packages:
resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==}
engines: {node: '>=18'}
gearhash-jit@1.0.2:
resolution: {integrity: sha512-UhzJL4KXSdqAKepy/tZwmi2Rcy0YMmtiC4DQS4SURCuIWdh8ECZtnXK2ePRMLigfB61hRKdLK/Vgg2bSw73izQ==}
generic-pool@3.9.0:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
@@ -1921,6 +2007,14 @@ packages:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
global-agent@3.0.0:
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
engines: {node: '>=10.0'}
globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
google-auth-library@10.7.0:
resolution: {integrity: sha512-QpTAbNJ36TliZLx3TTtahR8HG0hN9RllL1e3FymOvQSIKK8JmgV58H924ub2wa2DsS3ANjjP1Aw1N+Ramc8hqQ==}
engines: {node: '>=18'}
@@ -1956,6 +2050,9 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
@@ -2267,6 +2364,9 @@ packages:
resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
json-stringify-safe@5.0.1:
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
@@ -2388,6 +2488,10 @@ packages:
makeerror@1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
matcher@3.0.0:
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
engines: {node: '>=10'}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
@@ -2443,13 +2547,34 @@ packages:
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
minipass@3.3.6:
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
engines: {node: '>=8'}
minipass@5.0.0:
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
engines: {node: '>=8'}
minipass@7.1.3:
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
engines: {node: '>=16 || 14 >=14.17'}
minizlib@2.1.2:
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
engines: {node: '>= 8'}
minizlib@3.1.0:
resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
engines: {node: '>= 18'}
mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
hasBin: true
mlly@1.8.2:
resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}
@@ -2569,6 +2694,10 @@ packages:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
obuf@1.1.2:
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
@@ -2582,6 +2711,13 @@ packages:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
onnxruntime-common@1.21.0:
resolution: {integrity: sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ==}
onnxruntime-node@1.21.0:
resolution: {integrity: sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw==}
os: [win32, darwin, linux]
open@10.2.0:
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
engines: {node: '>=18'}
@@ -2807,6 +2943,10 @@ packages:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
progress@2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
@@ -2906,6 +3046,10 @@ packages:
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
hasBin: true
roarr@2.15.4:
resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
engines: {node: '>=8.0'}
rollup@4.61.1:
resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -2925,6 +3069,9 @@ packages:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
semver-compare@1.0.0:
resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@@ -2934,6 +3081,10 @@ packages:
engines: {node: '>=10'}
hasBin: true
serialize-error@7.0.1:
resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
engines: {node: '>=10'}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -2999,6 +3150,9 @@ packages:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
sprintf-js@1.1.3:
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
@@ -3087,6 +3241,15 @@ packages:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
tar@6.2.1:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
tar@7.5.19:
resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==}
engines: {node: '>=18'}
test-exclude@6.0.0:
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
engines: {node: '>=8'}
@@ -3200,6 +3363,10 @@ packages:
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
engines: {node: '>=4'}
type-fest@0.13.1:
resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
engines: {node: '>=10'}
type-fest@0.21.3:
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
engines: {node: '>=10'}
@@ -3353,6 +3520,10 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
yallist@5.0.0:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -3391,6 +3562,21 @@ snapshots:
transitivePeerDependencies:
- encoding
'@anush008/tokenizers-darwin-universal@0.0.0':
optional: true
'@anush008/tokenizers-linux-x64-gnu@0.0.0':
optional: true
'@anush008/tokenizers-win32-x64-msvc@0.0.0':
optional: true
'@anush008/tokenizers@0.0.0':
optionalDependencies:
'@anush008/tokenizers-darwin-universal': 0.0.0
'@anush008/tokenizers-linux-x64-gnu': 0.0.0
'@anush008/tokenizers-win32-x64-msvc': 0.0.0
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
@@ -4162,6 +4348,22 @@ snapshots:
- supports-color
- utf-8-validate
'@huggingface/blake3-jit@0.0.2': {}
'@huggingface/hub@2.13.2':
dependencies:
'@huggingface/tasks': 0.21.21
'@huggingface/xetchunk-wasm': 0.1.0
optionalDependencies:
cli-progress: 3.12.0
'@huggingface/tasks@0.21.21': {}
'@huggingface/xetchunk-wasm@0.1.0':
dependencies:
'@huggingface/blake3-jit': 0.0.2
gearhash-jit: 1.0.2
'@iovalkey/commands@0.1.0': {}
'@isaacs/cliui@8.0.2':
@@ -4173,6 +4375,10 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
'@isaacs/fs-minipass@4.0.1':
dependencies:
minipass: 7.1.3
'@istanbuljs/load-nyc-config@1.1.0':
dependencies:
camelcase: 5.3.1
@@ -5035,6 +5241,8 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
boolean@3.2.0: {}
bowser@2.14.1: {}
brace-expansion@1.1.15:
@@ -5134,10 +5342,19 @@ snapshots:
chownr@1.1.4: {}
chownr@2.0.0: {}
chownr@3.0.0: {}
ci-info@3.9.0: {}
cjs-module-lexer@1.4.3: {}
cli-progress@3.12.0:
dependencies:
string-width: 4.2.3
optional: true
cliui@8.0.1:
dependencies:
string-width: 4.2.3
@@ -5238,8 +5455,20 @@ snapshots:
bundle-name: 4.1.0
default-browser-id: 5.0.1
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.1
es-errors: 1.3.0
gopd: 1.2.0
define-lazy-prop@3.0.0: {}
define-properties@1.2.1:
dependencies:
define-data-property: 1.1.4
has-property-descriptors: 1.0.2
object-keys: 1.1.1
delayed-stream@1.0.0: {}
denque@2.1.0: {}
@@ -5248,6 +5477,8 @@ snapshots:
detect-newline@3.1.0: {}
detect-node@2.1.0: {}
diff-sequences@29.6.3: {}
diff@4.0.4: {}
@@ -5306,6 +5537,8 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.4
es6-error@4.1.1: {}
esbuild@0.28.1:
optionalDependencies:
'@esbuild/aix-ppc64': 0.28.1
@@ -5339,6 +5572,8 @@ snapshots:
escape-string-regexp@2.0.0: {}
escape-string-regexp@4.0.0: {}
event-target-shim@5.0.1: {}
eventemitter3@4.0.7: {}
@@ -5385,6 +5620,14 @@ snapshots:
dependencies:
strnum: 2.4.1
fastembed@2.1.0:
dependencies:
'@anush008/tokenizers': 0.0.0
'@huggingface/hub': 2.13.2
onnxruntime-node: 1.21.0
progress: 2.0.3
tar: 6.2.1
fastq@1.20.1:
dependencies:
reusify: 1.1.0
@@ -5454,6 +5697,10 @@ snapshots:
fs-constants@1.0.0: {}
fs-minipass@2.1.0:
dependencies:
minipass: 3.3.6
fs.realpath@1.0.0: {}
fsevents@2.3.3:
@@ -5477,6 +5724,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
gearhash-jit@1.0.2: {}
generic-pool@3.9.0: {}
gensync@1.0.0-beta.2: {}
@@ -5529,6 +5778,20 @@ snapshots:
once: 1.4.0
path-is-absolute: 1.0.1
global-agent@3.0.0:
dependencies:
boolean: 3.2.0
es6-error: 4.1.1
matcher: 3.0.0
roarr: 2.15.4
semver: 7.8.4
serialize-error: 7.0.1
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
gopd: 1.2.0
google-auth-library@10.7.0:
dependencies:
base64-js: 1.5.1
@@ -5575,6 +5838,10 @@ snapshots:
has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
dependencies:
es-define-property: 1.0.1
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
@@ -6070,6 +6337,8 @@ snapshots:
json-parse-even-better-errors@3.0.2: {}
json-stringify-safe@5.0.1: {}
json5@2.2.3: {}
jsonwebtoken@9.0.3:
@@ -6165,6 +6434,10 @@ snapshots:
dependencies:
tmpl: 1.0.5
matcher@3.0.0:
dependencies:
escape-string-regexp: 4.0.0
math-intrinsics@1.1.0: {}
md5@2.3.0:
@@ -6210,10 +6483,27 @@ snapshots:
minimist@1.2.8: {}
minipass@3.3.6:
dependencies:
yallist: 4.0.0
minipass@5.0.0: {}
minipass@7.1.3: {}
minizlib@2.1.2:
dependencies:
minipass: 3.3.6
yallist: 4.0.0
minizlib@3.1.0:
dependencies:
minipass: 7.1.3
mkdirp-classic@0.5.3: {}
mkdirp@1.0.4: {}
mlly@1.8.2:
dependencies:
acorn: 8.16.0
@@ -6344,6 +6634,8 @@ snapshots:
object-assign@4.1.1: {}
object-keys@1.1.1: {}
obuf@1.1.2: {}
ollama@0.5.18:
@@ -6358,6 +6650,14 @@ snapshots:
dependencies:
mimic-fn: 2.1.0
onnxruntime-common@1.21.0: {}
onnxruntime-node@1.21.0:
dependencies:
global-agent: 3.0.0
onnxruntime-common: 1.21.0
tar: 7.5.19
open@10.2.0:
dependencies:
default-browser: 5.5.0
@@ -6574,6 +6874,8 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.3.1
progress@2.0.3: {}
prompts@2.4.2:
dependencies:
kleur: 3.0.3
@@ -6688,6 +6990,15 @@ snapshots:
dependencies:
glob: 10.5.0
roarr@2.15.4:
dependencies:
boolean: 3.2.0
detect-node: 2.1.0
globalthis: 1.0.4
json-stringify-safe: 5.0.1
semver-compare: 1.0.0
sprintf-js: 1.1.3
rollup@4.61.1:
dependencies:
'@types/estree': 1.0.9
@@ -6729,10 +7040,16 @@ snapshots:
safe-stable-stringify@2.5.0: {}
semver-compare@1.0.0: {}
semver@6.3.1: {}
semver@7.8.4: {}
serialize-error@7.0.1:
dependencies:
type-fest: 0.13.1
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@@ -6790,6 +7107,8 @@ snapshots:
split2@4.2.0: {}
sprintf-js@1.1.3: {}
stack-utils@2.0.6:
dependencies:
escape-string-regexp: 2.0.0
@@ -6882,6 +7201,23 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
tar@6.2.1:
dependencies:
chownr: 2.0.0
fs-minipass: 2.1.0
minipass: 5.0.0
minizlib: 2.1.2
mkdirp: 1.0.4
yallist: 4.0.0
tar@7.5.19:
dependencies:
'@isaacs/fs-minipass': 4.0.1
chownr: 3.0.0
minipass: 7.1.3
minizlib: 3.1.0
yallist: 5.0.0
test-exclude@6.0.0:
dependencies:
'@istanbuljs/schema': 0.1.6
@@ -6995,6 +7331,8 @@ snapshots:
type-detect@4.0.8: {}
type-fest@0.13.1: {}
type-fest@0.21.3: {}
type-fest@3.13.1: {}
@@ -7108,6 +7446,8 @@ snapshots:
yallist@4.0.0: {}
yallist@5.0.0: {}
yargs-parser@21.1.1: {}
yargs@17.7.2:
+10 -4
View File
@@ -3,16 +3,22 @@ import { DEFAULT_MEMORY_CONFIG } from "./defaults";
export class ConfigManager {
static mergeConfig(userConfig: Partial<MemoryConfig> = {}): MemoryConfig {
const embedderProvider =
userConfig.embedder?.provider || DEFAULT_MEMORY_CONFIG.embedder.provider;
const embedderProviderKey = embedderProvider.toLowerCase();
const mergedConfig = {
version: userConfig.version || DEFAULT_MEMORY_CONFIG.version,
embedder: {
provider:
userConfig.embedder?.provider ||
DEFAULT_MEMORY_CONFIG.embedder.provider,
provider: embedderProvider,
config: (() => {
const defaultConf = DEFAULT_MEMORY_CONFIG.embedder.config;
const userConf = userConfig.embedder?.config;
let finalModel: string | any = defaultConf.model;
// The default embedder model (OpenAI's text-embedding-3-small) only
// makes sense for API-based providers. FastEmbed has its own fixed
// model set and default, so leave the model unset here and let
// FastEmbedEmbedder fall back to its own default.
let finalModel: string | any =
embedderProviderKey === "fastembed" ? undefined : defaultConf.model;
if (userConf?.model && typeof userConf.model === "object") {
finalModel = userConf.model;
@@ -0,0 +1,75 @@
import { EmbeddingModel, FlagEmbedding } from "fastembed";
import { Embedder } from "./base";
import { EmbeddingConfig } from "../types";
const DEFAULT_MODEL = EmbeddingModel.BGESmallENV15;
type FastEmbedModel = Exclude<EmbeddingModel, EmbeddingModel.CUSTOM>;
// FastEmbed only ships a fixed set of ONNX models. Keep the list handy so we can
// reject unknown model names up front with a clear message instead of letting
// FlagEmbedding.init fail later with an opaque download error.
const SUPPORTED_MODELS = Object.values(EmbeddingModel).filter(
(model) => model !== EmbeddingModel.CUSTOM,
) as FastEmbedModel[];
export class FastEmbedEmbedder implements Embedder {
private modelName: FastEmbedModel;
private embeddingModel?: Promise<FlagEmbedding>;
constructor(config: EmbeddingConfig) {
if (typeof config.model === "string" && config.model.length > 0) {
if (!SUPPORTED_MODELS.includes(config.model as FastEmbedModel)) {
throw new Error(
`Unsupported FastEmbed model "${config.model}". ` +
`Supported models: ${SUPPORTED_MODELS.join(", ")}.`,
);
}
this.modelName = config.model as FastEmbedModel;
} else {
this.modelName = DEFAULT_MODEL;
}
}
private getEmbeddingModel(): Promise<FlagEmbedding> {
if (!this.embeddingModel) {
this.embeddingModel = FlagEmbedding.init({
model: this.modelName,
}).catch((error) => {
this.embeddingModel = undefined;
throw error;
});
}
return this.embeddingModel;
}
private normalizeInput(text: string): string {
return text.replace(/\n/g, " ");
}
async embed(text: string): Promise<number[]> {
const normalizedText = this.normalizeInput(text);
const model = await this.getEmbeddingModel();
for await (const batch of model.embed([normalizedText])) {
const embedding = batch[0];
if (embedding !== undefined) {
return embedding;
}
}
throw new Error("FastEmbed embed() returned no embeddings");
}
async embedBatch(texts: string[]): Promise<number[][]> {
const normalizedTexts = texts.map((text) => this.normalizeInput(text));
const model = await this.getEmbeddingModel();
const embeddings: number[][] = [];
for await (const batch of model.embed(normalizedTexts)) {
embeddings.push(...batch);
}
return embeddings;
}
}
+1
View File
@@ -9,6 +9,7 @@ export * from "./embeddings/together";
export * from "./embeddings/google";
export * from "./embeddings/azure";
export * from "./embeddings/langchain";
export * from "./embeddings/fastembed";
export * from "./llms/base";
export * from "./llms/openai";
export * from "./llms/google";
+3
View File
@@ -36,6 +36,7 @@ import { GoogleEmbedder } from "../embeddings/google";
import { GoogleLLM } from "../llms/google";
import { AzureOpenAILLM } from "../llms/azure";
import { AzureOpenAIEmbedder } from "../embeddings/azure";
import { FastEmbedEmbedder } from "../embeddings/fastembed";
import { LangchainLLM } from "../llms/langchain";
import { LangchainEmbedder } from "../embeddings/langchain";
import { LangchainVectorStore } from "../vector_stores/langchain";
@@ -60,6 +61,8 @@ export class EmbedderFactory {
return new GoogleEmbedder(config);
case "azure_openai":
return new AzureOpenAIEmbedder(config);
case "fastembed":
return new FastEmbedEmbedder(config);
case "langchain":
return new LangchainEmbedder(config);
default:
@@ -298,6 +298,38 @@ describe("ConfigManager", () => {
});
});
describe("mergeConfig - FastEmbed defaults", () => {
const baseLlm = { provider: "openai", config: { apiKey: "k" } };
it("does not inject the OpenAI default embedder model into fastembed", () => {
const cfg = ConfigManager.mergeConfig({
embedder: {
provider: "fastembed",
config: {},
},
vectorStore: { provider: "memory", config: {} },
llm: baseLlm,
});
expect(cfg.embedder.provider).toBe("fastembed");
expect(cfg.embedder.config.model).toBeUndefined();
});
it("treats FastEmbed provider casing the same way as the factory", () => {
const cfg = ConfigManager.mergeConfig({
embedder: {
provider: "FastEmbed",
config: {},
},
vectorStore: { provider: "memory", config: {} },
llm: baseLlm,
});
expect(cfg.embedder.provider).toBe("FastEmbed");
expect(cfg.embedder.config.model).toBeUndefined();
});
});
describe("mergeConfig - LM Studio LLM config", () => {
const baseEmbedder = { provider: "openai", config: { apiKey: "k" } };
@@ -603,6 +635,30 @@ describe("Memory – LM Studio end-to-end flow", () => {
expect(result.results[0].memory).toBe("User likes hiking");
});
it("preserves the FastEmbed provider default model through the Memory config path", async () => {
const mem = new MemoryClass({
embedder: {
provider: "fastembed",
config: {},
},
vectorStore: { provider: "memory", config: { collectionName: "test" } },
llm: {
provider: "openai",
config: { apiKey: "test-key" },
},
disableHistory: true,
});
await mem.getAll({ filters: { user_id: "u1" } });
expect(mockEmbedderFactory.create).toHaveBeenCalledWith(
"fastembed",
expect.objectContaining({
model: undefined,
}),
);
});
it("add flow works with lmstudio LLM for fact extraction", async () => {
mockLlm.generateResponse.mockResolvedValueOnce(
'{"facts":["User loves sushi"]}',
@@ -20,6 +20,11 @@ jest.mock("../src/embeddings/google", () => ({
.fn()
.mockImplementation((config) => ({ type: "google-embedder", config })),
}));
jest.mock("../src/embeddings/fastembed", () => ({
FastEmbedEmbedder: jest
.fn()
.mockImplementation((config) => ({ type: "fastembed-embedder", config })),
}));
jest.mock("../src/embeddings/azure", () => ({
AzureOpenAIEmbedder: jest
.fn()
@@ -193,6 +198,7 @@ describe("EmbedderFactory", () => {
["google"],
["gemini"],
["azure_openai"],
["fastembed"],
["langchain"],
["lmstudio"],
["together"],
@@ -0,0 +1,123 @@
/// <reference types="jest" />
/**
* FastEmbed Embedder — unit tests (mocked FastEmbed client).
* Verifies async generator handling for embed and embedBatch.
*/
const mockInit = jest.fn();
const mockEmbed = jest.fn();
jest.mock("fastembed", () => {
return {
__esModule: true,
EmbeddingModel: {
BGESmallENV15: "fast-bge-small-en-v1.5",
},
FlagEmbedding: {
init: mockInit,
},
};
});
import { FastEmbedEmbedder } from "../src/embeddings/fastembed";
const mockVectorBatch = [
[0.1, 0.2],
[0.3, 0.4],
];
const mockEmbedding = [0.1, 0.2, 0.3];
const createEmbeddingGenerator = async function* (batches: number[][][]) {
for (const batch of batches) {
yield batch;
}
};
describe("FastEmbedEmbedder (unit)", () => {
beforeEach(() => {
mockInit.mockReset();
mockEmbed.mockReset();
mockInit.mockResolvedValue({
embed: mockEmbed,
});
});
it("embed() initializes lazily, normalizes newlines, and returns the first vector", async () => {
mockEmbed.mockReturnValue(
createEmbeddingGenerator([[mockEmbedding], mockVectorBatch]),
);
const embedder = new FastEmbedEmbedder({
model: "fast-bge-small-en-v1.5",
});
expect(mockInit).not.toHaveBeenCalled();
const result = await embedder.embed("Example\ntext");
expect(mockInit).toHaveBeenCalledTimes(1);
expect(mockInit).toHaveBeenCalledWith({
model: "fast-bge-small-en-v1.5",
});
expect(mockEmbed).toHaveBeenCalledTimes(1);
expect(mockEmbed.mock.calls[0][0]).toEqual(["Example text"]);
expect(result).toEqual(mockEmbedding);
});
it("embedBatch() consumes all async embedding batches and reuses the initialized model", async () => {
mockEmbed.mockReturnValueOnce(createEmbeddingGenerator([[mockEmbedding]]));
mockEmbed.mockReturnValueOnce(
createEmbeddingGenerator([[mockEmbedding], mockVectorBatch]),
);
const embedder = new FastEmbedEmbedder({});
await embedder.embed("warmup");
const result = await embedder.embedBatch([
"first text",
"second text",
"third text",
]);
expect(mockEmbed).toHaveBeenCalledWith([
"first text",
"second text",
"third text",
]);
expect(mockInit).toHaveBeenCalledTimes(1);
expect(result).toEqual([mockEmbedding, ...mockVectorBatch]);
});
it("embed() throws when FastEmbed yields no embeddings", async () => {
mockEmbed.mockReturnValue(createEmbeddingGenerator([]));
const embedder = new FastEmbedEmbedder({});
await expect(embedder.embed("missing")).rejects.toThrow(
"FastEmbed embed() returned no embeddings",
);
});
it("embed() retries initialization after a transient init failure", async () => {
mockInit.mockRejectedValueOnce(new Error("transient init failure"));
mockInit.mockResolvedValueOnce({
embed: mockEmbed,
});
mockEmbed.mockReturnValue(createEmbeddingGenerator([[mockEmbedding]]));
const embedder = new FastEmbedEmbedder({});
await expect(embedder.embed("first attempt")).rejects.toThrow(
"transient init failure",
);
const result = await embedder.embed("second attempt");
expect(mockInit).toHaveBeenCalledTimes(2);
expect(result).toEqual(mockEmbedding);
});
it("throws a clear error for an unsupported model instead of failing at init", () => {
expect(
() => new FastEmbedEmbedder({ model: "text-embedding-3-small" }),
).toThrow(/Unsupported FastEmbed model "text-embedding-3-small"/);
expect(mockInit).not.toHaveBeenCalled();
});
});
+1
View File
@@ -23,6 +23,7 @@ const external = [
"cloudflare",
"@cloudflare/workers-types",
"@langchain/core",
"fastembed",
"compromise",
"natural",
];