@nhtio/adk 1.20260625.1 → 1.20260626.0
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/CHANGELOG.md +67 -0
- package/batteries/embeddings/openai/adapter.cjs +1 -1
- package/batteries/embeddings/openai/adapter.mjs +1 -1
- package/batteries/embeddings/transformers_js/adapter.cjs +65 -18
- package/batteries/embeddings/transformers_js/adapter.cjs.map +1 -1
- package/batteries/embeddings/transformers_js/adapter.d.ts +11 -0
- package/batteries/embeddings/transformers_js/adapter.mjs +65 -18
- package/batteries/embeddings/transformers_js/adapter.mjs.map +1 -1
- package/batteries/embeddings/transformers_js/index.d.ts +3 -1
- package/batteries/embeddings/transformers_js/pooling.cjs +67 -0
- package/batteries/embeddings/transformers_js/pooling.cjs.map +1 -0
- package/batteries/embeddings/transformers_js/pooling.d.ts +41 -0
- package/batteries/embeddings/transformers_js/pooling.mjs +63 -0
- package/batteries/embeddings/transformers_js/pooling.mjs.map +1 -0
- package/batteries/embeddings/transformers_js/types.d.ts +34 -1
- package/batteries/embeddings/transformers_js/validation.cjs +11 -2
- package/batteries/embeddings/transformers_js/validation.cjs.map +1 -1
- package/batteries/embeddings/transformers_js/validation.mjs +11 -2
- package/batteries/embeddings/transformers_js/validation.mjs.map +1 -1
- package/batteries/embeddings/transformers_js.cjs +4 -0
- package/batteries/embeddings/transformers_js.mjs +2 -1
- package/batteries/embeddings/webllm/adapter.cjs +1 -1
- package/batteries/embeddings/webllm/adapter.mjs +1 -1
- package/batteries/encoding.cjs +7 -7
- package/batteries/encoding.mjs +7 -7
- package/batteries/llm/chat_common/generation.d.ts +122 -0
- package/batteries/llm/chat_common/helpers.d.ts +68 -0
- package/batteries/llm/chat_common/index.d.ts +2 -0
- package/batteries/llm/chat_common/lifecycle.d.ts +85 -0
- package/batteries/llm/chat_common/reasoning_parsers.d.ts +36 -7
- package/batteries/llm/chat_common/tool_parsers.d.ts +60 -11
- package/batteries/llm/chat_common/types.d.ts +36 -1
- package/batteries/llm/litert_lm/adapter.cjs +160 -32
- package/batteries/llm/litert_lm/adapter.cjs.map +1 -1
- package/batteries/llm/litert_lm/adapter.d.ts +11 -0
- package/batteries/llm/litert_lm/adapter.mjs +161 -33
- package/batteries/llm/litert_lm/adapter.mjs.map +1 -1
- package/batteries/llm/litert_lm/helpers.cjs +89 -12
- package/batteries/llm/litert_lm/helpers.cjs.map +1 -1
- package/batteries/llm/litert_lm/helpers.d.ts +61 -0
- package/batteries/llm/litert_lm/helpers.mjs +77 -13
- package/batteries/llm/litert_lm/helpers.mjs.map +1 -1
- package/batteries/llm/litert_lm/index.d.ts +4 -2
- package/batteries/llm/litert_lm/types.d.ts +82 -7
- package/batteries/llm/litert_lm/validation.cjs +34 -9
- package/batteries/llm/litert_lm/validation.cjs.map +1 -1
- package/batteries/llm/litert_lm/validation.mjs +34 -9
- package/batteries/llm/litert_lm/validation.mjs.map +1 -1
- package/batteries/llm/litert_lm.cjs +15 -2
- package/batteries/llm/litert_lm.mjs +5 -4
- package/batteries/llm/ollama/adapter.cjs +6 -6
- package/batteries/llm/ollama/adapter.mjs +6 -6
- package/batteries/llm/ollama/helpers.cjs +7 -7
- package/batteries/llm/ollama/helpers.cjs.map +1 -1
- package/batteries/llm/ollama/helpers.mjs +7 -7
- package/batteries/llm/ollama/helpers.mjs.map +1 -1
- package/batteries/llm/ollama/validation.cjs +1 -1
- package/batteries/llm/ollama/validation.mjs +1 -1
- package/batteries/llm/ollama.cjs +1 -1
- package/batteries/llm/ollama.mjs +1 -1
- package/batteries/llm/openai_chat_completions/adapter.cjs +6 -6
- package/batteries/llm/openai_chat_completions/adapter.mjs +6 -6
- package/batteries/llm/openai_chat_completions/helpers.cjs +16 -10
- package/batteries/llm/openai_chat_completions/helpers.cjs.map +1 -1
- package/batteries/llm/openai_chat_completions/helpers.d.ts +1 -1
- package/batteries/llm/openai_chat_completions/helpers.mjs +12 -11
- package/batteries/llm/openai_chat_completions/helpers.mjs.map +1 -1
- package/batteries/llm/openai_chat_completions/index.d.ts +1 -1
- package/batteries/llm/openai_chat_completions/validation.cjs +1 -1
- package/batteries/llm/openai_chat_completions/validation.mjs +1 -1
- package/batteries/llm/openai_chat_completions.cjs +6 -1
- package/batteries/llm/openai_chat_completions.mjs +2 -2
- package/batteries/llm/transformers_js/adapter.cjs +266 -48
- package/batteries/llm/transformers_js/adapter.cjs.map +1 -1
- package/batteries/llm/transformers_js/adapter.d.ts +17 -2
- package/batteries/llm/transformers_js/adapter.mjs +267 -49
- package/batteries/llm/transformers_js/adapter.mjs.map +1 -1
- package/batteries/llm/transformers_js/helpers.cjs +175 -8
- package/batteries/llm/transformers_js/helpers.cjs.map +1 -1
- package/batteries/llm/transformers_js/helpers.d.ts +59 -0
- package/batteries/llm/transformers_js/helpers.mjs +163 -9
- package/batteries/llm/transformers_js/helpers.mjs.map +1 -1
- package/batteries/llm/transformers_js/index.d.ts +7 -3
- package/batteries/llm/transformers_js/model_source.cjs +144 -0
- package/batteries/llm/transformers_js/model_source.cjs.map +1 -0
- package/batteries/llm/transformers_js/model_source.d.ts +95 -0
- package/batteries/llm/transformers_js/model_source.mjs +139 -0
- package/batteries/llm/transformers_js/model_source.mjs.map +1 -0
- package/batteries/llm/transformers_js/types.d.ts +109 -17
- package/batteries/llm/transformers_js/validation.cjs +31 -11
- package/batteries/llm/transformers_js/validation.cjs.map +1 -1
- package/batteries/llm/transformers_js/validation.mjs +31 -11
- package/batteries/llm/transformers_js/validation.mjs.map +1 -1
- package/batteries/llm/transformers_js.cjs +21 -2
- package/batteries/llm/transformers_js.mjs +6 -4
- package/batteries/llm/webllm_chat_completions/adapter.cjs +56 -22
- package/batteries/llm/webllm_chat_completions/adapter.cjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.mjs +56 -22
- package/batteries/llm/webllm_chat_completions/adapter.mjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/helpers.cjs +1 -1
- package/batteries/llm/webllm_chat_completions/helpers.mjs +1 -1
- package/batteries/llm/webllm_chat_completions/index.d.ts +1 -1
- package/batteries/llm/webllm_chat_completions/types.d.ts +11 -1
- package/batteries/llm/webllm_chat_completions/validation.cjs +14 -6
- package/batteries/llm/webllm_chat_completions/validation.cjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/validation.mjs +14 -6
- package/batteries/llm/webllm_chat_completions/validation.mjs.map +1 -1
- package/batteries/llm/webllm_chat_completions.cjs +1 -1
- package/batteries/llm/webllm_chat_completions.mjs +1 -1
- package/batteries/llm.cjs +6 -1
- package/batteries/llm.mjs +2 -2
- package/batteries/media/contracts.cjs +1 -1
- package/batteries/media/contracts.mjs +1 -1
- package/batteries/media/engines/audio_decode.cjs +1 -1
- package/batteries/media/engines/audio_decode.mjs +1 -1
- package/batteries/media/engines/data.cjs +1 -1
- package/batteries/media/engines/data.mjs +1 -1
- package/batteries/media/engines/exceljs.cjs +1 -1
- package/batteries/media/engines/exceljs.mjs +1 -1
- package/batteries/media/engines/execa_executor.cjs +1 -1
- package/batteries/media/engines/execa_executor.mjs +1 -1
- package/batteries/media/engines/fs_workspace.cjs +1 -1
- package/batteries/media/engines/fs_workspace.mjs +1 -1
- package/batteries/media/engines/jimp.cjs +1 -1
- package/batteries/media/engines/jimp.mjs +1 -1
- package/batteries/media/engines/sharp.cjs +1 -1
- package/batteries/media/engines/sharp.mjs +1 -1
- package/batteries/media/engines/sheetjs.cjs +1 -1
- package/batteries/media/engines/sheetjs.mjs +1 -1
- package/batteries/media/engines/tesseract_js.cjs +1 -1
- package/batteries/media/engines/tesseract_js.mjs +1 -1
- package/batteries/media/engines/transformers_asr.cjs +1 -1
- package/batteries/media/engines/transformers_asr.mjs +1 -1
- package/batteries/media/forge.cjs +6 -6
- package/batteries/media/forge.mjs +6 -6
- package/batteries/media/lint.cjs +1 -1
- package/batteries/media/lint.mjs +1 -1
- package/batteries/media.cjs +2 -2
- package/batteries/media.mjs +2 -2
- package/batteries/storage/flydrive.cjs +1 -1
- package/batteries/storage/flydrive.mjs +1 -1
- package/batteries/storage/in_memory.cjs +1 -1
- package/batteries/storage/in_memory.mjs +1 -1
- package/batteries/storage/opfs.cjs +1 -1
- package/batteries/storage/opfs.mjs +1 -1
- package/batteries/tools/_shared.cjs +2 -2
- package/batteries/tools/_shared.mjs +2 -2
- package/batteries/tools/color.cjs +2 -2
- package/batteries/tools/color.mjs +2 -2
- package/batteries/tools/comparison.cjs +3 -3
- package/batteries/tools/comparison.mjs +3 -3
- package/batteries/tools/data_structure.cjs +3 -3
- package/batteries/tools/data_structure.mjs +3 -3
- package/batteries/tools/datetime_extended.cjs +3 -3
- package/batteries/tools/datetime_extended.mjs +3 -3
- package/batteries/tools/datetime_math.cjs +3 -3
- package/batteries/tools/datetime_math.mjs +3 -3
- package/batteries/tools/encoding.cjs +3 -3
- package/batteries/tools/encoding.mjs +3 -3
- package/batteries/tools/formatting.cjs +3 -3
- package/batteries/tools/formatting.mjs +3 -3
- package/batteries/tools/geo_basics.cjs +2 -2
- package/batteries/tools/geo_basics.mjs +2 -2
- package/batteries/tools/math.cjs +3 -3
- package/batteries/tools/math.mjs +3 -3
- package/batteries/tools/memory.cjs +6 -6
- package/batteries/tools/memory.mjs +6 -6
- package/batteries/tools/parsing.cjs +4 -4
- package/batteries/tools/parsing.mjs +4 -4
- package/batteries/tools/retrievables.cjs +5 -5
- package/batteries/tools/retrievables.mjs +5 -5
- package/batteries/tools/scrapper.cjs +1 -1
- package/batteries/tools/scrapper.mjs +1 -1
- package/batteries/tools/searxng.cjs +1 -1
- package/batteries/tools/searxng.mjs +1 -1
- package/batteries/tools/standing_instructions.cjs +4 -4
- package/batteries/tools/standing_instructions.mjs +4 -4
- package/batteries/tools/statistics.cjs +4 -4
- package/batteries/tools/statistics.mjs +4 -4
- package/batteries/tools/string_processing.cjs +3 -3
- package/batteries/tools/string_processing.mjs +3 -3
- package/batteries/tools/structured_data.cjs +3 -3
- package/batteries/tools/structured_data.mjs +3 -3
- package/batteries/tools/text_analysis.cjs +3 -3
- package/batteries/tools/text_analysis.mjs +3 -3
- package/batteries/tools/text_comparison.cjs +2 -2
- package/batteries/tools/text_comparison.mjs +2 -2
- package/batteries/tools/time.cjs +3 -3
- package/batteries/tools/time.mjs +3 -3
- package/batteries/tools/unit_conversion.cjs +2 -2
- package/batteries/tools/unit_conversion.mjs +2 -2
- package/batteries/tools/web_retrieval.cjs +1 -1
- package/batteries/tools/web_retrieval.mjs +1 -1
- package/batteries/tools.cjs +2 -2
- package/batteries/tools.mjs +2 -2
- package/batteries/vector/arangodb.cjs +1 -1
- package/batteries/vector/arangodb.mjs +1 -1
- package/batteries/vector/clickhouse.cjs +1 -1
- package/batteries/vector/clickhouse.mjs +1 -1
- package/batteries/vector/cloudflare.cjs +1 -1
- package/batteries/vector/cloudflare.mjs +1 -1
- package/batteries/vector/couchbase.cjs +1 -1
- package/batteries/vector/couchbase.mjs +1 -1
- package/batteries/vector/duckdb.cjs +1 -1
- package/batteries/vector/duckdb.mjs +1 -1
- package/batteries/vector/elasticsearch.cjs +1 -1
- package/batteries/vector/elasticsearch.mjs +1 -1
- package/batteries/vector/filters.cjs +1 -1
- package/batteries/vector/filters.mjs +1 -1
- package/batteries/vector/hnswlib.cjs +1 -1
- package/batteries/vector/hnswlib.mjs +1 -1
- package/batteries/vector/lancedb.cjs +1 -1
- package/batteries/vector/lancedb.mjs +1 -1
- package/batteries/vector/mariadb.cjs +1 -1
- package/batteries/vector/mariadb.mjs +1 -1
- package/batteries/vector/meilisearch.cjs +1 -1
- package/batteries/vector/meilisearch.mjs +1 -1
- package/batteries/vector/migrate.cjs +1 -1
- package/batteries/vector/migrate.mjs +1 -1
- package/batteries/vector/mongodb.cjs +1 -1
- package/batteries/vector/mongodb.mjs +1 -1
- package/batteries/vector/neo4j.cjs +1 -1
- package/batteries/vector/neo4j.mjs +1 -1
- package/batteries/vector/opensearch.cjs +1 -1
- package/batteries/vector/opensearch.mjs +1 -1
- package/batteries/vector/oracle23ai.cjs +1 -1
- package/batteries/vector/oracle23ai.mjs +1 -1
- package/batteries/vector/pinecone.cjs +1 -1
- package/batteries/vector/pinecone.mjs +1 -1
- package/batteries/vector/redis.cjs +1 -1
- package/batteries/vector/redis.mjs +1 -1
- package/batteries/vector/retrievable.cjs +1 -1
- package/batteries/vector/retrievable.mjs +1 -1
- package/batteries/vector/s3vectors.cjs +1 -1
- package/batteries/vector/s3vectors.mjs +1 -1
- package/batteries/vector/solr.cjs +1 -1
- package/batteries/vector/solr.mjs +1 -1
- package/batteries/vector/surrealdb.cjs +1 -1
- package/batteries/vector/surrealdb.mjs +1 -1
- package/batteries/vector/typesense.cjs +1 -1
- package/batteries/vector/typesense.mjs +1 -1
- package/batteries/vector/vespa.cjs +1 -1
- package/batteries/vector/vespa.mjs +1 -1
- package/batteries/vector/weaviate.cjs +1 -1
- package/batteries/vector/weaviate.mjs +1 -1
- package/batteries.cjs +8 -3
- package/batteries.mjs +4 -4
- package/{common-0CmrSKNb.mjs → common-DJH3UemH.mjs} +7 -7
- package/{common-0CmrSKNb.mjs.map → common-DJH3UemH.mjs.map} +1 -1
- package/{common-CpcsEqPY.js → common-ax3vKu6O.js} +7 -7
- package/{common-CpcsEqPY.js.map → common-ax3vKu6O.js.map} +1 -1
- package/common.cjs +7 -7
- package/common.mjs +7 -7
- package/{dispatch_runner-C2ZpFfw4.mjs → dispatch_runner-3anlRk3F.mjs} +3 -3
- package/{dispatch_runner-C2ZpFfw4.mjs.map → dispatch_runner-3anlRk3F.mjs.map} +1 -1
- package/{dispatch_runner-DMTkGHQI.js → dispatch_runner-CTkL_cMm.js} +3 -3
- package/{dispatch_runner-DMTkGHQI.js.map → dispatch_runner-CTkL_cMm.js.map} +1 -1
- package/dispatch_runner.cjs +1 -1
- package/dispatch_runner.mjs +1 -1
- package/eslint/rules.cjs +1 -1
- package/eslint/rules.mjs +1 -1
- package/eslint.cjs +2 -2
- package/eslint.mjs +2 -2
- package/forge.cjs +4 -4
- package/forge.mjs +4 -4
- package/guards.cjs +8 -8
- package/guards.mjs +8 -8
- package/{helpers-1okZzNCa.js → helpers-CJcqikes.js} +131 -1
- package/helpers-CJcqikes.js.map +1 -0
- package/{helpers-BCyDAn_R.mjs → helpers-DlrRbLVn.mjs} +102 -2
- package/helpers-DlrRbLVn.mjs.map +1 -0
- package/index.cjs +11 -11
- package/index.mjs +11 -11
- package/lifecycle-C4kwYJ-N.mjs +68 -0
- package/lifecycle-C4kwYJ-N.mjs.map +1 -0
- package/lifecycle-vYQJ2rmf.js +79 -0
- package/lifecycle-vYQJ2rmf.js.map +1 -0
- package/mcp/adk-docs-corpus.json +1 -1
- package/package.json +249 -239
- package/reasoning_parsers-CFNoFf6x.js +1016 -0
- package/reasoning_parsers-CFNoFf6x.js.map +1 -0
- package/reasoning_parsers-Dp66LFBt.mjs +747 -0
- package/reasoning_parsers-Dp66LFBt.mjs.map +1 -0
- package/{scrapper-CxmS0VaD.mjs → scrapper-BrViMDPI.mjs} +5 -5
- package/{scrapper-CxmS0VaD.mjs.map → scrapper-BrViMDPI.mjs.map} +1 -1
- package/{scrapper-BUCxsVVi.js → scrapper-CzgdX1NN.js} +5 -5
- package/{scrapper-BUCxsVVi.js.map → scrapper-CzgdX1NN.js.map} +1 -1
- package/{searxng-CgxQluyF.js → searxng-B5I1KmQj.js} +5 -5
- package/{searxng-CgxQluyF.js.map → searxng-B5I1KmQj.js.map} +1 -1
- package/{searxng-Dgh-4RpS.mjs → searxng-BvjwwZav.mjs} +5 -5
- package/{searxng-Dgh-4RpS.mjs.map → searxng-BvjwwZav.mjs.map} +1 -1
- package/server.json +2 -2
- package/skills/adk-assembly/SKILL.md +2 -2
- package/{spooled_artifact-fgc1lutm.mjs → spooled_artifact-BnbqIZ3a.mjs} +3 -3
- package/{spooled_artifact-fgc1lutm.mjs.map → spooled_artifact-BnbqIZ3a.mjs.map} +1 -1
- package/{spooled_artifact-MbMz6NXX.js → spooled_artifact-DrIR90nV.js} +3 -3
- package/{spooled_artifact-MbMz6NXX.js.map → spooled_artifact-DrIR90nV.js.map} +1 -1
- package/spooled_artifact.cjs +2 -2
- package/spooled_artifact.mjs +2 -2
- package/{spooled_markdown_artifact-3sK6HFRw.mjs → spooled_markdown_artifact-BJH_JZFm.mjs} +3 -3
- package/{spooled_markdown_artifact-3sK6HFRw.mjs.map → spooled_markdown_artifact-BJH_JZFm.mjs.map} +1 -1
- package/{spooled_markdown_artifact-Gj0ruiLJ.js → spooled_markdown_artifact-rg_WCNWq.js} +3 -3
- package/{spooled_markdown_artifact-Gj0ruiLJ.js.map → spooled_markdown_artifact-rg_WCNWq.js.map} +1 -1
- package/{thought-CU7PYkB1.js → thought-CE88bddw.js} +3 -3
- package/{thought-CU7PYkB1.js.map → thought-CE88bddw.js.map} +1 -1
- package/{thought-DmcfCRAw.mjs → thought-DdiQCpgX.mjs} +3 -3
- package/{thought-DmcfCRAw.mjs.map → thought-DdiQCpgX.mjs.map} +1 -1
- package/{tool-Bt6tmFXT.js → tool-BngpaOWb.js} +3 -3
- package/{tool-Bt6tmFXT.js.map → tool-BngpaOWb.js.map} +1 -1
- package/{tool-C_uxdxjp.mjs → tool-asIfCjxb.mjs} +3 -3
- package/{tool-C_uxdxjp.mjs.map → tool-asIfCjxb.mjs.map} +1 -1
- package/{tool_call-BixyvnWZ.js → tool_call-CZVmISyc.js} +4 -4
- package/{tool_call-BixyvnWZ.js.map → tool_call-CZVmISyc.js.map} +1 -1
- package/{tool_call-pfvAsgkY.mjs → tool_call-TtzdXxcb.mjs} +4 -4
- package/{tool_call-pfvAsgkY.mjs.map → tool_call-TtzdXxcb.mjs.map} +1 -1
- package/{tool_registry-FeMMz993.mjs → tool_registry-Bj0JhXCv.mjs} +164 -164
- package/tool_registry-Bj0JhXCv.mjs.map +1 -0
- package/{tool_registry-CIIfbY_w.js → tool_registry-DbS8Pem9.js} +163 -163
- package/tool_registry-DbS8Pem9.js.map +1 -0
- package/{turn_runner-Brz6Sd0F.mjs → turn_runner-305dGa72.mjs} +5 -5
- package/{turn_runner-Brz6Sd0F.mjs.map → turn_runner-305dGa72.mjs.map} +1 -1
- package/{turn_runner-DB-WWls8.js → turn_runner-DqJBbqeU.js} +5 -5
- package/{turn_runner-DB-WWls8.js.map → turn_runner-DqJBbqeU.js.map} +1 -1
- package/turn_runner.cjs +1 -1
- package/turn_runner.mjs +1 -1
- package/{validate-MBEmBzMl.mjs → validate-B8IGAERs.mjs} +2 -2
- package/{validate-MBEmBzMl.mjs.map → validate-B8IGAERs.mjs.map} +1 -1
- package/{validate-B4RXgYJC.js → validate-Cq8xhZrZ.js} +2 -2
- package/{validate-B4RXgYJC.js.map → validate-Cq8xhZrZ.js.map} +1 -1
- package/helpers-1okZzNCa.js.map +0 -1
- package/helpers-BCyDAn_R.mjs.map +0 -1
- package/reasoning_parsers-B6W-1twu.mjs +0 -439
- package/reasoning_parsers-B6W-1twu.mjs.map +0 -1
- package/reasoning_parsers-CdjuWEfx.js +0 -654
- package/reasoning_parsers-CdjuWEfx.js.map +0 -1
- package/tool_registry-CIIfbY_w.js.map +0 -1
- package/tool_registry-FeMMz993.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,73 @@ you *when* you got it, not *what changed*: a `^` range will float across battery
|
|
|
15
15
|
breaking changes, so pin an exact version if you need stability and read the entry before
|
|
16
16
|
upgrading.
|
|
17
17
|
|
|
18
|
+
## 2026-06-26
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Portable generation vocabulary shared by the two text-out on-device batteries** (`transformers_js`,
|
|
23
|
+
`litert_lm`). Both now accept one canonical {@link ChatGenerationOptions} surface — `maxTokens`, `sampler`
|
|
24
|
+
(`'greedy'`|`'top-k'`|`'top-p'`), `temperature`, `topK`, `topP`, `seed`, `enableThinking`,
|
|
25
|
+
`multimodal: { image, audio }` — and each adapter maps it onto its own runtime API. Precedence is
|
|
26
|
+
**canonical-wins**: the canonical field is honored and the battery's native field (transformers.js
|
|
27
|
+
`maxNewTokens`, LiteRT `maxOutputTokens` / `samplerParams`) is the fallback consulted only when the canonical
|
|
28
|
+
one is absent, so existing native-field config keeps working. Defaults are identical across both batteries
|
|
29
|
+
and chosen for reproducibility (`sampler: 'greedy'`, `enableThinking: false` — many reasoning templates
|
|
30
|
+
default thinking *on* and burn the token budget before the answer; this turns it off unless asked).
|
|
31
|
+
- **Normalized lifecycle hook surface across all on-device batteries** (`transformers_js`, `litert_lm`,
|
|
32
|
+
`webllm_chat_completions`, and the transformers.js embeddings battery). A new opt-in {@link BatteryLifecycleHooks}
|
|
33
|
+
block: an `onLifecycle` firehose plus per-phase hooks `onLoading` → `onCompiling` → `onReady` →
|
|
34
|
+
`onGenerating` → `onComplete` (or `onError`), each handed a normalized `BatteryLifecycleReport`
|
|
35
|
+
(`{ phase, battery, model, at, detail?, progress?, raw?, error? }`). `progress` is normalized to `0..1`
|
|
36
|
+
during `loading` when the provider reports it; the `compiling` phase marks the WebGPU/wasm shader/graph
|
|
37
|
+
build between download and first token — often the slowest part of a cold start, and previously invisible.
|
|
38
|
+
Purely additive — omit the hooks and behavior is byte-for-byte unchanged; a throwing consumer hook can never
|
|
39
|
+
abort a load or a turn. The existing per-provider `onInitProgress` is untouched.
|
|
40
|
+
- **Multimodal INPUT for the transformers.js battery.** Image and audio flow through the model's processor
|
|
41
|
+
(called positionally, `_call(text, images, audio)`); a multimodal model genuinely perceives them. For the
|
|
42
|
+
common audio case — uncompressed PCM WAV — the battery decodes the RIFF itself with a `DataView`,
|
|
43
|
+
dependency-free and **env-neutral**, *before* importing the heavy peer (transformers.js's own `read_audio`
|
|
44
|
+
needs the Web Audio API's `AudioContext`, which does not exist in Node; compressed containers still fall
|
|
45
|
+
back to it, browser-only). Enable per kind via the canonical `multimodal: { image, audio }`.
|
|
46
|
+
- **Opt-in media-OUTPUT seam (`extractMediaOutputs`) on the `transformers_js` and `litert_lm` batteries.**
|
|
47
|
+
Default absent → text-out, byte-for-byte unchanged. Supply the hook and a wrapped media-emitting model's
|
|
48
|
+
generated audio/image is persisted via `ctx.storeMediaBytes`, wrapped as a first-party `Media.toolGenerated(...)`,
|
|
49
|
+
and surfaced as an assistant `Message.attachments` entry (a media-only turn — empty text + attachment — is
|
|
50
|
+
legitimate). The batteries remain multimodal-in / text-out by default; this is for an LLM turn that produces
|
|
51
|
+
media alongside or instead of text.
|
|
52
|
+
- **`'phi'` tool-call parser** added to the shared parser layer's `toolCallParser` set and the `'auto'`
|
|
53
|
+
priority order (`hermes → gemma → gpt_oss → phi → pythonic → llama3_json → mistral → qwen3_coder`). Anchored
|
|
54
|
+
on the literal `functools` token (verified against vLLM's `phi4_mini_json` parser), so it runs with the
|
|
55
|
+
other marker-anchored families ahead of the weak-signal JSON/pythonic forms.
|
|
56
|
+
- **`EmbeddingGemma 300M`** (`onnx-community/embeddinggemma-300m-ONNX`) verified through the
|
|
57
|
+
`transformers_js` embeddings battery — 768-dim, unit-norm, deterministic — alongside the existing
|
|
58
|
+
MiniLM / BGE-small / Arctic-S entries.
|
|
59
|
+
- **The real-model test matrix** (`tests/_fixtures/model_matrix.ts`, gated on `TEST_MODEL_MATRIX=1`): loads
|
|
60
|
+
each real ONNX / `.litertlm` model, drives one dispatch turn, and asserts the expected parser family or
|
|
61
|
+
multimodal grounding is extracted — because a small model may not emit the format its chat template implies
|
|
62
|
+
(Gemma 4 E2B emits the decoder-stripped `call:NAME{k:v}`, not the template's `<|tool_call>…`). A Node half
|
|
63
|
+
(`pnpm run test:matrix`) and a headed-WebGPU browser half (`pnpm run test:matrix:browser`, local-only — CI
|
|
64
|
+
runners have no GPU). `bin/capture_tool_outputs.ts` (`pnpm run capture:tools`) captures real raw output from
|
|
65
|
+
hosted big-only families (`qwen3_coder`, `gpt_oss`, `mistral`) via any OpenAI-compatible proxy into committed
|
|
66
|
+
parser fixtures — configured with `--base-url`/`--api-key` or the generic `CAPTURE_BASE_URL` /
|
|
67
|
+
`CAPTURE_API_KEY` env vars; never run in CI.
|
|
68
|
+
- **Documentation: a dedicated "LLM Batteries" section** under Featured Batteries — an overview hub, a
|
|
69
|
+
"Shared Contract" page (the three `chat_common` pillars: parser layer, portable generation vocabulary,
|
|
70
|
+
lifecycle hooks), and a reference page per battery (OpenAI, Ollama, WebLLM, LiteRT-LM, Transformers.js) each
|
|
71
|
+
carrying a tested-model table grounded in the matrix. Plus a showcase, "Building the On-Device Batteries,"
|
|
72
|
+
documenting the parser archaeology, the false-green fixtures, and the one wall we could not engineer around:
|
|
73
|
+
**LiteRT-LM in the browser runs Gemma and only Gemma**, proven at the file-format level
|
|
74
|
+
(`tf_lite_prefill_decode` vs `tf_lite_artisan_text_decoder`) — converting a non-Gemma model to a
|
|
75
|
+
browser-runnable `.litertlm` is a dead end with the public toolchain. The dead `convert_model` / `deploy_model`
|
|
76
|
+
scripts (which described a fictional CLI) were removed and the conversion doc rewritten as "The Real-Model
|
|
77
|
+
Matrix." Docs are bundled in the npm package and served by the ADK Assembly MCP, so this ships with the release.
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
|
|
81
|
+
- **`bin/capture_tool_outputs.ts` now reads its proxy URL/key from `CAPTURE_BASE_URL` / `CAPTURE_API_KEY`**
|
|
82
|
+
(or the existing `--base-url` / `--api-key` flags), replacing internal-specific env-var names. Dev tool only;
|
|
83
|
+
not part of the published runtime surface.
|
|
84
|
+
|
|
18
85
|
## 2026-06-25
|
|
19
86
|
|
|
20
87
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_tool_registry = require("../../../tool_registry-
|
|
3
|
+
const require_tool_registry = require("../../../tool_registry-DbS8Pem9.js");
|
|
4
4
|
require("../../../guards.cjs");
|
|
5
5
|
const require_lib_utils_retry = require("../../../lib/utils/retry.cjs");
|
|
6
6
|
const require_batteries_embeddings_openai_exceptions = require("./exceptions.cjs");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as isError } from "../../../tool_registry-
|
|
1
|
+
import { c as isError } from "../../../tool_registry-Bj0JhXCv.mjs";
|
|
2
2
|
import "../../../guards.mjs";
|
|
3
3
|
import { computeBackoff, parseRetryAfter, sleepWithJitter } from "../../../lib/utils/retry.mjs";
|
|
4
4
|
import { E_OPENAI_EMBEDDINGS_HTTP_ERROR, E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE, E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT } from "./exceptions.mjs";
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_tool_registry = require("../../../tool_registry-
|
|
3
|
+
const require_tool_registry = require("../../../tool_registry-DbS8Pem9.js");
|
|
4
4
|
require("../../../guards.cjs");
|
|
5
|
+
const require_lifecycle = require("../../../lifecycle-vYQJ2rmf.js");
|
|
6
|
+
const require_batteries_llm_transformers_js_model_source = require("../../llm/transformers_js/model_source.cjs");
|
|
5
7
|
const require_batteries_embeddings_openai_helpers = require("../openai/helpers.cjs");
|
|
8
|
+
const require_batteries_embeddings_transformers_js_pooling = require("./pooling.cjs");
|
|
6
9
|
const require_batteries_embeddings_transformers_js_exceptions = require("./exceptions.cjs");
|
|
7
10
|
const require_batteries_embeddings_transformers_js_validation = require("./validation.cjs");
|
|
8
11
|
//#region src/batteries/embeddings/transformers_js/adapter.ts
|
|
@@ -27,13 +30,16 @@ const require_batteries_embeddings_transformers_js_validation = require("./valid
|
|
|
27
30
|
* from a different runtime (WebLLM/MLC, or even node-ONNX vs web-ONNX for the same model). A vector
|
|
28
31
|
* corpus must be embedded AND queried by one backend.
|
|
29
32
|
*/
|
|
30
|
-
var
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
var makeDefaultCreatePipeline = (modelSource) => {
|
|
34
|
+
return async ({ model, device, dtype, onInitProgress }) => {
|
|
35
|
+
const { pipeline, env } = await import("@huggingface/transformers");
|
|
36
|
+
const load = async () => await pipeline("feature-extraction", model, {
|
|
37
|
+
...device ? { device } : {},
|
|
38
|
+
...dtype ? { dtype } : {},
|
|
39
|
+
...onInitProgress ? { progress_callback: onInitProgress } : {}
|
|
40
|
+
});
|
|
41
|
+
return modelSource ? require_batteries_llm_transformers_js_model_source.withModelSource(env, modelSource, load) : load();
|
|
42
|
+
};
|
|
37
43
|
};
|
|
38
44
|
/**
|
|
39
45
|
* Embeddings adapter for transformers.js's feature-extraction pipeline.
|
|
@@ -79,22 +85,50 @@ var TransformersJsEmbeddingsAdapter = class TransformersJsEmbeddingsAdapter {
|
|
|
79
85
|
this.#pipeline = void 0;
|
|
80
86
|
this.#pipelinePromise = void 0;
|
|
81
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Release the loaded model's ONNX sessions + GPU/wasm buffers, then drop the cached pipeline.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* `reset()` only nulls the JS reference; the native ONNX Runtime sessions and WebGPU/wasm device memory
|
|
93
|
+
* stay alive until GC. Loading many embedding models back-to-back in one browser session (e.g. a full
|
|
94
|
+
* matrix run) accumulates those sessions until the heap is exhausted. `FeatureExtractionPipeline`
|
|
95
|
+
* extends `Pipeline`, which exposes `dispose()` — this awaits it so the memory is reclaimed between
|
|
96
|
+
* loads, swallows a disposal error (teardown must not throw), and finishes with `reset()`. Idempotent.
|
|
97
|
+
*/
|
|
98
|
+
async dispose() {
|
|
99
|
+
const pipeWithDispose = this.#pipeline ?? await this.#pipelinePromise?.catch(() => void 0);
|
|
100
|
+
if (typeof pipeWithDispose?.dispose === "function") await Promise.resolve(pipeWithDispose.dispose()).catch(() => void 0);
|
|
101
|
+
this.reset();
|
|
102
|
+
}
|
|
82
103
|
async #resolvePipeline() {
|
|
83
104
|
if (this.#pipeline) return this.#pipeline;
|
|
84
105
|
if (!this.isAvailable()) throw new require_batteries_embeddings_transformers_js_exceptions.E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS(["the transformers.js embeddings battery is not available in this runtime"]);
|
|
106
|
+
const opts = this.#options;
|
|
85
107
|
this.#pipelinePromise ??= (async () => {
|
|
86
|
-
|
|
108
|
+
require_lifecycle.emitLifecycle(opts, "transformers_js_embed", opts.model, "loading", { detail: "loading feature-extraction pipeline" });
|
|
109
|
+
const forwardedInitProgress = opts.onLifecycle ?? opts.onLoading ?? opts.onReady ?? opts.onGenerating ?? opts.onError ? (info) => {
|
|
110
|
+
const p = info?.progress;
|
|
111
|
+
require_lifecycle.emitLifecycle(opts, "transformers_js_embed", opts.model, "loading", {
|
|
112
|
+
...typeof p === "number" ? { progress: p / 100 } : {},
|
|
113
|
+
raw: info
|
|
114
|
+
});
|
|
115
|
+
opts.onInitProgress?.(info);
|
|
116
|
+
} : opts.onInitProgress;
|
|
117
|
+
const createPipeline = opts.createPipeline ?? makeDefaultCreatePipeline(opts.modelSource);
|
|
87
118
|
try {
|
|
119
|
+
require_lifecycle.emitLifecycle(opts, "transformers_js_embed", opts.model, "compiling", { detail: "compiling feature-extraction graph" });
|
|
88
120
|
const pipe = await createPipeline({
|
|
89
|
-
model:
|
|
90
|
-
device:
|
|
91
|
-
dtype:
|
|
92
|
-
onInitProgress:
|
|
121
|
+
model: opts.model,
|
|
122
|
+
device: opts.device,
|
|
123
|
+
dtype: opts.dtype,
|
|
124
|
+
onInitProgress: forwardedInitProgress
|
|
93
125
|
});
|
|
94
126
|
this.#pipeline = pipe;
|
|
127
|
+
require_lifecycle.emitLifecycle(opts, "transformers_js_embed", opts.model, "ready", { detail: "feature-extraction pipeline ready" });
|
|
95
128
|
return pipe;
|
|
96
129
|
} catch (err) {
|
|
97
130
|
this.#pipelinePromise = void 0;
|
|
131
|
+
require_lifecycle.emitLifecycle(opts, "transformers_js_embed", opts.model, "error", { error: err });
|
|
98
132
|
throw new require_batteries_embeddings_transformers_js_exceptions.E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([`could not load the transformers.js pipeline: ${require_tool_registry.isError(err) ? err.message : String(err)} — install the peer dependency (pnpm add @huggingface/transformers)`]);
|
|
99
133
|
}
|
|
100
134
|
})();
|
|
@@ -124,19 +158,32 @@ var TransformersJsEmbeddingsAdapter = class TransformersJsEmbeddingsAdapter {
|
|
|
124
158
|
if (texts.length === 0) return [];
|
|
125
159
|
const input = require_batteries_embeddings_openai_helpers.applyEmbeddingPrefix(texts, opts?.kind ?? "document", this.#options);
|
|
126
160
|
const pipe = await this.#resolvePipeline();
|
|
161
|
+
const pooling = this.#options.pooling ?? "mean";
|
|
162
|
+
const normalize = this.#options.normalize ?? true;
|
|
163
|
+
const battery = (this.#options.poolingOwner ?? "engine") === "battery";
|
|
164
|
+
require_lifecycle.emitLifecycle(this.#options, "transformers_js_embed", this.#options.model, "generating");
|
|
127
165
|
let tensor;
|
|
128
166
|
try {
|
|
129
|
-
tensor = await pipe(input, {
|
|
130
|
-
pooling
|
|
131
|
-
normalize
|
|
167
|
+
tensor = await pipe(input, battery ? { pooling: "none" } : {
|
|
168
|
+
pooling,
|
|
169
|
+
normalize
|
|
132
170
|
});
|
|
133
171
|
} catch (err) {
|
|
172
|
+
require_lifecycle.emitLifecycle(this.#options, "transformers_js_embed", this.#options.model, "error", { error: err });
|
|
134
173
|
throw new require_batteries_embeddings_transformers_js_exceptions.E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([require_tool_registry.isError(err) ? err.message : String(err)]);
|
|
135
174
|
}
|
|
136
175
|
if (!tensor || typeof tensor.tolist !== "function") throw new require_batteries_embeddings_transformers_js_exceptions.E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR(["feature-extraction returned a non-Tensor result"]);
|
|
137
176
|
const list = tensor.tolist();
|
|
138
|
-
|
|
139
|
-
if (
|
|
177
|
+
let vectors;
|
|
178
|
+
if (battery) {
|
|
179
|
+
const states = list;
|
|
180
|
+
vectors = require_batteries_embeddings_transformers_js_pooling.poolAndNormalize(Array.isArray(states) && Array.isArray(states[0]) && Array.isArray(states[0][0]) ? states : [states], pooling, normalize);
|
|
181
|
+
} else vectors = Array.isArray(list) && Array.isArray(list[0]) ? list : [list];
|
|
182
|
+
if (vectors.length !== input.length) {
|
|
183
|
+
require_lifecycle.emitLifecycle(this.#options, "transformers_js_embed", this.#options.model, "error", { error: /* @__PURE__ */ new Error(`expected ${input.length} vectors, got ${vectors.length}`) });
|
|
184
|
+
throw new require_batteries_embeddings_transformers_js_exceptions.E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([`expected ${input.length} vectors, got ${vectors.length}`]);
|
|
185
|
+
}
|
|
186
|
+
require_lifecycle.emitLifecycle(this.#options, "transformers_js_embed", this.#options.model, "complete");
|
|
140
187
|
return vectors;
|
|
141
188
|
}
|
|
142
189
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.cjs","names":["#options","#pipeline","#resolvePipeline","#pipelinePromise"],"sources":["../../../../src/batteries/embeddings/transformers_js/adapter.ts"],"sourcesContent":["/**\n * transformers.js (ONNX, dual-environment) Embeddings adapter battery.\n *\n * @module @nhtio/adk/batteries/embeddings/transformers_js/adapter\n *\n * @remarks\n * Embeddings battery backed by transformers.js's `feature-extraction` pipeline. **Environment-neutral**\n * — runs in Node (via `onnxruntime-node`) and the browser (via `onnxruntime-web` / WebGPU), auto-\n * selected by the package; there is no WebGPU requirement, so this battery is surfaced from the\n * environment-neutral `@nhtio/adk/batteries/embeddings` barrel alongside the OpenAI one.\n *\n * Same user-facing surface as the OpenAI / WebLLM embeddings batteries (`isAvailable` / `dimensions` /\n * `preload` / `reset` / `embed` / `embedMany`), same `number[]` return shape, same query/document\n * prefix handling (the shared `applyEmbeddingPrefix`).\n *\n * `@huggingface/transformers` is an optional peer dependency, imported lazily.\n *\n * **Cross-runtime vector caveat:** embeddings produced here are not guaranteed bit-identical to those\n * from a different runtime (WebLLM/MLC, or even node-ONNX vs web-ONNX for the same model). A vector\n * corpus must be embedded AND queried by one backend.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { validateOptions } from './validation'\nimport { applyEmbeddingPrefix } from '../openai/helpers'\nimport {\n E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS,\n E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR,\n} from './exceptions'\nimport type { EmbedOptions } from '../openai/types'\nimport type {\n TransformersJsEmbeddingsAdapterOptions,\n TransformersJsEmbeddingsPipeline,\n CreateTransformersJsEmbeddingsPipeline,\n} from './types'\n\nconst defaultCreatePipeline: CreateTransformersJsEmbeddingsPipeline = async ({\n model,\n device,\n dtype,\n onInitProgress,\n}) => {\n const { pipeline } = await import('@huggingface/transformers')\n return (await pipeline('feature-extraction', model, {\n ...(device ? { device } : {}),\n ...(dtype ? { dtype } : {}),\n ...(onInitProgress ? { progress_callback: onInitProgress } : {}),\n } as never)) as unknown as TransformersJsEmbeddingsPipeline\n}\n\n/**\n * Embeddings adapter for transformers.js's feature-extraction pipeline.\n *\n * @remarks\n * Reusable: construct once, call {@link TransformersJsEmbeddingsAdapter.embed} / {@link embedMany} as\n * many times as needed. The pipeline is resolved lazily on first use (or via {@link preload}) and\n * cached with single-flight semantics so concurrent calls share one load.\n */\nexport class TransformersJsEmbeddingsAdapter {\n readonly #options: TransformersJsEmbeddingsAdapterOptions\n #pipeline: TransformersJsEmbeddingsPipeline | undefined\n #pipelinePromise: Promise<TransformersJsEmbeddingsPipeline> | undefined\n\n /**\n * Whether this battery is available. transformers.js is environment-neutral (Node + browser), so\n * this is `true` whenever the runtime can import the peer — there is no WebGPU requirement.\n */\n public static isAvailable(): boolean {\n return true\n }\n\n /**\n * @param options - Constructor options. Validated eagerly.\n * @throws {@link @nhtio/adk/batteries!E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS} when invalid.\n */\n constructor(options: unknown) {\n this.#options = validateOptions(options)\n this.#pipeline = this.#options.pipeline\n }\n\n /** Declared output dimensionality (from options), or `undefined` if not configured. */\n get dimensions(): number | undefined {\n return this.#options.dimensions\n }\n\n /** Instance availability probe (honours an injected `isAvailable`). */\n isAvailable(): boolean {\n return (this.#options.isAvailable ?? TransformersJsEmbeddingsAdapter.isAvailable)()\n }\n\n /** Eagerly loads (and caches) the pipeline so the first `embed` call is fast. Idempotent. */\n async preload(): Promise<void> {\n await this.#resolvePipeline()\n }\n\n /** Drops the cached pipeline and in-flight load so the next call reloads. */\n reset(): void {\n this.#pipeline = undefined\n this.#pipelinePromise = undefined\n }\n\n async #resolvePipeline(): Promise<TransformersJsEmbeddingsPipeline> {\n if (this.#pipeline) return this.#pipeline\n if (!this.isAvailable()) {\n throw new E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS([\n 'the transformers.js embeddings battery is not available in this runtime',\n ])\n }\n this.#pipelinePromise ??= (async () => {\n const createPipeline = this.#options.createPipeline ?? defaultCreatePipeline\n try {\n const pipe = await createPipeline({\n model: this.#options.model,\n device: this.#options.device,\n dtype: this.#options.dtype,\n onInitProgress: this.#options.onInitProgress,\n })\n this.#pipeline = pipe\n return pipe\n } catch (err) {\n this.#pipelinePromise = undefined\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n `could not load the transformers.js pipeline: ${isError(err) ? err.message : String(err)} — install the peer dependency (pnpm add @huggingface/transformers)`,\n ])\n }\n })()\n return this.#pipelinePromise\n }\n\n /**\n * Embeds a single string.\n *\n * @param text - The input text.\n * @param opts - Per-call options (`kind`).\n * @returns The embedding vector as a plain `number[]`.\n */\n async embed(text: string, opts?: EmbedOptions): Promise<number[]> {\n const [vec] = await this.embedMany([text], opts)\n return vec\n }\n\n /**\n * Embeds a batch of strings in a single pipeline call.\n *\n * @param texts - The input texts.\n * @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.\n * @returns One embedding vector per input, in input order, each a plain `number[]`.\n * @throws {@link @nhtio/adk/batteries!E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR} when the call fails\n * or returns a malformed result.\n */\n async embedMany(texts: string[], opts?: EmbedOptions): Promise<number[][]> {\n if (texts.length === 0) return []\n const kind = opts?.kind ?? 'document'\n const input = applyEmbeddingPrefix(texts, kind, this.#options)\n\n const pipe = await this.#resolvePipeline()\n\n let tensor: { tolist: () => unknown; dims?: number[] }\n try {\n tensor = (await (pipe as unknown as (i: unknown, o: unknown) => Promise<unknown>)(input, {\n pooling: this.#options.pooling ?? 'mean',\n normalize: this.#options.normalize ?? true,\n })) as { tolist: () => unknown; dims?: number[] }\n } catch (err) {\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n isError(err) ? err.message : String(err),\n ])\n }\n\n if (!tensor || typeof tensor.tolist !== 'function') {\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n 'feature-extraction returned a non-Tensor result',\n ])\n }\n\n // With pooling, the Tensor is [batch, hidden] → tolist() yields number[][].\n const list = tensor.tolist()\n const vectors =\n Array.isArray(list) && Array.isArray(list[0]) ? (list as number[][]) : [list as number[]]\n\n if (vectors.length !== input.length) {\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n `expected ${input.length} vectors, got ${vectors.length}`,\n ])\n }\n return vectors\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,IAAM,wBAAgE,OAAO,EAC3E,OACA,QACA,OACA,qBACI;CACJ,MAAM,EAAE,aAAa,MAAM,OAAO;CAClC,OAAQ,MAAM,SAAS,sBAAsB,OAAO;EAClD,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC3B,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;EACzB,GAAI,iBAAiB,EAAE,mBAAmB,eAAe,IAAI,CAAC;CAChE,CAAU;AACZ;;;;;;;;;AAUA,IAAa,kCAAb,MAAa,gCAAgC;CAC3C;CACA;CACA;;;;;CAMA,OAAc,cAAuB;EACnC,OAAO;CACT;;;;;CAMA,YAAY,SAAkB;EAC5B,KAAKA,WAAW,wDAAA,gBAAgB,OAAO;EACvC,KAAKC,YAAY,KAAKD,SAAS;CACjC;;CAGA,IAAI,aAAiC;EACnC,OAAO,KAAKA,SAAS;CACvB;;CAGA,cAAuB;EACrB,QAAQ,KAAKA,SAAS,eAAe,gCAAgC,aAAa;CACpF;;CAGA,MAAM,UAAyB;EAC7B,MAAM,KAAKE,iBAAiB;CAC9B;;CAGA,QAAc;EACZ,KAAKD,YAAY,KAAA;EACjB,KAAKE,mBAAmB,KAAA;CAC1B;CAEA,MAAMD,mBAA8D;EAClE,IAAI,KAAKD,WAAW,OAAO,KAAKA;EAChC,IAAI,CAAC,KAAK,YAAY,GACpB,MAAM,IAAI,wDAAA,6CAA6C,CACrD,yEACF,CAAC;EAEH,KAAKE,sBAAsB,YAAY;GACrC,MAAM,iBAAiB,KAAKH,SAAS,kBAAkB;GACvD,IAAI;IACF,MAAM,OAAO,MAAM,eAAe;KAChC,OAAO,KAAKA,SAAS;KACrB,QAAQ,KAAKA,SAAS;KACtB,OAAO,KAAKA,SAAS;KACrB,gBAAgB,KAAKA,SAAS;IAChC,CAAC;IACD,KAAKC,YAAY;IACjB,OAAO;GACT,SAAS,KAAK;IACZ,KAAKE,mBAAmB,KAAA;IACxB,MAAM,IAAI,wDAAA,0CAA0C,CAClD,gDAAgD,sBAAA,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG,EAAE,oEAC3F,CAAC;GACH;EACF,GAAG;EACH,OAAO,KAAKA;CACd;;;;;;;;CASA,MAAM,MAAM,MAAc,MAAwC;EAChE,MAAM,CAAC,OAAO,MAAM,KAAK,UAAU,CAAC,IAAI,GAAG,IAAI;EAC/C,OAAO;CACT;;;;;;;;;;CAWA,MAAM,UAAU,OAAiB,MAA0C;EACzE,IAAI,MAAM,WAAW,GAAG,OAAO,CAAC;EAEhC,MAAM,QAAQ,4CAAA,qBAAqB,OADtB,MAAM,QAAQ,YACqB,KAAKH,QAAQ;EAE7D,MAAM,OAAO,MAAM,KAAKE,iBAAiB;EAEzC,IAAI;EACJ,IAAI;GACF,SAAU,MAAO,KAAiE,OAAO;IACvF,SAAS,KAAKF,SAAS,WAAW;IAClC,WAAW,KAAKA,SAAS,aAAa;GACxC,CAAC;EACH,SAAS,KAAK;GACZ,MAAM,IAAI,wDAAA,0CAA0C,CAClD,sBAAA,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG,CACzC,CAAC;EACH;EAEA,IAAI,CAAC,UAAU,OAAO,OAAO,WAAW,YACtC,MAAM,IAAI,wDAAA,0CAA0C,CAClD,iDACF,CAAC;EAIH,MAAM,OAAO,OAAO,OAAO;EAC3B,MAAM,UACJ,MAAM,QAAQ,IAAI,KAAK,MAAM,QAAQ,KAAK,EAAE,IAAK,OAAsB,CAAC,IAAgB;EAE1F,IAAI,QAAQ,WAAW,MAAM,QAC3B,MAAM,IAAI,wDAAA,0CAA0C,CAClD,YAAY,MAAM,OAAO,gBAAgB,QAAQ,QACnD,CAAC;EAEH,OAAO;CACT;AACF"}
|
|
1
|
+
{"version":3,"file":"adapter.cjs","names":["#options","#pipeline","#resolvePipeline","#pipelinePromise"],"sources":["../../../../src/batteries/embeddings/transformers_js/adapter.ts"],"sourcesContent":["/**\n * transformers.js (ONNX, dual-environment) Embeddings adapter battery.\n *\n * @module @nhtio/adk/batteries/embeddings/transformers_js/adapter\n *\n * @remarks\n * Embeddings battery backed by transformers.js's `feature-extraction` pipeline. **Environment-neutral**\n * — runs in Node (via `onnxruntime-node`) and the browser (via `onnxruntime-web` / WebGPU), auto-\n * selected by the package; there is no WebGPU requirement, so this battery is surfaced from the\n * environment-neutral `@nhtio/adk/batteries/embeddings` barrel alongside the OpenAI one.\n *\n * Same user-facing surface as the OpenAI / WebLLM embeddings batteries (`isAvailable` / `dimensions` /\n * `preload` / `reset` / `embed` / `embedMany`), same `number[]` return shape, same query/document\n * prefix handling (the shared `applyEmbeddingPrefix`).\n *\n * `@huggingface/transformers` is an optional peer dependency, imported lazily.\n *\n * **Cross-runtime vector caveat:** embeddings produced here are not guaranteed bit-identical to those\n * from a different runtime (WebLLM/MLC, or even node-ONNX vs web-ONNX for the same model). A vector\n * corpus must be embedded AND queried by one backend.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { poolAndNormalize } from './pooling'\nimport { validateOptions } from './validation'\nimport { applyEmbeddingPrefix } from '../openai/helpers'\nimport { emitLifecycle } from '../../llm/chat_common/lifecycle'\nimport { withModelSource } from '../../llm/transformers_js/model_source'\nimport {\n E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS,\n E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR,\n} from './exceptions'\nimport type { EmbedOptions } from '../openai/types'\nimport type {\n TransformersJsEmbeddingsAdapterOptions,\n TransformersJsEmbeddingsPipeline,\n CreateTransformersJsEmbeddingsPipeline,\n} from './types'\n\nconst makeDefaultCreatePipeline = (\n modelSource: TransformersJsEmbeddingsAdapterOptions['modelSource']\n): CreateTransformersJsEmbeddingsPipeline => {\n return async ({ model, device, dtype, onInitProgress }) => {\n const transformers = await import('@huggingface/transformers')\n const { pipeline, env } = transformers\n const load = async () =>\n (await pipeline('feature-extraction', model, {\n ...(device ? { device } : {}),\n ...(dtype ? { dtype } : {}),\n ...(onInitProgress ? { progress_callback: onInitProgress } : {}),\n } as never)) as unknown as TransformersJsEmbeddingsPipeline\n // When a custom model source is configured, serve files through it behind the global-`env` mutex.\n return modelSource ? withModelSource(env as never, modelSource, load) : load()\n }\n}\n\n/**\n * Embeddings adapter for transformers.js's feature-extraction pipeline.\n *\n * @remarks\n * Reusable: construct once, call {@link TransformersJsEmbeddingsAdapter.embed} / {@link embedMany} as\n * many times as needed. The pipeline is resolved lazily on first use (or via {@link preload}) and\n * cached with single-flight semantics so concurrent calls share one load.\n */\nexport class TransformersJsEmbeddingsAdapter {\n readonly #options: TransformersJsEmbeddingsAdapterOptions\n #pipeline: TransformersJsEmbeddingsPipeline | undefined\n #pipelinePromise: Promise<TransformersJsEmbeddingsPipeline> | undefined\n\n /**\n * Whether this battery is available. transformers.js is environment-neutral (Node + browser), so\n * this is `true` whenever the runtime can import the peer — there is no WebGPU requirement.\n */\n public static isAvailable(): boolean {\n return true\n }\n\n /**\n * @param options - Constructor options. Validated eagerly.\n * @throws {@link @nhtio/adk/batteries!E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS} when invalid.\n */\n constructor(options: unknown) {\n this.#options = validateOptions(options)\n this.#pipeline = this.#options.pipeline\n }\n\n /** Declared output dimensionality (from options), or `undefined` if not configured. */\n get dimensions(): number | undefined {\n return this.#options.dimensions\n }\n\n /** Instance availability probe (honours an injected `isAvailable`). */\n isAvailable(): boolean {\n return (this.#options.isAvailable ?? TransformersJsEmbeddingsAdapter.isAvailable)()\n }\n\n /** Eagerly loads (and caches) the pipeline so the first `embed` call is fast. Idempotent. */\n async preload(): Promise<void> {\n await this.#resolvePipeline()\n }\n\n /** Drops the cached pipeline and in-flight load so the next call reloads. */\n reset(): void {\n this.#pipeline = undefined\n this.#pipelinePromise = undefined\n }\n\n /**\n * Release the loaded model's ONNX sessions + GPU/wasm buffers, then drop the cached pipeline.\n *\n * @remarks\n * `reset()` only nulls the JS reference; the native ONNX Runtime sessions and WebGPU/wasm device memory\n * stay alive until GC. Loading many embedding models back-to-back in one browser session (e.g. a full\n * matrix run) accumulates those sessions until the heap is exhausted. `FeatureExtractionPipeline`\n * extends `Pipeline`, which exposes `dispose()` — this awaits it so the memory is reclaimed between\n * loads, swallows a disposal error (teardown must not throw), and finishes with `reset()`. Idempotent.\n */\n async dispose(): Promise<void> {\n const pipeline = this.#pipeline ?? (await this.#pipelinePromise?.catch(() => undefined))\n const pipeWithDispose = pipeline as { dispose?: () => Promise<unknown> } | undefined\n if (typeof pipeWithDispose?.dispose === 'function') {\n await Promise.resolve(pipeWithDispose.dispose()).catch(() => undefined)\n }\n this.reset()\n }\n\n async #resolvePipeline(): Promise<TransformersJsEmbeddingsPipeline> {\n if (this.#pipeline) return this.#pipeline\n if (!this.isAvailable()) {\n throw new E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS([\n 'the transformers.js embeddings battery is not available in this runtime',\n ])\n }\n const opts = this.#options\n this.#pipelinePromise ??= (async () => {\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'loading', {\n detail: 'loading feature-extraction pipeline',\n })\n // Forward each provider download event into a normalized `loading` lifecycle report.\n const hasLifecycle =\n opts.onLifecycle ?? opts.onLoading ?? opts.onReady ?? opts.onGenerating ?? opts.onError\n const forwardedInitProgress = hasLifecycle\n ? (info: unknown) => {\n const p = (info as { progress?: number } | undefined)?.progress\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'loading', {\n ...(typeof p === 'number' ? { progress: p / 100 } : {}),\n raw: info,\n })\n opts.onInitProgress?.(info as never)\n }\n : opts.onInitProgress\n const createPipeline = opts.createPipeline ?? makeDefaultCreatePipeline(opts.modelSource)\n try {\n // `from_pretrained` covers both fetch (reported via progress_callback → `loading`) and the\n // ONNX-graph / WebGPU-WASM warmup. Mark the latter as `compiling` — a COARSE upper-bound marker\n // (fetch + compile overlap inside the call), consistent with the LLM batteries.\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'compiling', {\n detail: 'compiling feature-extraction graph',\n })\n const pipe = await createPipeline({\n model: opts.model,\n device: opts.device,\n dtype: opts.dtype,\n onInitProgress: forwardedInitProgress,\n })\n this.#pipeline = pipe\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'ready', {\n detail: 'feature-extraction pipeline ready',\n })\n return pipe\n } catch (err) {\n this.#pipelinePromise = undefined\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'error', { error: err })\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n `could not load the transformers.js pipeline: ${isError(err) ? err.message : String(err)} — install the peer dependency (pnpm add @huggingface/transformers)`,\n ])\n }\n })()\n return this.#pipelinePromise\n }\n\n /**\n * Embeds a single string.\n *\n * @param text - The input text.\n * @param opts - Per-call options (`kind`).\n * @returns The embedding vector as a plain `number[]`.\n */\n async embed(text: string, opts?: EmbedOptions): Promise<number[]> {\n const [vec] = await this.embedMany([text], opts)\n return vec\n }\n\n /**\n * Embeds a batch of strings in a single pipeline call.\n *\n * @param texts - The input texts.\n * @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.\n * @returns One embedding vector per input, in input order, each a plain `number[]`.\n * @throws {@link @nhtio/adk/batteries!E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR} when the call fails\n * or returns a malformed result.\n */\n async embedMany(texts: string[], opts?: EmbedOptions): Promise<number[][]> {\n if (texts.length === 0) return []\n const kind = opts?.kind ?? 'document'\n const input = applyEmbeddingPrefix(texts, kind, this.#options)\n\n const pipe = await this.#resolvePipeline()\n const pooling = this.#options.pooling ?? 'mean'\n const normalize = this.#options.normalize ?? true\n const battery = (this.#options.poolingOwner ?? 'engine') === 'battery'\n\n emitLifecycle(this.#options, 'transformers_js_embed', this.#options.model, 'generating')\n\n let tensor: { tolist: () => unknown; dims?: number[] }\n try {\n tensor = (await (pipe as unknown as (i: unknown, o: unknown) => Promise<unknown>)(\n input,\n // 'battery' owner: request RAW token states (no engine pooling/normalize) and do it ourselves\n // in deterministic JS. 'engine' owner: delegate to the pipeline exactly as before.\n battery ? { pooling: 'none' } : { pooling, normalize }\n )) as { tolist: () => unknown; dims?: number[] }\n } catch (err) {\n emitLifecycle(this.#options, 'transformers_js_embed', this.#options.model, 'error', {\n error: err,\n })\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n isError(err) ? err.message : String(err),\n ])\n }\n\n if (!tensor || typeof tensor.tolist !== 'function') {\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n 'feature-extraction returned a non-Tensor result',\n ])\n }\n\n const list = tensor.tolist()\n let vectors: number[][]\n if (battery) {\n // Raw states: tolist() → [batch, seq, hidden]. A single ungrouped input may come back as\n // [seq, hidden] → wrap to a batch of one. Pool + normalize deterministically.\n const states = list as unknown[]\n const tokenStates = (\n Array.isArray(states) &&\n Array.isArray(states[0]) &&\n Array.isArray((states[0] as unknown[])[0])\n ? states\n : [states]\n ) as number[][][]\n vectors = poolAndNormalize(tokenStates, pooling, normalize)\n } else {\n // With engine pooling, the Tensor is [batch, hidden] → tolist() yields number[][].\n vectors =\n Array.isArray(list) && Array.isArray(list[0]) ? (list as number[][]) : [list as number[]]\n }\n\n if (vectors.length !== input.length) {\n emitLifecycle(this.#options, 'transformers_js_embed', this.#options.model, 'error', {\n error: new Error(`expected ${input.length} vectors, got ${vectors.length}`),\n })\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n `expected ${input.length} vectors, got ${vectors.length}`,\n ])\n }\n emitLifecycle(this.#options, 'transformers_js_embed', this.#options.model, 'complete')\n return vectors\n }\n}\n"],"mappings":"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"}
|
|
@@ -48,6 +48,17 @@ export declare class TransformersJsEmbeddingsAdapter {
|
|
|
48
48
|
preload(): Promise<void>;
|
|
49
49
|
/** Drops the cached pipeline and in-flight load so the next call reloads. */
|
|
50
50
|
reset(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Release the loaded model's ONNX sessions + GPU/wasm buffers, then drop the cached pipeline.
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* `reset()` only nulls the JS reference; the native ONNX Runtime sessions and WebGPU/wasm device memory
|
|
56
|
+
* stay alive until GC. Loading many embedding models back-to-back in one browser session (e.g. a full
|
|
57
|
+
* matrix run) accumulates those sessions until the heap is exhausted. `FeatureExtractionPipeline`
|
|
58
|
+
* extends `Pipeline`, which exposes `dispose()` — this awaits it so the memory is reclaimed between
|
|
59
|
+
* loads, swallows a disposal error (teardown must not throw), and finishes with `reset()`. Idempotent.
|
|
60
|
+
*/
|
|
61
|
+
dispose(): Promise<void>;
|
|
51
62
|
/**
|
|
52
63
|
* Embeds a single string.
|
|
53
64
|
*
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { c as isError } from "../../../tool_registry-
|
|
1
|
+
import { c as isError } from "../../../tool_registry-Bj0JhXCv.mjs";
|
|
2
2
|
import "../../../guards.mjs";
|
|
3
|
+
import { n as emitLifecycle } from "../../../lifecycle-C4kwYJ-N.mjs";
|
|
4
|
+
import { withModelSource } from "../../llm/transformers_js/model_source.mjs";
|
|
3
5
|
import { applyEmbeddingPrefix } from "../openai/helpers.mjs";
|
|
6
|
+
import { poolAndNormalize } from "./pooling.mjs";
|
|
4
7
|
import { E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS, E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR } from "./exceptions.mjs";
|
|
5
8
|
import { validateOptions } from "./validation.mjs";
|
|
6
9
|
//#region src/batteries/embeddings/transformers_js/adapter.ts
|
|
@@ -25,13 +28,16 @@ import { validateOptions } from "./validation.mjs";
|
|
|
25
28
|
* from a different runtime (WebLLM/MLC, or even node-ONNX vs web-ONNX for the same model). A vector
|
|
26
29
|
* corpus must be embedded AND queried by one backend.
|
|
27
30
|
*/
|
|
28
|
-
var
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
var makeDefaultCreatePipeline = (modelSource) => {
|
|
32
|
+
return async ({ model, device, dtype, onInitProgress }) => {
|
|
33
|
+
const { pipeline, env } = await import("@huggingface/transformers");
|
|
34
|
+
const load = async () => await pipeline("feature-extraction", model, {
|
|
35
|
+
...device ? { device } : {},
|
|
36
|
+
...dtype ? { dtype } : {},
|
|
37
|
+
...onInitProgress ? { progress_callback: onInitProgress } : {}
|
|
38
|
+
});
|
|
39
|
+
return modelSource ? withModelSource(env, modelSource, load) : load();
|
|
40
|
+
};
|
|
35
41
|
};
|
|
36
42
|
/**
|
|
37
43
|
* Embeddings adapter for transformers.js's feature-extraction pipeline.
|
|
@@ -77,22 +83,50 @@ var TransformersJsEmbeddingsAdapter = class TransformersJsEmbeddingsAdapter {
|
|
|
77
83
|
this.#pipeline = void 0;
|
|
78
84
|
this.#pipelinePromise = void 0;
|
|
79
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Release the loaded model's ONNX sessions + GPU/wasm buffers, then drop the cached pipeline.
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* `reset()` only nulls the JS reference; the native ONNX Runtime sessions and WebGPU/wasm device memory
|
|
91
|
+
* stay alive until GC. Loading many embedding models back-to-back in one browser session (e.g. a full
|
|
92
|
+
* matrix run) accumulates those sessions until the heap is exhausted. `FeatureExtractionPipeline`
|
|
93
|
+
* extends `Pipeline`, which exposes `dispose()` — this awaits it so the memory is reclaimed between
|
|
94
|
+
* loads, swallows a disposal error (teardown must not throw), and finishes with `reset()`. Idempotent.
|
|
95
|
+
*/
|
|
96
|
+
async dispose() {
|
|
97
|
+
const pipeWithDispose = this.#pipeline ?? await this.#pipelinePromise?.catch(() => void 0);
|
|
98
|
+
if (typeof pipeWithDispose?.dispose === "function") await Promise.resolve(pipeWithDispose.dispose()).catch(() => void 0);
|
|
99
|
+
this.reset();
|
|
100
|
+
}
|
|
80
101
|
async #resolvePipeline() {
|
|
81
102
|
if (this.#pipeline) return this.#pipeline;
|
|
82
103
|
if (!this.isAvailable()) throw new E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS(["the transformers.js embeddings battery is not available in this runtime"]);
|
|
104
|
+
const opts = this.#options;
|
|
83
105
|
this.#pipelinePromise ??= (async () => {
|
|
84
|
-
|
|
106
|
+
emitLifecycle(opts, "transformers_js_embed", opts.model, "loading", { detail: "loading feature-extraction pipeline" });
|
|
107
|
+
const forwardedInitProgress = opts.onLifecycle ?? opts.onLoading ?? opts.onReady ?? opts.onGenerating ?? opts.onError ? (info) => {
|
|
108
|
+
const p = info?.progress;
|
|
109
|
+
emitLifecycle(opts, "transformers_js_embed", opts.model, "loading", {
|
|
110
|
+
...typeof p === "number" ? { progress: p / 100 } : {},
|
|
111
|
+
raw: info
|
|
112
|
+
});
|
|
113
|
+
opts.onInitProgress?.(info);
|
|
114
|
+
} : opts.onInitProgress;
|
|
115
|
+
const createPipeline = opts.createPipeline ?? makeDefaultCreatePipeline(opts.modelSource);
|
|
85
116
|
try {
|
|
117
|
+
emitLifecycle(opts, "transformers_js_embed", opts.model, "compiling", { detail: "compiling feature-extraction graph" });
|
|
86
118
|
const pipe = await createPipeline({
|
|
87
|
-
model:
|
|
88
|
-
device:
|
|
89
|
-
dtype:
|
|
90
|
-
onInitProgress:
|
|
119
|
+
model: opts.model,
|
|
120
|
+
device: opts.device,
|
|
121
|
+
dtype: opts.dtype,
|
|
122
|
+
onInitProgress: forwardedInitProgress
|
|
91
123
|
});
|
|
92
124
|
this.#pipeline = pipe;
|
|
125
|
+
emitLifecycle(opts, "transformers_js_embed", opts.model, "ready", { detail: "feature-extraction pipeline ready" });
|
|
93
126
|
return pipe;
|
|
94
127
|
} catch (err) {
|
|
95
128
|
this.#pipelinePromise = void 0;
|
|
129
|
+
emitLifecycle(opts, "transformers_js_embed", opts.model, "error", { error: err });
|
|
96
130
|
throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([`could not load the transformers.js pipeline: ${isError(err) ? err.message : String(err)} — install the peer dependency (pnpm add @huggingface/transformers)`]);
|
|
97
131
|
}
|
|
98
132
|
})();
|
|
@@ -122,19 +156,32 @@ var TransformersJsEmbeddingsAdapter = class TransformersJsEmbeddingsAdapter {
|
|
|
122
156
|
if (texts.length === 0) return [];
|
|
123
157
|
const input = applyEmbeddingPrefix(texts, opts?.kind ?? "document", this.#options);
|
|
124
158
|
const pipe = await this.#resolvePipeline();
|
|
159
|
+
const pooling = this.#options.pooling ?? "mean";
|
|
160
|
+
const normalize = this.#options.normalize ?? true;
|
|
161
|
+
const battery = (this.#options.poolingOwner ?? "engine") === "battery";
|
|
162
|
+
emitLifecycle(this.#options, "transformers_js_embed", this.#options.model, "generating");
|
|
125
163
|
let tensor;
|
|
126
164
|
try {
|
|
127
|
-
tensor = await pipe(input, {
|
|
128
|
-
pooling
|
|
129
|
-
normalize
|
|
165
|
+
tensor = await pipe(input, battery ? { pooling: "none" } : {
|
|
166
|
+
pooling,
|
|
167
|
+
normalize
|
|
130
168
|
});
|
|
131
169
|
} catch (err) {
|
|
170
|
+
emitLifecycle(this.#options, "transformers_js_embed", this.#options.model, "error", { error: err });
|
|
132
171
|
throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([isError(err) ? err.message : String(err)]);
|
|
133
172
|
}
|
|
134
173
|
if (!tensor || typeof tensor.tolist !== "function") throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR(["feature-extraction returned a non-Tensor result"]);
|
|
135
174
|
const list = tensor.tolist();
|
|
136
|
-
|
|
137
|
-
if (
|
|
175
|
+
let vectors;
|
|
176
|
+
if (battery) {
|
|
177
|
+
const states = list;
|
|
178
|
+
vectors = poolAndNormalize(Array.isArray(states) && Array.isArray(states[0]) && Array.isArray(states[0][0]) ? states : [states], pooling, normalize);
|
|
179
|
+
} else vectors = Array.isArray(list) && Array.isArray(list[0]) ? list : [list];
|
|
180
|
+
if (vectors.length !== input.length) {
|
|
181
|
+
emitLifecycle(this.#options, "transformers_js_embed", this.#options.model, "error", { error: /* @__PURE__ */ new Error(`expected ${input.length} vectors, got ${vectors.length}`) });
|
|
182
|
+
throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([`expected ${input.length} vectors, got ${vectors.length}`]);
|
|
183
|
+
}
|
|
184
|
+
emitLifecycle(this.#options, "transformers_js_embed", this.#options.model, "complete");
|
|
138
185
|
return vectors;
|
|
139
186
|
}
|
|
140
187
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.mjs","names":["#options","#pipeline","#resolvePipeline","#pipelinePromise"],"sources":["../../../../src/batteries/embeddings/transformers_js/adapter.ts"],"sourcesContent":["/**\n * transformers.js (ONNX, dual-environment) Embeddings adapter battery.\n *\n * @module @nhtio/adk/batteries/embeddings/transformers_js/adapter\n *\n * @remarks\n * Embeddings battery backed by transformers.js's `feature-extraction` pipeline. **Environment-neutral**\n * — runs in Node (via `onnxruntime-node`) and the browser (via `onnxruntime-web` / WebGPU), auto-\n * selected by the package; there is no WebGPU requirement, so this battery is surfaced from the\n * environment-neutral `@nhtio/adk/batteries/embeddings` barrel alongside the OpenAI one.\n *\n * Same user-facing surface as the OpenAI / WebLLM embeddings batteries (`isAvailable` / `dimensions` /\n * `preload` / `reset` / `embed` / `embedMany`), same `number[]` return shape, same query/document\n * prefix handling (the shared `applyEmbeddingPrefix`).\n *\n * `@huggingface/transformers` is an optional peer dependency, imported lazily.\n *\n * **Cross-runtime vector caveat:** embeddings produced here are not guaranteed bit-identical to those\n * from a different runtime (WebLLM/MLC, or even node-ONNX vs web-ONNX for the same model). A vector\n * corpus must be embedded AND queried by one backend.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { validateOptions } from './validation'\nimport { applyEmbeddingPrefix } from '../openai/helpers'\nimport {\n E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS,\n E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR,\n} from './exceptions'\nimport type { EmbedOptions } from '../openai/types'\nimport type {\n TransformersJsEmbeddingsAdapterOptions,\n TransformersJsEmbeddingsPipeline,\n CreateTransformersJsEmbeddingsPipeline,\n} from './types'\n\nconst defaultCreatePipeline: CreateTransformersJsEmbeddingsPipeline = async ({\n model,\n device,\n dtype,\n onInitProgress,\n}) => {\n const { pipeline } = await import('@huggingface/transformers')\n return (await pipeline('feature-extraction', model, {\n ...(device ? { device } : {}),\n ...(dtype ? { dtype } : {}),\n ...(onInitProgress ? { progress_callback: onInitProgress } : {}),\n } as never)) as unknown as TransformersJsEmbeddingsPipeline\n}\n\n/**\n * Embeddings adapter for transformers.js's feature-extraction pipeline.\n *\n * @remarks\n * Reusable: construct once, call {@link TransformersJsEmbeddingsAdapter.embed} / {@link embedMany} as\n * many times as needed. The pipeline is resolved lazily on first use (or via {@link preload}) and\n * cached with single-flight semantics so concurrent calls share one load.\n */\nexport class TransformersJsEmbeddingsAdapter {\n readonly #options: TransformersJsEmbeddingsAdapterOptions\n #pipeline: TransformersJsEmbeddingsPipeline | undefined\n #pipelinePromise: Promise<TransformersJsEmbeddingsPipeline> | undefined\n\n /**\n * Whether this battery is available. transformers.js is environment-neutral (Node + browser), so\n * this is `true` whenever the runtime can import the peer — there is no WebGPU requirement.\n */\n public static isAvailable(): boolean {\n return true\n }\n\n /**\n * @param options - Constructor options. Validated eagerly.\n * @throws {@link @nhtio/adk/batteries!E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS} when invalid.\n */\n constructor(options: unknown) {\n this.#options = validateOptions(options)\n this.#pipeline = this.#options.pipeline\n }\n\n /** Declared output dimensionality (from options), or `undefined` if not configured. */\n get dimensions(): number | undefined {\n return this.#options.dimensions\n }\n\n /** Instance availability probe (honours an injected `isAvailable`). */\n isAvailable(): boolean {\n return (this.#options.isAvailable ?? TransformersJsEmbeddingsAdapter.isAvailable)()\n }\n\n /** Eagerly loads (and caches) the pipeline so the first `embed` call is fast. Idempotent. */\n async preload(): Promise<void> {\n await this.#resolvePipeline()\n }\n\n /** Drops the cached pipeline and in-flight load so the next call reloads. */\n reset(): void {\n this.#pipeline = undefined\n this.#pipelinePromise = undefined\n }\n\n async #resolvePipeline(): Promise<TransformersJsEmbeddingsPipeline> {\n if (this.#pipeline) return this.#pipeline\n if (!this.isAvailable()) {\n throw new E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS([\n 'the transformers.js embeddings battery is not available in this runtime',\n ])\n }\n this.#pipelinePromise ??= (async () => {\n const createPipeline = this.#options.createPipeline ?? defaultCreatePipeline\n try {\n const pipe = await createPipeline({\n model: this.#options.model,\n device: this.#options.device,\n dtype: this.#options.dtype,\n onInitProgress: this.#options.onInitProgress,\n })\n this.#pipeline = pipe\n return pipe\n } catch (err) {\n this.#pipelinePromise = undefined\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n `could not load the transformers.js pipeline: ${isError(err) ? err.message : String(err)} — install the peer dependency (pnpm add @huggingface/transformers)`,\n ])\n }\n })()\n return this.#pipelinePromise\n }\n\n /**\n * Embeds a single string.\n *\n * @param text - The input text.\n * @param opts - Per-call options (`kind`).\n * @returns The embedding vector as a plain `number[]`.\n */\n async embed(text: string, opts?: EmbedOptions): Promise<number[]> {\n const [vec] = await this.embedMany([text], opts)\n return vec\n }\n\n /**\n * Embeds a batch of strings in a single pipeline call.\n *\n * @param texts - The input texts.\n * @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.\n * @returns One embedding vector per input, in input order, each a plain `number[]`.\n * @throws {@link @nhtio/adk/batteries!E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR} when the call fails\n * or returns a malformed result.\n */\n async embedMany(texts: string[], opts?: EmbedOptions): Promise<number[][]> {\n if (texts.length === 0) return []\n const kind = opts?.kind ?? 'document'\n const input = applyEmbeddingPrefix(texts, kind, this.#options)\n\n const pipe = await this.#resolvePipeline()\n\n let tensor: { tolist: () => unknown; dims?: number[] }\n try {\n tensor = (await (pipe as unknown as (i: unknown, o: unknown) => Promise<unknown>)(input, {\n pooling: this.#options.pooling ?? 'mean',\n normalize: this.#options.normalize ?? true,\n })) as { tolist: () => unknown; dims?: number[] }\n } catch (err) {\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n isError(err) ? err.message : String(err),\n ])\n }\n\n if (!tensor || typeof tensor.tolist !== 'function') {\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n 'feature-extraction returned a non-Tensor result',\n ])\n }\n\n // With pooling, the Tensor is [batch, hidden] → tolist() yields number[][].\n const list = tensor.tolist()\n const vectors =\n Array.isArray(list) && Array.isArray(list[0]) ? (list as number[][]) : [list as number[]]\n\n if (vectors.length !== input.length) {\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n `expected ${input.length} vectors, got ${vectors.length}`,\n ])\n }\n return vectors\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,IAAM,wBAAgE,OAAO,EAC3E,OACA,QACA,OACA,qBACI;CACJ,MAAM,EAAE,aAAa,MAAM,OAAO;CAClC,OAAQ,MAAM,SAAS,sBAAsB,OAAO;EAClD,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC3B,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;EACzB,GAAI,iBAAiB,EAAE,mBAAmB,eAAe,IAAI,CAAC;CAChE,CAAU;AACZ;;;;;;;;;AAUA,IAAa,kCAAb,MAAa,gCAAgC;CAC3C;CACA;CACA;;;;;CAMA,OAAc,cAAuB;EACnC,OAAO;CACT;;;;;CAMA,YAAY,SAAkB;EAC5B,KAAKA,WAAW,gBAAgB,OAAO;EACvC,KAAKC,YAAY,KAAKD,SAAS;CACjC;;CAGA,IAAI,aAAiC;EACnC,OAAO,KAAKA,SAAS;CACvB;;CAGA,cAAuB;EACrB,QAAQ,KAAKA,SAAS,eAAe,gCAAgC,aAAa;CACpF;;CAGA,MAAM,UAAyB;EAC7B,MAAM,KAAKE,iBAAiB;CAC9B;;CAGA,QAAc;EACZ,KAAKD,YAAY,KAAA;EACjB,KAAKE,mBAAmB,KAAA;CAC1B;CAEA,MAAMD,mBAA8D;EAClE,IAAI,KAAKD,WAAW,OAAO,KAAKA;EAChC,IAAI,CAAC,KAAK,YAAY,GACpB,MAAM,IAAI,6CAA6C,CACrD,yEACF,CAAC;EAEH,KAAKE,sBAAsB,YAAY;GACrC,MAAM,iBAAiB,KAAKH,SAAS,kBAAkB;GACvD,IAAI;IACF,MAAM,OAAO,MAAM,eAAe;KAChC,OAAO,KAAKA,SAAS;KACrB,QAAQ,KAAKA,SAAS;KACtB,OAAO,KAAKA,SAAS;KACrB,gBAAgB,KAAKA,SAAS;IAChC,CAAC;IACD,KAAKC,YAAY;IACjB,OAAO;GACT,SAAS,KAAK;IACZ,KAAKE,mBAAmB,KAAA;IACxB,MAAM,IAAI,0CAA0C,CAClD,gDAAgD,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG,EAAE,oEAC3F,CAAC;GACH;EACF,GAAG;EACH,OAAO,KAAKA;CACd;;;;;;;;CASA,MAAM,MAAM,MAAc,MAAwC;EAChE,MAAM,CAAC,OAAO,MAAM,KAAK,UAAU,CAAC,IAAI,GAAG,IAAI;EAC/C,OAAO;CACT;;;;;;;;;;CAWA,MAAM,UAAU,OAAiB,MAA0C;EACzE,IAAI,MAAM,WAAW,GAAG,OAAO,CAAC;EAEhC,MAAM,QAAQ,qBAAqB,OADtB,MAAM,QAAQ,YACqB,KAAKH,QAAQ;EAE7D,MAAM,OAAO,MAAM,KAAKE,iBAAiB;EAEzC,IAAI;EACJ,IAAI;GACF,SAAU,MAAO,KAAiE,OAAO;IACvF,SAAS,KAAKF,SAAS,WAAW;IAClC,WAAW,KAAKA,SAAS,aAAa;GACxC,CAAC;EACH,SAAS,KAAK;GACZ,MAAM,IAAI,0CAA0C,CAClD,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG,CACzC,CAAC;EACH;EAEA,IAAI,CAAC,UAAU,OAAO,OAAO,WAAW,YACtC,MAAM,IAAI,0CAA0C,CAClD,iDACF,CAAC;EAIH,MAAM,OAAO,OAAO,OAAO;EAC3B,MAAM,UACJ,MAAM,QAAQ,IAAI,KAAK,MAAM,QAAQ,KAAK,EAAE,IAAK,OAAsB,CAAC,IAAgB;EAE1F,IAAI,QAAQ,WAAW,MAAM,QAC3B,MAAM,IAAI,0CAA0C,CAClD,YAAY,MAAM,OAAO,gBAAgB,QAAQ,QACnD,CAAC;EAEH,OAAO;CACT;AACF"}
|
|
1
|
+
{"version":3,"file":"adapter.mjs","names":["#options","#pipeline","#resolvePipeline","#pipelinePromise"],"sources":["../../../../src/batteries/embeddings/transformers_js/adapter.ts"],"sourcesContent":["/**\n * transformers.js (ONNX, dual-environment) Embeddings adapter battery.\n *\n * @module @nhtio/adk/batteries/embeddings/transformers_js/adapter\n *\n * @remarks\n * Embeddings battery backed by transformers.js's `feature-extraction` pipeline. **Environment-neutral**\n * — runs in Node (via `onnxruntime-node`) and the browser (via `onnxruntime-web` / WebGPU), auto-\n * selected by the package; there is no WebGPU requirement, so this battery is surfaced from the\n * environment-neutral `@nhtio/adk/batteries/embeddings` barrel alongside the OpenAI one.\n *\n * Same user-facing surface as the OpenAI / WebLLM embeddings batteries (`isAvailable` / `dimensions` /\n * `preload` / `reset` / `embed` / `embedMany`), same `number[]` return shape, same query/document\n * prefix handling (the shared `applyEmbeddingPrefix`).\n *\n * `@huggingface/transformers` is an optional peer dependency, imported lazily.\n *\n * **Cross-runtime vector caveat:** embeddings produced here are not guaranteed bit-identical to those\n * from a different runtime (WebLLM/MLC, or even node-ONNX vs web-ONNX for the same model). A vector\n * corpus must be embedded AND queried by one backend.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { poolAndNormalize } from './pooling'\nimport { validateOptions } from './validation'\nimport { applyEmbeddingPrefix } from '../openai/helpers'\nimport { emitLifecycle } from '../../llm/chat_common/lifecycle'\nimport { withModelSource } from '../../llm/transformers_js/model_source'\nimport {\n E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS,\n E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR,\n} from './exceptions'\nimport type { EmbedOptions } from '../openai/types'\nimport type {\n TransformersJsEmbeddingsAdapterOptions,\n TransformersJsEmbeddingsPipeline,\n CreateTransformersJsEmbeddingsPipeline,\n} from './types'\n\nconst makeDefaultCreatePipeline = (\n modelSource: TransformersJsEmbeddingsAdapterOptions['modelSource']\n): CreateTransformersJsEmbeddingsPipeline => {\n return async ({ model, device, dtype, onInitProgress }) => {\n const transformers = await import('@huggingface/transformers')\n const { pipeline, env } = transformers\n const load = async () =>\n (await pipeline('feature-extraction', model, {\n ...(device ? { device } : {}),\n ...(dtype ? { dtype } : {}),\n ...(onInitProgress ? { progress_callback: onInitProgress } : {}),\n } as never)) as unknown as TransformersJsEmbeddingsPipeline\n // When a custom model source is configured, serve files through it behind the global-`env` mutex.\n return modelSource ? withModelSource(env as never, modelSource, load) : load()\n }\n}\n\n/**\n * Embeddings adapter for transformers.js's feature-extraction pipeline.\n *\n * @remarks\n * Reusable: construct once, call {@link TransformersJsEmbeddingsAdapter.embed} / {@link embedMany} as\n * many times as needed. The pipeline is resolved lazily on first use (or via {@link preload}) and\n * cached with single-flight semantics so concurrent calls share one load.\n */\nexport class TransformersJsEmbeddingsAdapter {\n readonly #options: TransformersJsEmbeddingsAdapterOptions\n #pipeline: TransformersJsEmbeddingsPipeline | undefined\n #pipelinePromise: Promise<TransformersJsEmbeddingsPipeline> | undefined\n\n /**\n * Whether this battery is available. transformers.js is environment-neutral (Node + browser), so\n * this is `true` whenever the runtime can import the peer — there is no WebGPU requirement.\n */\n public static isAvailable(): boolean {\n return true\n }\n\n /**\n * @param options - Constructor options. Validated eagerly.\n * @throws {@link @nhtio/adk/batteries!E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS} when invalid.\n */\n constructor(options: unknown) {\n this.#options = validateOptions(options)\n this.#pipeline = this.#options.pipeline\n }\n\n /** Declared output dimensionality (from options), or `undefined` if not configured. */\n get dimensions(): number | undefined {\n return this.#options.dimensions\n }\n\n /** Instance availability probe (honours an injected `isAvailable`). */\n isAvailable(): boolean {\n return (this.#options.isAvailable ?? TransformersJsEmbeddingsAdapter.isAvailable)()\n }\n\n /** Eagerly loads (and caches) the pipeline so the first `embed` call is fast. Idempotent. */\n async preload(): Promise<void> {\n await this.#resolvePipeline()\n }\n\n /** Drops the cached pipeline and in-flight load so the next call reloads. */\n reset(): void {\n this.#pipeline = undefined\n this.#pipelinePromise = undefined\n }\n\n /**\n * Release the loaded model's ONNX sessions + GPU/wasm buffers, then drop the cached pipeline.\n *\n * @remarks\n * `reset()` only nulls the JS reference; the native ONNX Runtime sessions and WebGPU/wasm device memory\n * stay alive until GC. Loading many embedding models back-to-back in one browser session (e.g. a full\n * matrix run) accumulates those sessions until the heap is exhausted. `FeatureExtractionPipeline`\n * extends `Pipeline`, which exposes `dispose()` — this awaits it so the memory is reclaimed between\n * loads, swallows a disposal error (teardown must not throw), and finishes with `reset()`. Idempotent.\n */\n async dispose(): Promise<void> {\n const pipeline = this.#pipeline ?? (await this.#pipelinePromise?.catch(() => undefined))\n const pipeWithDispose = pipeline as { dispose?: () => Promise<unknown> } | undefined\n if (typeof pipeWithDispose?.dispose === 'function') {\n await Promise.resolve(pipeWithDispose.dispose()).catch(() => undefined)\n }\n this.reset()\n }\n\n async #resolvePipeline(): Promise<TransformersJsEmbeddingsPipeline> {\n if (this.#pipeline) return this.#pipeline\n if (!this.isAvailable()) {\n throw new E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS([\n 'the transformers.js embeddings battery is not available in this runtime',\n ])\n }\n const opts = this.#options\n this.#pipelinePromise ??= (async () => {\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'loading', {\n detail: 'loading feature-extraction pipeline',\n })\n // Forward each provider download event into a normalized `loading` lifecycle report.\n const hasLifecycle =\n opts.onLifecycle ?? opts.onLoading ?? opts.onReady ?? opts.onGenerating ?? opts.onError\n const forwardedInitProgress = hasLifecycle\n ? (info: unknown) => {\n const p = (info as { progress?: number } | undefined)?.progress\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'loading', {\n ...(typeof p === 'number' ? { progress: p / 100 } : {}),\n raw: info,\n })\n opts.onInitProgress?.(info as never)\n }\n : opts.onInitProgress\n const createPipeline = opts.createPipeline ?? makeDefaultCreatePipeline(opts.modelSource)\n try {\n // `from_pretrained` covers both fetch (reported via progress_callback → `loading`) and the\n // ONNX-graph / WebGPU-WASM warmup. Mark the latter as `compiling` — a COARSE upper-bound marker\n // (fetch + compile overlap inside the call), consistent with the LLM batteries.\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'compiling', {\n detail: 'compiling feature-extraction graph',\n })\n const pipe = await createPipeline({\n model: opts.model,\n device: opts.device,\n dtype: opts.dtype,\n onInitProgress: forwardedInitProgress,\n })\n this.#pipeline = pipe\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'ready', {\n detail: 'feature-extraction pipeline ready',\n })\n return pipe\n } catch (err) {\n this.#pipelinePromise = undefined\n emitLifecycle(opts, 'transformers_js_embed', opts.model, 'error', { error: err })\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n `could not load the transformers.js pipeline: ${isError(err) ? err.message : String(err)} — install the peer dependency (pnpm add @huggingface/transformers)`,\n ])\n }\n })()\n return this.#pipelinePromise\n }\n\n /**\n * Embeds a single string.\n *\n * @param text - The input text.\n * @param opts - Per-call options (`kind`).\n * @returns The embedding vector as a plain `number[]`.\n */\n async embed(text: string, opts?: EmbedOptions): Promise<number[]> {\n const [vec] = await this.embedMany([text], opts)\n return vec\n }\n\n /**\n * Embeds a batch of strings in a single pipeline call.\n *\n * @param texts - The input texts.\n * @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.\n * @returns One embedding vector per input, in input order, each a plain `number[]`.\n * @throws {@link @nhtio/adk/batteries!E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR} when the call fails\n * or returns a malformed result.\n */\n async embedMany(texts: string[], opts?: EmbedOptions): Promise<number[][]> {\n if (texts.length === 0) return []\n const kind = opts?.kind ?? 'document'\n const input = applyEmbeddingPrefix(texts, kind, this.#options)\n\n const pipe = await this.#resolvePipeline()\n const pooling = this.#options.pooling ?? 'mean'\n const normalize = this.#options.normalize ?? true\n const battery = (this.#options.poolingOwner ?? 'engine') === 'battery'\n\n emitLifecycle(this.#options, 'transformers_js_embed', this.#options.model, 'generating')\n\n let tensor: { tolist: () => unknown; dims?: number[] }\n try {\n tensor = (await (pipe as unknown as (i: unknown, o: unknown) => Promise<unknown>)(\n input,\n // 'battery' owner: request RAW token states (no engine pooling/normalize) and do it ourselves\n // in deterministic JS. 'engine' owner: delegate to the pipeline exactly as before.\n battery ? { pooling: 'none' } : { pooling, normalize }\n )) as { tolist: () => unknown; dims?: number[] }\n } catch (err) {\n emitLifecycle(this.#options, 'transformers_js_embed', this.#options.model, 'error', {\n error: err,\n })\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n isError(err) ? err.message : String(err),\n ])\n }\n\n if (!tensor || typeof tensor.tolist !== 'function') {\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n 'feature-extraction returned a non-Tensor result',\n ])\n }\n\n const list = tensor.tolist()\n let vectors: number[][]\n if (battery) {\n // Raw states: tolist() → [batch, seq, hidden]. A single ungrouped input may come back as\n // [seq, hidden] → wrap to a batch of one. Pool + normalize deterministically.\n const states = list as unknown[]\n const tokenStates = (\n Array.isArray(states) &&\n Array.isArray(states[0]) &&\n Array.isArray((states[0] as unknown[])[0])\n ? states\n : [states]\n ) as number[][][]\n vectors = poolAndNormalize(tokenStates, pooling, normalize)\n } else {\n // With engine pooling, the Tensor is [batch, hidden] → tolist() yields number[][].\n vectors =\n Array.isArray(list) && Array.isArray(list[0]) ? (list as number[][]) : [list as number[]]\n }\n\n if (vectors.length !== input.length) {\n emitLifecycle(this.#options, 'transformers_js_embed', this.#options.model, 'error', {\n error: new Error(`expected ${input.length} vectors, got ${vectors.length}`),\n })\n throw new E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR([\n `expected ${input.length} vectors, got ${vectors.length}`,\n ])\n }\n emitLifecycle(this.#options, 'transformers_js_embed', this.#options.model, 'complete')\n return vectors\n }\n}\n"],"mappings":"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"}
|
|
@@ -12,5 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export { TransformersJsEmbeddingsAdapter } from "./adapter";
|
|
14
14
|
export { transformersJsEmbeddingsOptionsSchema, validateOptions } from "./validation";
|
|
15
|
-
export
|
|
15
|
+
export { poolAndNormalize, defaultPoolAndNormalize, l2Normalize } from "./pooling";
|
|
16
|
+
export type { TokenStates3D, Pooled2D } from "./pooling";
|
|
17
|
+
export type { EmbeddingKind, EmbedOptions, BaseEmbeddingsAdapterOptions, TransformersJsEmbeddingsAdapterOptions, TransformersJsEmbeddingsPipeline, TransformersJsEmbeddingsDataType, TransformersJsEmbeddingsDeviceType, TransformersJsEmbeddingsProgressCallback, TransformersJsPooling, TransformersJsPoolingOwner, TransformersJsEmbeddingsModelSource, CreateTransformersJsEmbeddingsPipeline, BatteryLifecyclePhase, BatteryLifecycleBattery, BatteryLifecycleReport, BatteryLifecycleCallback, BatteryLifecycleHooks, } from "./types";
|
|
16
18
|
export { E_INVALID_TRANSFORMERS_JS_EMBEDDINGS_OPTIONS, E_TRANSFORMERS_JS_EMBEDDINGS_ENGINE_ERROR, } from "./exceptions";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("../../../chunk-Cek0wNdY.js");
|
|
3
|
+
//#region src/batteries/embeddings/transformers_js/pooling.ts
|
|
4
|
+
/**
|
|
5
|
+
* Mean-pool a `[seq, hidden]` matrix into a `[hidden]` vector.
|
|
6
|
+
*
|
|
7
|
+
* @param rows - The per-token hidden-state rows.
|
|
8
|
+
*/
|
|
9
|
+
var meanPool = (rows) => {
|
|
10
|
+
const seq = rows.length;
|
|
11
|
+
if (seq === 0) return [];
|
|
12
|
+
const hidden = rows[0].length;
|
|
13
|
+
const out = new Array(hidden).fill(0);
|
|
14
|
+
for (const row of rows) for (let h = 0; h < hidden; h++) out[h] += row[h];
|
|
15
|
+
for (let h = 0; h < hidden; h++) out[h] /= seq;
|
|
16
|
+
return out;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* L2-normalize a vector in place-safe fashion (returns a new array). A zero vector is returned
|
|
20
|
+
* unchanged (no divide-by-zero).
|
|
21
|
+
*
|
|
22
|
+
* @param vec - The vector to normalize.
|
|
23
|
+
*/
|
|
24
|
+
var l2Normalize = (vec) => {
|
|
25
|
+
let sumSq = 0;
|
|
26
|
+
for (const v of vec) sumSq += v * v;
|
|
27
|
+
const norm = Math.sqrt(sumSq);
|
|
28
|
+
if (norm === 0) return vec.slice();
|
|
29
|
+
return vec.map((v) => v / norm);
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Pool a single `[seq, hidden]` matrix into one `[hidden]` vector per the strategy.
|
|
33
|
+
*
|
|
34
|
+
* @param rows - The per-token hidden-state rows for one input.
|
|
35
|
+
* @param pooling - The pooling strategy.
|
|
36
|
+
*/
|
|
37
|
+
var poolOne = (rows, pooling) => {
|
|
38
|
+
switch (pooling) {
|
|
39
|
+
case "cls":
|
|
40
|
+
case "first_token": return (rows[0] ?? []).slice();
|
|
41
|
+
case "eos":
|
|
42
|
+
case "last_token": return (rows[rows.length - 1] ?? []).slice();
|
|
43
|
+
case "none": return meanPool(rows);
|
|
44
|
+
default: return meanPool(rows);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Pool raw `[batch, seq, hidden]` token states into `[batch, hidden]` vectors and optionally
|
|
49
|
+
* L2-normalize — the deterministic battery-owned path (`poolingOwner: 'battery'`).
|
|
50
|
+
*
|
|
51
|
+
* @param tokenStates - The `[batch, seq, hidden]` raw states from `pooling:'none'`.
|
|
52
|
+
* @param pooling - The pooling strategy (`'mean'` default).
|
|
53
|
+
* @param normalize - Whether to L2-normalize each pooled vector (default `true`).
|
|
54
|
+
* @returns One pooled (and optionally normalized) vector per batch row.
|
|
55
|
+
*/
|
|
56
|
+
var poolAndNormalize = (tokenStates, pooling, normalize) => tokenStates.map((rows) => {
|
|
57
|
+
const pooled = poolOne(rows, pooling);
|
|
58
|
+
return normalize ? l2Normalize(pooled) : pooled;
|
|
59
|
+
});
|
|
60
|
+
/** Default {@link poolAndNormalize}. */
|
|
61
|
+
var defaultPoolAndNormalize = poolAndNormalize;
|
|
62
|
+
//#endregion
|
|
63
|
+
exports.defaultPoolAndNormalize = defaultPoolAndNormalize;
|
|
64
|
+
exports.l2Normalize = l2Normalize;
|
|
65
|
+
exports.poolAndNormalize = poolAndNormalize;
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=pooling.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pooling.cjs","names":[],"sources":["../../../../src/batteries/embeddings/transformers_js/pooling.ts"],"sourcesContent":["/**\n * Deterministic, battery-owned pooling + L2-normalization for the transformers.js embeddings battery.\n *\n * @module @nhtio/adk/batteries/embeddings/transformers_js/pooling\n *\n * @remarks\n * Used when `poolingOwner: 'battery'`. The pipeline is asked for raw `pooling:'none'` token states —\n * a `[batch, seq, hidden]` tensor — and this module pools them into one vector per input in pure JS,\n * identically across Node and the browser. That removes the per-runtime post-processing variance that\n * widens the node↔browser cosine gap; only the irreducible ONNX-Runtime kernel floor remains.\n *\n * Pooling strategies mirror the transformers.js feature-extraction options:\n * - `mean` — attention-unaware mean over the sequence axis (matches the pipeline's `'mean'`, which\n * also means over all positions for a single ungrouped sequence).\n * - `cls` / `first_token` — the first token's hidden state.\n * - `eos` / `last_token` — the last token's hidden state.\n * - `none` — no pooling (caller already has the vector); returned unchanged per row.\n */\n\n/** A 3-D `[batch, seq, hidden]` nested array (raw token states from `pooling:'none'`). */\nexport type TokenStates3D = number[][][]\n/** A 2-D `[batch, hidden]` nested array (already-pooled vectors). */\nexport type Pooled2D = number[][]\n\n/**\n * Mean-pool a `[seq, hidden]` matrix into a `[hidden]` vector.\n *\n * @param rows - The per-token hidden-state rows.\n */\nconst meanPool = (rows: number[][]): number[] => {\n const seq = rows.length\n if (seq === 0) return []\n const hidden = rows[0].length\n const out = new Array<number>(hidden).fill(0)\n for (const row of rows) {\n for (let h = 0; h < hidden; h++) out[h] += row[h]\n }\n for (let h = 0; h < hidden; h++) out[h] /= seq\n return out\n}\n\n/**\n * L2-normalize a vector in place-safe fashion (returns a new array). A zero vector is returned\n * unchanged (no divide-by-zero).\n *\n * @param vec - The vector to normalize.\n */\nexport const l2Normalize = (vec: number[]): number[] => {\n let sumSq = 0\n for (const v of vec) sumSq += v * v\n const norm = Math.sqrt(sumSq)\n if (norm === 0) return vec.slice()\n return vec.map((v) => v / norm)\n}\n\n/**\n * Pool a single `[seq, hidden]` matrix into one `[hidden]` vector per the strategy.\n *\n * @param rows - The per-token hidden-state rows for one input.\n * @param pooling - The pooling strategy.\n */\nconst poolOne = (rows: number[][], pooling: string): number[] => {\n switch (pooling) {\n case 'cls':\n case 'first_token':\n return (rows[0] ?? []).slice()\n case 'eos':\n case 'last_token':\n return (rows[rows.length - 1] ?? []).slice()\n case 'none':\n // No pooling requested but we have token states — fall back to mean so a vector still results.\n return meanPool(rows)\n case 'mean':\n default:\n return meanPool(rows)\n }\n}\n\n/**\n * Pool raw `[batch, seq, hidden]` token states into `[batch, hidden]` vectors and optionally\n * L2-normalize — the deterministic battery-owned path (`poolingOwner: 'battery'`).\n *\n * @param tokenStates - The `[batch, seq, hidden]` raw states from `pooling:'none'`.\n * @param pooling - The pooling strategy (`'mean'` default).\n * @param normalize - Whether to L2-normalize each pooled vector (default `true`).\n * @returns One pooled (and optionally normalized) vector per batch row.\n */\nexport const poolAndNormalize = (\n tokenStates: TokenStates3D,\n pooling: string,\n normalize: boolean\n): Pooled2D =>\n tokenStates.map((rows) => {\n const pooled = poolOne(rows, pooling)\n return normalize ? l2Normalize(pooled) : pooled\n })\n\n/** Default {@link poolAndNormalize}. */\nexport const defaultPoolAndNormalize = poolAndNormalize\n"],"mappings":";;;;;;;;AA6BA,IAAM,YAAY,SAA+B;CAC/C,MAAM,MAAM,KAAK;CACjB,IAAI,QAAQ,GAAG,OAAO,CAAC;CACvB,MAAM,SAAS,KAAK,GAAG;CACvB,MAAM,MAAM,IAAI,MAAc,MAAM,EAAE,KAAK,CAAC;CAC5C,KAAK,MAAM,OAAO,MAChB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,IAAI,MAAM,IAAI;CAEjD,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,IAAI,MAAM;CAC3C,OAAO;AACT;;;;;;;AAQA,IAAa,eAAe,QAA4B;CACtD,IAAI,QAAQ;CACZ,KAAK,MAAM,KAAK,KAAK,SAAS,IAAI;CAClC,MAAM,OAAO,KAAK,KAAK,KAAK;CAC5B,IAAI,SAAS,GAAG,OAAO,IAAI,MAAM;CACjC,OAAO,IAAI,KAAK,MAAM,IAAI,IAAI;AAChC;;;;;;;AAQA,IAAM,WAAW,MAAkB,YAA8B;CAC/D,QAAQ,SAAR;EACE,KAAK;EACL,KAAK,eACH,QAAQ,KAAK,MAAM,CAAC,GAAG,MAAM;EAC/B,KAAK;EACL,KAAK,cACH,QAAQ,KAAK,KAAK,SAAS,MAAM,CAAC,GAAG,MAAM;EAC7C,KAAK,QAEH,OAAO,SAAS,IAAI;EAEtB,SACE,OAAO,SAAS,IAAI;CACxB;AACF;;;;;;;;;;AAWA,IAAa,oBACX,aACA,SACA,cAEA,YAAY,KAAK,SAAS;CACxB,MAAM,SAAS,QAAQ,MAAM,OAAO;CACpC,OAAO,YAAY,YAAY,MAAM,IAAI;AAC3C,CAAC;;AAGH,IAAa,0BAA0B"}
|