sensemaking 0.15.4 → 0.17.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/README.md +33 -5
- package/dist/cjs/chunk/extract.d.cts +2 -0
- package/dist/cjs/chunk/extract.d.ts +2 -0
- package/dist/cjs/chunk/extract.js +139 -0
- package/dist/cjs/chunk/extract.js.map +1 -0
- package/dist/cjs/chunk/group.d.cts +4 -0
- package/dist/cjs/chunk/group.d.ts +4 -0
- package/dist/cjs/chunk/group.js +489 -0
- package/dist/cjs/chunk/group.js.map +1 -0
- package/dist/cjs/chunk/index.d.cts +8 -0
- package/dist/cjs/chunk/index.d.ts +8 -0
- package/dist/cjs/chunk/index.js +44 -0
- package/dist/cjs/chunk/index.js.map +1 -0
- package/dist/cjs/chunk/parse.d.cts +2 -0
- package/dist/cjs/chunk/parse.d.ts +2 -0
- package/dist/cjs/chunk/parse.js +71 -0
- package/dist/cjs/chunk/parse.js.map +1 -0
- package/dist/cjs/chunk/types.d.cts +19 -0
- package/dist/cjs/chunk/types.d.ts +19 -0
- package/dist/cjs/chunk/types.js +5 -0
- package/dist/cjs/chunk/types.js.map +1 -0
- package/dist/cjs/chunk/version.d.cts +1 -0
- package/dist/cjs/chunk/version.d.ts +1 -0
- package/dist/cjs/chunk/version.js +14 -0
- package/dist/cjs/chunk/version.js.map +1 -0
- package/dist/cjs/cli/download.js +12 -11
- package/dist/cjs/cli/download.js.map +1 -1
- package/dist/cjs/cli/index.d.cts +1 -1
- package/dist/cjs/cli/index.d.ts +1 -1
- package/dist/cjs/cli/index.js +1 -1
- package/dist/cjs/cli/index.js.map +1 -1
- package/dist/cjs/cli/init.js +31 -5
- package/dist/cjs/cli/init.js.map +1 -1
- package/dist/cjs/cli/map.js +1 -1
- package/dist/cjs/cli/map.js.map +1 -1
- package/dist/cjs/cli/named.js +1 -1
- package/dist/cjs/cli/named.js.map +1 -1
- package/dist/cjs/cli/path.js +2 -2
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js +1 -1
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js +1 -1
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js +1 -1
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/cli/shared.d.cts +1 -1
- package/dist/cjs/cli/shared.d.ts +1 -1
- package/dist/cjs/cli/shared.js +3 -3
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/cli/status.js +344 -100
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/commands/map.d.cts +1 -1
- package/dist/cjs/commands/map.d.ts +1 -1
- package/dist/cjs/commands/map.js.map +1 -1
- package/dist/cjs/commands/peek.d.cts +1 -1
- package/dist/cjs/commands/peek.d.ts +1 -1
- package/dist/cjs/commands/peek.js +13 -6
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/related.js +17 -13
- package/dist/cjs/commands/related.js.map +1 -1
- package/dist/cjs/commands/scope.js +1 -1
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.d.cts +1 -1
- package/dist/cjs/commands/search.d.ts +1 -1
- package/dist/cjs/commands/search.js +28 -99
- package/dist/cjs/commands/search.js.map +1 -1
- package/dist/cjs/commands/signals.d.cts +18 -0
- package/dist/cjs/commands/signals.d.ts +18 -0
- package/dist/cjs/commands/signals.js +299 -0
- package/dist/cjs/commands/signals.js.map +1 -0
- package/dist/cjs/commands/status.d.cts +2 -2
- package/dist/cjs/commands/status.d.ts +2 -2
- package/dist/cjs/commands/status.js +1 -1
- package/dist/cjs/commands/status.js.map +1 -1
- package/dist/cjs/config/access.d.cts +8 -3
- package/dist/cjs/config/access.d.ts +8 -3
- package/dist/cjs/config/access.js +35 -17
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.d.cts +5 -1
- package/dist/cjs/config/index.d.ts +5 -1
- package/dist/cjs/config/index.js +16 -2
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/load.d.cts +6 -1
- package/dist/cjs/config/load.d.ts +6 -1
- package/dist/cjs/config/load.js +83 -8
- package/dist/cjs/config/load.js.map +1 -1
- package/dist/cjs/config/resolve.js +1 -1
- package/dist/cjs/config/resolve.js.map +1 -1
- package/dist/cjs/config/signals.d.cts +4 -0
- package/dist/cjs/config/signals.d.ts +4 -0
- package/dist/cjs/config/signals.js +30 -0
- package/dist/cjs/config/signals.js.map +1 -0
- package/dist/cjs/config/types.d.cts +7 -4
- package/dist/cjs/config/types.d.ts +7 -4
- package/dist/cjs/config/types.js +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.d.cts +1 -0
- package/dist/cjs/config/validate.d.ts +1 -0
- package/dist/cjs/config/validate.js +127 -11
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/db/open.d.cts +1 -1
- package/dist/cjs/db/open.d.ts +1 -1
- package/dist/cjs/db/open.js +9 -4
- package/dist/cjs/db/open.js.map +1 -1
- package/dist/cjs/db/reconcile.d.cts +1 -0
- package/dist/cjs/db/reconcile.d.ts +1 -0
- package/dist/cjs/db/reconcile.js +23 -8
- package/dist/cjs/db/reconcile.js.map +1 -1
- package/dist/cjs/{sql-functions.js → db/sql-functions.js} +1 -1
- package/dist/cjs/db/sql-functions.js.map +1 -0
- package/dist/cjs/embed/cohere.d.cts +2 -0
- package/dist/cjs/embed/cohere.d.ts +2 -0
- package/dist/cjs/embed/cohere.js +238 -0
- package/dist/cjs/embed/cohere.js.map +1 -0
- package/dist/cjs/embed/http.d.cts +2 -0
- package/dist/cjs/embed/http.d.ts +2 -0
- package/dist/cjs/embed/http.js +258 -0
- package/dist/cjs/embed/http.js.map +1 -0
- package/dist/cjs/embed/identity.d.cts +13 -0
- package/dist/cjs/embed/identity.d.ts +13 -0
- package/dist/cjs/embed/identity.js +194 -0
- package/dist/cjs/embed/identity.js.map +1 -0
- package/dist/cjs/embed/langfit.d.cts +6 -0
- package/dist/cjs/embed/langfit.d.ts +6 -0
- package/dist/cjs/embed/langfit.js +286 -0
- package/dist/cjs/embed/langfit.js.map +1 -0
- package/dist/cjs/embed/languages.d.cts +3 -0
- package/dist/cjs/embed/languages.d.ts +3 -0
- package/dist/cjs/embed/languages.js +98 -0
- package/dist/cjs/embed/languages.js.map +1 -0
- package/dist/cjs/embed/openai.d.cts +2 -0
- package/dist/cjs/embed/openai.d.ts +2 -0
- package/dist/cjs/embed/openai.js +268 -0
- package/dist/cjs/embed/openai.js.map +1 -0
- package/dist/cjs/embed/query.d.cts +21 -0
- package/dist/cjs/embed/query.d.ts +21 -0
- package/dist/cjs/embed/query.js +610 -0
- package/dist/cjs/embed/query.js.map +1 -0
- package/dist/cjs/embed/registry.d.cts +3 -0
- package/dist/cjs/embed/registry.d.ts +3 -0
- package/dist/cjs/embed/registry.js +114 -0
- package/dist/cjs/embed/registry.js.map +1 -0
- package/dist/cjs/embed/static.d.cts +2 -0
- package/dist/cjs/embed/static.d.ts +2 -0
- package/dist/cjs/embed/static.js +330 -0
- package/dist/cjs/embed/static.js.map +1 -0
- package/dist/cjs/embed/store.d.cts +8 -0
- package/dist/cjs/embed/store.d.ts +8 -0
- package/dist/cjs/embed/store.js +498 -0
- package/dist/cjs/embed/store.js.map +1 -0
- package/dist/cjs/embed/types.d.cts +8 -0
- package/dist/cjs/embed/types.d.ts +8 -0
- package/dist/cjs/embed/types.js +7 -0
- package/dist/cjs/embed/types.js.map +1 -0
- package/dist/cjs/errors.d.cts +1 -1
- package/dist/cjs/errors.d.ts +1 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/embed.d.cts +2 -28
- package/dist/cjs/features/embed.d.ts +2 -28
- package/dist/cjs/features/embed.js +39 -1027
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/{fences.js → features/fences.js} +1 -1
- package/dist/cjs/features/fences.js.map +1 -0
- package/dist/cjs/features/links.js +1 -1
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/rank.js +1 -1
- package/dist/cjs/features/rank.js.map +1 -1
- package/dist/cjs/features/sections.js +4 -3
- package/dist/cjs/features/sections.js.map +1 -1
- package/dist/cjs/features/tags.js +1 -1
- package/dist/cjs/features/tags.js.map +1 -1
- package/dist/cjs/features/types.d.cts +4 -2
- package/dist/cjs/features/types.d.ts +4 -2
- package/dist/cjs/graph/graph.js.map +1 -0
- package/dist/cjs/graph/traverse.js.map +1 -0
- package/dist/cjs/index.d.cts +4 -4
- package/dist/cjs/index.d.ts +4 -4
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/output/column-hint.js.map +1 -0
- package/dist/cjs/{output.d.cts → output/output.d.cts} +2 -2
- package/dist/cjs/{output.d.ts → output/output.d.ts} +2 -2
- package/dist/cjs/{output.js → output/output.js} +9 -2
- package/dist/cjs/output/output.js.map +1 -0
- package/dist/cjs/output/progress.js.map +1 -0
- package/dist/cjs/{search-error.js → output/search-error.js} +1 -1
- package/dist/cjs/output/search-error.js.map +1 -0
- package/dist/cjs/scan/frontmatter.d.cts +13 -0
- package/dist/cjs/scan/frontmatter.d.ts +13 -0
- package/dist/cjs/scan/frontmatter.js +229 -0
- package/dist/cjs/scan/frontmatter.js.map +1 -0
- package/dist/cjs/scan/index.d.cts +25 -0
- package/dist/cjs/scan/index.d.ts +25 -0
- package/dist/cjs/scan/index.js +107 -0
- package/dist/cjs/scan/index.js.map +1 -0
- package/dist/cjs/scan/list.d.cts +12 -0
- package/dist/cjs/scan/list.d.ts +12 -0
- package/dist/cjs/scan/list.js +154 -0
- package/dist/cjs/scan/list.js.map +1 -0
- package/dist/cjs/text/segment.d.cts +3 -0
- package/dist/cjs/text/segment.d.ts +3 -0
- package/dist/cjs/text/segment.js +154 -0
- package/dist/cjs/text/segment.js.map +1 -0
- package/dist/cjs/text/strip.d.cts +3 -0
- package/dist/cjs/text/strip.d.ts +3 -0
- package/dist/cjs/text/strip.js +32 -0
- package/dist/cjs/text/strip.js.map +1 -0
- package/dist/esm/chunk/extract.d.ts +2 -0
- package/dist/esm/chunk/extract.js +114 -0
- package/dist/esm/chunk/extract.js.map +1 -0
- package/dist/esm/chunk/group.d.ts +4 -0
- package/dist/esm/chunk/group.js +253 -0
- package/dist/esm/chunk/group.js.map +1 -0
- package/dist/esm/chunk/index.d.ts +8 -0
- package/dist/esm/chunk/index.js +16 -0
- package/dist/esm/chunk/index.js.map +1 -0
- package/dist/esm/chunk/parse.d.ts +2 -0
- package/dist/esm/chunk/parse.js +63 -0
- package/dist/esm/chunk/parse.js.map +1 -0
- package/dist/esm/chunk/types.d.ts +19 -0
- package/dist/esm/chunk/types.js +2 -0
- package/dist/esm/chunk/types.js.map +1 -0
- package/dist/esm/chunk/version.d.ts +1 -0
- package/dist/esm/chunk/version.js +3 -0
- package/dist/esm/chunk/version.js.map +1 -0
- package/dist/esm/cli/download.js +9 -8
- package/dist/esm/cli/download.js.map +1 -1
- package/dist/esm/cli/index.d.ts +1 -1
- package/dist/esm/cli/index.js +1 -1
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/cli/init.js +32 -6
- package/dist/esm/cli/init.js.map +1 -1
- package/dist/esm/cli/map.js +1 -1
- package/dist/esm/cli/map.js.map +1 -1
- package/dist/esm/cli/named.js +1 -1
- package/dist/esm/cli/named.js.map +1 -1
- package/dist/esm/cli/path.js +2 -2
- package/dist/esm/cli/path.js.map +1 -1
- package/dist/esm/cli/peek.js +1 -1
- package/dist/esm/cli/peek.js.map +1 -1
- package/dist/esm/cli/related.js +1 -1
- package/dist/esm/cli/related.js.map +1 -1
- package/dist/esm/cli/search.js +1 -1
- package/dist/esm/cli/search.js.map +1 -1
- package/dist/esm/cli/shared.d.ts +1 -1
- package/dist/esm/cli/shared.js +3 -3
- package/dist/esm/cli/shared.js.map +1 -1
- package/dist/esm/cli/status.js +37 -9
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/commands/map.d.ts +1 -1
- package/dist/esm/commands/map.js.map +1 -1
- package/dist/esm/commands/peek.d.ts +1 -1
- package/dist/esm/commands/peek.js +8 -1
- package/dist/esm/commands/peek.js.map +1 -1
- package/dist/esm/commands/related.js +14 -10
- package/dist/esm/commands/related.js.map +1 -1
- package/dist/esm/commands/scope.js +1 -1
- package/dist/esm/commands/scope.js.map +1 -1
- package/dist/esm/commands/search.d.ts +1 -1
- package/dist/esm/commands/search.js +30 -81
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/commands/signals.d.ts +18 -0
- package/dist/esm/commands/signals.js +78 -0
- package/dist/esm/commands/signals.js.map +1 -0
- package/dist/esm/commands/status.d.ts +2 -2
- package/dist/esm/commands/status.js +2 -2
- package/dist/esm/commands/status.js.map +1 -1
- package/dist/esm/config/access.d.ts +8 -3
- package/dist/esm/config/access.js +34 -15
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.d.ts +5 -1
- package/dist/esm/config/index.js +3 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/load.d.ts +6 -1
- package/dist/esm/config/load.js +67 -9
- package/dist/esm/config/load.js.map +1 -1
- package/dist/esm/config/resolve.js +2 -2
- package/dist/esm/config/resolve.js.map +1 -1
- package/dist/esm/config/signals.d.ts +4 -0
- package/dist/esm/config/signals.js +14 -0
- package/dist/esm/config/signals.js.map +1 -0
- package/dist/esm/config/types.d.ts +7 -4
- package/dist/esm/config/types.js +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.d.ts +1 -0
- package/dist/esm/config/validate.js +65 -12
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/db/open.d.ts +1 -1
- package/dist/esm/db/open.js +11 -6
- package/dist/esm/db/open.js.map +1 -1
- package/dist/esm/db/reconcile.d.ts +1 -0
- package/dist/esm/db/reconcile.js +14 -4
- package/dist/esm/db/reconcile.js.map +1 -1
- package/dist/esm/{sql-functions.js → db/sql-functions.js} +1 -1
- package/dist/esm/db/sql-functions.js.map +1 -0
- package/dist/esm/embed/cohere.d.ts +2 -0
- package/dist/esm/embed/cohere.js +46 -0
- package/dist/esm/embed/cohere.js.map +1 -0
- package/dist/esm/embed/http.d.ts +2 -0
- package/dist/esm/embed/http.js +33 -0
- package/dist/esm/embed/http.js.map +1 -0
- package/dist/esm/embed/identity.d.ts +13 -0
- package/dist/esm/embed/identity.js +92 -0
- package/dist/esm/embed/identity.js.map +1 -0
- package/dist/esm/embed/langfit.d.ts +6 -0
- package/dist/esm/embed/langfit.js +44 -0
- package/dist/esm/embed/langfit.js.map +1 -0
- package/dist/esm/embed/languages.d.ts +3 -0
- package/dist/esm/embed/languages.js +80 -0
- package/dist/esm/embed/languages.js.map +1 -0
- package/dist/esm/embed/openai.d.ts +2 -0
- package/dist/esm/embed/openai.js +49 -0
- package/dist/esm/embed/openai.js.map +1 -0
- package/dist/esm/embed/query.d.ts +21 -0
- package/dist/esm/embed/query.js +181 -0
- package/dist/esm/embed/query.js.map +1 -0
- package/dist/esm/embed/registry.d.ts +3 -0
- package/dist/esm/embed/registry.js +45 -0
- package/dist/esm/embed/registry.js.map +1 -0
- package/dist/esm/embed/static.d.ts +2 -0
- package/dist/esm/embed/static.js +71 -0
- package/dist/esm/embed/static.js.map +1 -0
- package/dist/esm/embed/store.d.ts +8 -0
- package/dist/esm/embed/store.js +123 -0
- package/dist/esm/embed/store.js.map +1 -0
- package/dist/esm/embed/types.d.ts +8 -0
- package/dist/esm/embed/types.js +3 -0
- package/dist/esm/embed/types.js.map +1 -0
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/embed.d.ts +2 -28
- package/dist/esm/features/embed.js +33 -377
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/{fences.js → features/fences.js} +1 -1
- package/dist/esm/features/fences.js.map +1 -0
- package/dist/esm/features/links.js +1 -1
- package/dist/esm/features/links.js.map +1 -1
- package/dist/esm/features/rank.js +1 -1
- package/dist/esm/features/rank.js.map +1 -1
- package/dist/esm/features/sections.js +4 -3
- package/dist/esm/features/sections.js.map +1 -1
- package/dist/esm/features/tags.js +1 -1
- package/dist/esm/features/tags.js.map +1 -1
- package/dist/esm/features/types.d.ts +4 -2
- package/dist/esm/features/types.js.map +1 -1
- package/dist/esm/graph/graph.js.map +1 -0
- package/dist/esm/graph/traverse.js.map +1 -0
- package/dist/esm/index.d.ts +4 -4
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/output/column-hint.js.map +1 -0
- package/dist/esm/{output.d.ts → output/output.d.ts} +2 -2
- package/dist/esm/{output.js → output/output.js} +6 -2
- package/dist/esm/output/output.js.map +1 -0
- package/dist/esm/output/progress.js.map +1 -0
- package/dist/esm/{search-error.js → output/search-error.js} +1 -1
- package/dist/esm/output/search-error.js.map +1 -0
- package/dist/esm/scan/frontmatter.d.ts +13 -0
- package/dist/esm/{scan.js → scan/frontmatter.js} +4 -120
- package/dist/esm/scan/frontmatter.js.map +1 -0
- package/dist/esm/scan/index.d.ts +25 -0
- package/dist/esm/scan/index.js +58 -0
- package/dist/esm/scan/index.js.map +1 -0
- package/dist/esm/scan/list.d.ts +12 -0
- package/dist/esm/scan/list.js +58 -0
- package/dist/esm/scan/list.js.map +1 -0
- package/dist/esm/text/segment.d.ts +3 -0
- package/dist/esm/text/segment.js +95 -0
- package/dist/esm/text/segment.js.map +1 -0
- package/dist/esm/text/strip.d.ts +3 -0
- package/dist/esm/text/strip.js +12 -0
- package/dist/esm/text/strip.js.map +1 -0
- package/package.json +23 -2
- package/schema.json +22 -8
- package/skills/sense/SKILL.md +21 -5
- package/skills/sense-setup/EXAMPLES.md +6 -6
- package/skills/sense-setup/SKILL.md +7 -5
- package/dist/cjs/column-hint.js.map +0 -1
- package/dist/cjs/fences.js.map +0 -1
- package/dist/cjs/graph.js.map +0 -1
- package/dist/cjs/output.js.map +0 -1
- package/dist/cjs/progress.js.map +0 -1
- package/dist/cjs/scan.d.cts +0 -35
- package/dist/cjs/scan.d.ts +0 -35
- package/dist/cjs/scan.js +0 -433
- package/dist/cjs/scan.js.map +0 -1
- package/dist/cjs/search-error.js.map +0 -1
- package/dist/cjs/segment.d.cts +0 -2
- package/dist/cjs/segment.d.ts +0 -2
- package/dist/cjs/segment.js +0 -98
- package/dist/cjs/segment.js.map +0 -1
- package/dist/cjs/sql-functions.js.map +0 -1
- package/dist/cjs/traverse.js.map +0 -1
- package/dist/esm/column-hint.js.map +0 -1
- package/dist/esm/fences.js.map +0 -1
- package/dist/esm/graph.js.map +0 -1
- package/dist/esm/output.js.map +0 -1
- package/dist/esm/progress.js.map +0 -1
- package/dist/esm/scan.d.ts +0 -35
- package/dist/esm/scan.js.map +0 -1
- package/dist/esm/search-error.js.map +0 -1
- package/dist/esm/segment.d.ts +0 -2
- package/dist/esm/segment.js +0 -65
- package/dist/esm/segment.js.map +0 -1
- package/dist/esm/sql-functions.js.map +0 -1
- package/dist/esm/traverse.js.map +0 -1
- /package/dist/cjs/{sql-functions.d.cts → db/sql-functions.d.cts} +0 -0
- /package/dist/cjs/{sql-functions.d.ts → db/sql-functions.d.ts} +0 -0
- /package/dist/cjs/{fences.d.cts → features/fences.d.cts} +0 -0
- /package/dist/cjs/{fences.d.ts → features/fences.d.ts} +0 -0
- /package/dist/cjs/{graph.d.cts → graph/graph.d.cts} +0 -0
- /package/dist/cjs/{graph.d.ts → graph/graph.d.ts} +0 -0
- /package/dist/cjs/{graph.js → graph/graph.js} +0 -0
- /package/dist/cjs/{traverse.d.cts → graph/traverse.d.cts} +0 -0
- /package/dist/cjs/{traverse.d.ts → graph/traverse.d.ts} +0 -0
- /package/dist/cjs/{traverse.js → graph/traverse.js} +0 -0
- /package/dist/cjs/{column-hint.d.cts → output/column-hint.d.cts} +0 -0
- /package/dist/cjs/{column-hint.d.ts → output/column-hint.d.ts} +0 -0
- /package/dist/cjs/{column-hint.js → output/column-hint.js} +0 -0
- /package/dist/cjs/{progress.d.cts → output/progress.d.cts} +0 -0
- /package/dist/cjs/{progress.d.ts → output/progress.d.ts} +0 -0
- /package/dist/cjs/{progress.js → output/progress.js} +0 -0
- /package/dist/cjs/{search-error.d.cts → output/search-error.d.cts} +0 -0
- /package/dist/cjs/{search-error.d.ts → output/search-error.d.ts} +0 -0
- /package/dist/esm/{sql-functions.d.ts → db/sql-functions.d.ts} +0 -0
- /package/dist/esm/{fences.d.ts → features/fences.d.ts} +0 -0
- /package/dist/esm/{graph.d.ts → graph/graph.d.ts} +0 -0
- /package/dist/esm/{graph.js → graph/graph.js} +0 -0
- /package/dist/esm/{traverse.d.ts → graph/traverse.d.ts} +0 -0
- /package/dist/esm/{traverse.js → graph/traverse.js} +0 -0
- /package/dist/esm/{column-hint.d.ts → output/column-hint.d.ts} +0 -0
- /package/dist/esm/{column-hint.js → output/column-hint.js} +0 -0
- /package/dist/esm/{progress.d.ts → output/progress.d.ts} +0 -0
- /package/dist/esm/{progress.js → output/progress.js} +0 -0
- /package/dist/esm/{search-error.d.ts → output/search-error.d.ts} +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SenseError } from '../errors.js';
|
|
2
|
+
import { fetchWithRetry } from './http.js';
|
|
3
|
+
const BATCH_CAP = 96;
|
|
4
|
+
// Cohere's native /v2/embed: input_type distinguishes doc vs query embeddings, which the
|
|
5
|
+
// OpenAI-compatible shape cannot express.
|
|
6
|
+
export async function cohereProvider(model, url, keyEnv) {
|
|
7
|
+
const base = (url !== null && url !== void 0 ? url : 'https://api.cohere.com').replace(/\/+$/, '');
|
|
8
|
+
const headers = {
|
|
9
|
+
'content-type': 'application/json'
|
|
10
|
+
};
|
|
11
|
+
const key = keyEnv ? process.env[keyEnv] : undefined;
|
|
12
|
+
if (key) headers.authorization = `Bearer ${key}`;
|
|
13
|
+
async function post(texts, inputType) {
|
|
14
|
+
const res = await fetchWithRetry(`${base}/v2/embed`, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers,
|
|
17
|
+
body: JSON.stringify({
|
|
18
|
+
model,
|
|
19
|
+
input_type: inputType,
|
|
20
|
+
texts,
|
|
21
|
+
embedding_types: [
|
|
22
|
+
'float'
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok) throw new SenseError('EMBED_MODEL', `${base}/v2/embed -> HTTP ${res.status}`);
|
|
27
|
+
const body = await res.json();
|
|
28
|
+
// Cohere returns one vector per text in order, with no index field to re-sort by; the
|
|
29
|
+
// count is what a caller mapping positionally still has to be able to trust.
|
|
30
|
+
const vectors = body.embeddings.float;
|
|
31
|
+
if (vectors.length !== texts.length) throw new SenseError('EMBED_MODEL', `${base}/v2/embed returned ${vectors.length} embeddings for ${texts.length} inputs`);
|
|
32
|
+
return vectors.map((v)=>Float32Array.from(v));
|
|
33
|
+
}
|
|
34
|
+
const dims = (await post([
|
|
35
|
+
'dimension probe'
|
|
36
|
+
], 'search_query'))[0].length;
|
|
37
|
+
return {
|
|
38
|
+
id: `cohere:${base}:${model}`,
|
|
39
|
+
dims,
|
|
40
|
+
batchCap: BATCH_CAP,
|
|
41
|
+
embedDocuments: (texts)=>post(texts, 'search_document'),
|
|
42
|
+
embedQuery: async (text)=>(await post([
|
|
43
|
+
text
|
|
44
|
+
], 'search_query'))[0]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/cohere.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\nimport { fetchWithRetry } from './http.ts';\nimport type { EmbedProvider } from './types.ts';\n\nconst BATCH_CAP = 96;\n\n// Cohere's native /v2/embed: input_type distinguishes doc vs query embeddings, which the\n// OpenAI-compatible shape cannot express.\nexport async function cohereProvider(model: string, url: string | undefined, keyEnv: string | undefined): Promise<EmbedProvider> {\n const base = (url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n const key = keyEnv ? process.env[keyEnv] : undefined;\n if (key) headers.authorization = `Bearer ${key}`;\n\n async function post(texts: string[], inputType: 'search_document' | 'search_query'): Promise<Float32Array[]> {\n const res = await fetchWithRetry(`${base}/v2/embed`, { method: 'POST', headers, body: JSON.stringify({ model, input_type: inputType, texts, embedding_types: ['float'] }) });\n if (!res.ok) throw new SenseError('EMBED_MODEL', `${base}/v2/embed -> HTTP ${res.status}`);\n const body = (await res.json()) as { embeddings: { float: number[][] } };\n // Cohere returns one vector per text in order, with no index field to re-sort by; the\n // count is what a caller mapping positionally still has to be able to trust.\n const vectors = body.embeddings.float;\n if (vectors.length !== texts.length) throw new SenseError('EMBED_MODEL', `${base}/v2/embed returned ${vectors.length} embeddings for ${texts.length} inputs`);\n return vectors.map((v) => Float32Array.from(v));\n }\n\n const dims = (await post(['dimension probe'], 'search_query'))[0].length;\n return { id: `cohere:${base}:${model}`, dims, batchCap: BATCH_CAP, embedDocuments: (texts) => post(texts, 'search_document'), embedQuery: async (text) => (await post([text], 'search_query'))[0] };\n}\n"],"names":["SenseError","fetchWithRetry","BATCH_CAP","cohereProvider","model","url","keyEnv","base","replace","headers","key","process","env","undefined","authorization","post","texts","inputType","res","method","body","JSON","stringify","input_type","embedding_types","ok","status","json","vectors","embeddings","float","length","map","v","Float32Array","from","dims","id","batchCap","embedDocuments","embedQuery","text"],"mappings":"AAAA,SAASA,UAAU,QAAQ,eAAe;AAC1C,SAASC,cAAc,QAAQ,YAAY;AAG3C,MAAMC,YAAY;AAElB,yFAAyF;AACzF,0CAA0C;AAC1C,OAAO,eAAeC,eAAeC,KAAa,EAAEC,GAAuB,EAAEC,MAA0B;IACrG,MAAMC,OAAO,CAACF,gBAAAA,iBAAAA,MAAO,0BAA0BG,OAAO,CAAC,QAAQ;IAC/D,MAAMC,UAAkC;QAAE,gBAAgB;IAAmB;IAC7E,MAAMC,MAAMJ,SAASK,QAAQC,GAAG,CAACN,OAAO,GAAGO;IAC3C,IAAIH,KAAKD,QAAQK,aAAa,GAAG,CAAC,OAAO,EAAEJ,KAAK;IAEhD,eAAeK,KAAKC,KAAe,EAAEC,SAA6C;QAChF,MAAMC,MAAM,MAAMjB,eAAe,GAAGM,KAAK,SAAS,CAAC,EAAE;YAAEY,QAAQ;YAAQV;YAASW,MAAMC,KAAKC,SAAS,CAAC;gBAAElB;gBAAOmB,YAAYN;gBAAWD;gBAAOQ,iBAAiB;oBAAC;iBAAQ;YAAC;QAAG;QAC1K,IAAI,CAACN,IAAIO,EAAE,EAAE,MAAM,IAAIzB,WAAW,eAAe,GAAGO,KAAK,kBAAkB,EAAEW,IAAIQ,MAAM,EAAE;QACzF,MAAMN,OAAQ,MAAMF,IAAIS,IAAI;QAC5B,sFAAsF;QACtF,6EAA6E;QAC7E,MAAMC,UAAUR,KAAKS,UAAU,CAACC,KAAK;QACrC,IAAIF,QAAQG,MAAM,KAAKf,MAAMe,MAAM,EAAE,MAAM,IAAI/B,WAAW,eAAe,GAAGO,KAAK,mBAAmB,EAAEqB,QAAQG,MAAM,CAAC,gBAAgB,EAAEf,MAAMe,MAAM,CAAC,OAAO,CAAC;QAC5J,OAAOH,QAAQI,GAAG,CAAC,CAACC,IAAMC,aAAaC,IAAI,CAACF;IAC9C;IAEA,MAAMG,OAAO,AAAC,CAAA,MAAMrB,KAAK;QAAC;KAAkB,EAAE,eAAc,CAAE,CAAC,EAAE,CAACgB,MAAM;IACxE,OAAO;QAAEM,IAAI,CAAC,OAAO,EAAE9B,KAAK,CAAC,EAAEH,OAAO;QAAEgC;QAAME,UAAUpC;QAAWqC,gBAAgB,CAACvB,QAAUD,KAAKC,OAAO;QAAoBwB,YAAY,OAAOC,OAAS,AAAC,CAAA,MAAM1B,KAAK;gBAAC0B;aAAK,EAAE,eAAc,CAAE,CAAC,EAAE;IAAC;AACpM"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const MAX_ATTEMPTS = 3;
|
|
2
|
+
function sleep(ms) {
|
|
3
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
4
|
+
}
|
|
5
|
+
// Shared retry glue for the HTTP providers: 429/5xx/network failure retry with exponential
|
|
6
|
+
// backoff honoring Retry-After; any other status returns immediately for the caller to handle.
|
|
7
|
+
export async function fetchWithRetry(url, init, baseDelayMs = 200) {
|
|
8
|
+
for(let attempt = 0; attempt < MAX_ATTEMPTS; attempt++){
|
|
9
|
+
try {
|
|
10
|
+
const res = await fetch(url, init);
|
|
11
|
+
if (res.ok || res.status !== 429 && res.status < 500 || attempt === MAX_ATTEMPTS - 1) return res;
|
|
12
|
+
const retryAfter = Number(res.headers.get('retry-after'));
|
|
13
|
+
// Capped: a server naming a huge Retry-After must not stall an embed run for it.
|
|
14
|
+
await sleep(retryAfter > 0 ? Math.min(retryAfter * 1000, 10000) : baseDelayMs * 2 ** attempt);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
if (attempt === MAX_ATTEMPTS - 1) throw err;
|
|
17
|
+
await sleep(baseDelayMs * 2 ** attempt);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
throw new Error('unreachable');
|
|
21
|
+
}
|
|
22
|
+
// Any HTTP response means the endpoint is up; only a network failure or the timeout counts as
|
|
23
|
+
// unreachable, so an air-gapped machine never waits more than timeoutMs on `sense status`.
|
|
24
|
+
export async function probeReachable(url, timeoutMs = 1500) {
|
|
25
|
+
try {
|
|
26
|
+
await fetch(url, {
|
|
27
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
28
|
+
});
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/http.ts"],"sourcesContent":["const MAX_ATTEMPTS = 3;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// Shared retry glue for the HTTP providers: 429/5xx/network failure retry with exponential\n// backoff honoring Retry-After; any other status returns immediately for the caller to handle.\nexport async function fetchWithRetry(url: string, init: RequestInit, baseDelayMs = 200): Promise<Response> {\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || (res.status !== 429 && res.status < 500) || attempt === MAX_ATTEMPTS - 1) return res;\n const retryAfter = Number(res.headers.get('retry-after'));\n // Capped: a server naming a huge Retry-After must not stall an embed run for it.\n await sleep(retryAfter > 0 ? Math.min(retryAfter * 1000, 10_000) : baseDelayMs * 2 ** attempt);\n } catch (err) {\n if (attempt === MAX_ATTEMPTS - 1) throw err;\n await sleep(baseDelayMs * 2 ** attempt);\n }\n }\n throw new Error('unreachable');\n}\n\n// Any HTTP response means the endpoint is up; only a network failure or the timeout counts as\n// unreachable, so an air-gapped machine never waits more than timeoutMs on `sense status`.\nexport async function probeReachable(url: string, timeoutMs = 1500): Promise<boolean> {\n try {\n await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });\n return true;\n } catch {\n return false;\n }\n}\n"],"names":["MAX_ATTEMPTS","sleep","ms","Promise","resolve","setTimeout","fetchWithRetry","url","init","baseDelayMs","attempt","res","fetch","ok","status","retryAfter","Number","headers","get","Math","min","err","Error","probeReachable","timeoutMs","signal","AbortSignal","timeout"],"mappings":"AAAA,MAAMA,eAAe;AAErB,SAASC,MAAMC,EAAU;IACvB,OAAO,IAAIC,QAAQ,CAACC,UAAYC,WAAWD,SAASF;AACtD;AAEA,2FAA2F;AAC3F,+FAA+F;AAC/F,OAAO,eAAeI,eAAeC,GAAW,EAAEC,IAAiB,EAAEC,cAAc,GAAG;IACpF,IAAK,IAAIC,UAAU,GAAGA,UAAUV,cAAcU,UAAW;QACvD,IAAI;YACF,MAAMC,MAAM,MAAMC,MAAML,KAAKC;YAC7B,IAAIG,IAAIE,EAAE,IAAKF,IAAIG,MAAM,KAAK,OAAOH,IAAIG,MAAM,GAAG,OAAQJ,YAAYV,eAAe,GAAG,OAAOW;YAC/F,MAAMI,aAAaC,OAAOL,IAAIM,OAAO,CAACC,GAAG,CAAC;YAC1C,iFAAiF;YACjF,MAAMjB,MAAMc,aAAa,IAAII,KAAKC,GAAG,CAACL,aAAa,MAAM,SAAUN,cAAc,KAAKC;QACxF,EAAE,OAAOW,KAAK;YACZ,IAAIX,YAAYV,eAAe,GAAG,MAAMqB;YACxC,MAAMpB,MAAMQ,cAAc,KAAKC;QACjC;IACF;IACA,MAAM,IAAIY,MAAM;AAClB;AAEA,8FAA8F;AAC9F,2FAA2F;AAC3F,OAAO,eAAeC,eAAehB,GAAW,EAAEiB,YAAY,IAAI;IAChE,IAAI;QACF,MAAMZ,MAAML,KAAK;YAAEkB,QAAQC,YAAYC,OAAO,CAACH;QAAW;QAC1D,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const MODEL_FILES: string[];
|
|
2
|
+
export declare const MODEL_FILENAMES: string;
|
|
3
|
+
export declare function isDownloadable(model: string): boolean;
|
|
4
|
+
export declare function refsMainPath(model: string): string;
|
|
5
|
+
export declare function readRef(model: string): string | undefined;
|
|
6
|
+
export declare function writeRef(model: string, sha: string): void;
|
|
7
|
+
export declare function snapshotDir(model: string, sha: string): string;
|
|
8
|
+
export declare function languagesPath(model: string): string;
|
|
9
|
+
export declare function readLanguages(model: string): string[] | undefined;
|
|
10
|
+
export declare function writeLanguages(model: string, languages: string[]): void;
|
|
11
|
+
export declare function modelDir(model: string): string;
|
|
12
|
+
export declare function hasModelFiles(model: string): boolean;
|
|
13
|
+
export declare function modelIdentity(model: string): string | undefined;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
// HF cache layout, no network: ~/.sense/models/models--<org>--<name>/{refs/main,
|
|
5
|
+
// snapshots/<sha>/<files>}, files stored directly (hf_hub's blobs/symlinks layer skipped).
|
|
6
|
+
export const MODEL_FILES = [
|
|
7
|
+
'model.safetensors',
|
|
8
|
+
'tokenizer.json'
|
|
9
|
+
];
|
|
10
|
+
// The pair, for messages that name what a local model directory must contain.
|
|
11
|
+
export const MODEL_FILENAMES = MODEL_FILES.join(' and ');
|
|
12
|
+
// A Hugging Face repo id: one slash, HF's charset. Anything else is a path, used as one rather
|
|
13
|
+
// than resolved against the HF cache.
|
|
14
|
+
const HF_MODEL_ID = /^[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*$/;
|
|
15
|
+
export function isDownloadable(model) {
|
|
16
|
+
return HF_MODEL_ID.test(model);
|
|
17
|
+
}
|
|
18
|
+
function repoDir(model) {
|
|
19
|
+
const [org, name] = model.split('/');
|
|
20
|
+
return join(homedir(), '.sense', 'models', `models--${org}--${name}`);
|
|
21
|
+
}
|
|
22
|
+
export function refsMainPath(model) {
|
|
23
|
+
return join(repoDir(model), 'refs', 'main');
|
|
24
|
+
}
|
|
25
|
+
// What `main` last resolved to, read from disk -- no network. Undefined until some resolution
|
|
26
|
+
// (a download or an explicit prefetch) has written it once.
|
|
27
|
+
export function readRef(model) {
|
|
28
|
+
const p = refsMainPath(model);
|
|
29
|
+
if (!existsSync(p)) return undefined;
|
|
30
|
+
const sha = readFileSync(p, 'utf8').trim();
|
|
31
|
+
return sha.length > 0 ? sha : undefined;
|
|
32
|
+
}
|
|
33
|
+
// A recorded ref pins until its snapshot directory is removed; nothing here re-resolves it.
|
|
34
|
+
export function writeRef(model, sha) {
|
|
35
|
+
mkdirSync(join(repoDir(model), 'refs'), {
|
|
36
|
+
recursive: true
|
|
37
|
+
});
|
|
38
|
+
writeFileSync(refsMainPath(model), sha);
|
|
39
|
+
}
|
|
40
|
+
export function snapshotDir(model, sha) {
|
|
41
|
+
return join(repoDir(model), 'snapshots', sha);
|
|
42
|
+
}
|
|
43
|
+
// Languages describe the repo, not one weight snapshot, so this lives beside refs/, not
|
|
44
|
+
// inside a snapshot dir.
|
|
45
|
+
export function languagesPath(model) {
|
|
46
|
+
return join(repoDir(model), 'languages.json');
|
|
47
|
+
}
|
|
48
|
+
// undefined means "never resolved yet"; an empty array is a resolved fact (checked, none
|
|
49
|
+
// declared) and is cached the same way, so a languageless model is not re-fetched every run.
|
|
50
|
+
export function readLanguages(model) {
|
|
51
|
+
const p = languagesPath(model);
|
|
52
|
+
if (!existsSync(p)) return undefined;
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(readFileSync(p, 'utf8'));
|
|
55
|
+
} catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function writeLanguages(model, languages) {
|
|
60
|
+
mkdirSync(repoDir(model), {
|
|
61
|
+
recursive: true
|
|
62
|
+
});
|
|
63
|
+
writeFileSync(languagesPath(model), JSON.stringify(languages));
|
|
64
|
+
}
|
|
65
|
+
// A local directory the caller manages, or the resolved snapshot once `main` is pinned; an HF
|
|
66
|
+
// id with no recorded ref yet has no known snapshot, so this names the (fileless) repo dir.
|
|
67
|
+
export function modelDir(model) {
|
|
68
|
+
if (!isDownloadable(model)) return model;
|
|
69
|
+
const sha = readRef(model);
|
|
70
|
+
return sha ? snapshotDir(model, sha) : repoDir(model);
|
|
71
|
+
}
|
|
72
|
+
// Both files, so an interrupted download reads as absent and the next one resumes it.
|
|
73
|
+
export function hasModelFiles(model) {
|
|
74
|
+
const dir = modelDir(model);
|
|
75
|
+
return MODEL_FILES.every((file)=>existsSync(join(dir, file)));
|
|
76
|
+
}
|
|
77
|
+
// Weight identity for the signature, no network: a HF id's resolved sha, or the two
|
|
78
|
+
// files' size+mtime for a local path. Undefined when nothing is known yet.
|
|
79
|
+
export function modelIdentity(model) {
|
|
80
|
+
if (isDownloadable(model)) return readRef(model);
|
|
81
|
+
const dir = modelDir(model);
|
|
82
|
+
const parts = [];
|
|
83
|
+
for (const file of MODEL_FILES){
|
|
84
|
+
try {
|
|
85
|
+
const st = statSync(join(dir, file));
|
|
86
|
+
parts.push(`${st.size}:${Math.trunc(st.mtimeMs)}`);
|
|
87
|
+
} catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return parts.join(',');
|
|
92
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/identity.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// HF cache layout, no network: ~/.sense/models/models--<org>--<name>/{refs/main,\n// snapshots/<sha>/<files>}, files stored directly (hf_hub's blobs/symlinks layer skipped).\n\nexport const MODEL_FILES = ['model.safetensors', 'tokenizer.json'];\n// The pair, for messages that name what a local model directory must contain.\nexport const MODEL_FILENAMES = MODEL_FILES.join(' and ');\n\n// A Hugging Face repo id: one slash, HF's charset. Anything else is a path, used as one rather\n// than resolved against the HF cache.\nconst HF_MODEL_ID = /^[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*$/;\n\nexport function isDownloadable(model: string): boolean {\n return HF_MODEL_ID.test(model);\n}\n\nfunction repoDir(model: string): string {\n const [org, name] = model.split('/');\n return join(homedir(), '.sense', 'models', `models--${org}--${name}`);\n}\n\nexport function refsMainPath(model: string): string {\n return join(repoDir(model), 'refs', 'main');\n}\n\n// What `main` last resolved to, read from disk -- no network. Undefined until some resolution\n// (a download or an explicit prefetch) has written it once.\nexport function readRef(model: string): string | undefined {\n const p = refsMainPath(model);\n if (!existsSync(p)) return undefined;\n const sha = readFileSync(p, 'utf8').trim();\n return sha.length > 0 ? sha : undefined;\n}\n\n// A recorded ref pins until its snapshot directory is removed; nothing here re-resolves it.\nexport function writeRef(model: string, sha: string): void {\n mkdirSync(join(repoDir(model), 'refs'), { recursive: true });\n writeFileSync(refsMainPath(model), sha);\n}\n\nexport function snapshotDir(model: string, sha: string): string {\n return join(repoDir(model), 'snapshots', sha);\n}\n\n// Languages describe the repo, not one weight snapshot, so this lives beside refs/, not\n// inside a snapshot dir.\nexport function languagesPath(model: string): string {\n return join(repoDir(model), 'languages.json');\n}\n\n// undefined means \"never resolved yet\"; an empty array is a resolved fact (checked, none\n// declared) and is cached the same way, so a languageless model is not re-fetched every run.\nexport function readLanguages(model: string): string[] | undefined {\n const p = languagesPath(model);\n if (!existsSync(p)) return undefined;\n try {\n return JSON.parse(readFileSync(p, 'utf8'));\n } catch {\n return undefined;\n }\n}\n\nexport function writeLanguages(model: string, languages: string[]): void {\n mkdirSync(repoDir(model), { recursive: true });\n writeFileSync(languagesPath(model), JSON.stringify(languages));\n}\n\n// A local directory the caller manages, or the resolved snapshot once `main` is pinned; an HF\n// id with no recorded ref yet has no known snapshot, so this names the (fileless) repo dir.\nexport function modelDir(model: string): string {\n if (!isDownloadable(model)) return model;\n const sha = readRef(model);\n return sha ? snapshotDir(model, sha) : repoDir(model);\n}\n\n// Both files, so an interrupted download reads as absent and the next one resumes it.\nexport function hasModelFiles(model: string): boolean {\n const dir = modelDir(model);\n return MODEL_FILES.every((file) => existsSync(join(dir, file)));\n}\n\n// Weight identity for the signature, no network: a HF id's resolved sha, or the two\n// files' size+mtime for a local path. Undefined when nothing is known yet.\nexport function modelIdentity(model: string): string | undefined {\n if (isDownloadable(model)) return readRef(model);\n const dir = modelDir(model);\n const parts: string[] = [];\n for (const file of MODEL_FILES) {\n try {\n const st = statSync(join(dir, file));\n parts.push(`${st.size}:${Math.trunc(st.mtimeMs)}`);\n } catch {\n return undefined;\n }\n }\n return parts.join(',');\n}\n"],"names":["existsSync","mkdirSync","readFileSync","statSync","writeFileSync","homedir","join","MODEL_FILES","MODEL_FILENAMES","HF_MODEL_ID","isDownloadable","model","test","repoDir","org","name","split","refsMainPath","readRef","p","undefined","sha","trim","length","writeRef","recursive","snapshotDir","languagesPath","readLanguages","JSON","parse","writeLanguages","languages","stringify","modelDir","hasModelFiles","dir","every","file","modelIdentity","parts","st","push","size","Math","trunc","mtimeMs"],"mappings":"AAAA,SAASA,UAAU,EAAEC,SAAS,EAAEC,YAAY,EAAEC,QAAQ,EAAEC,aAAa,QAAQ,UAAU;AACvF,SAASC,OAAO,QAAQ,UAAU;AAClC,SAASC,IAAI,QAAQ,YAAY;AAEjC,iFAAiF;AACjF,2FAA2F;AAE3F,OAAO,MAAMC,cAAc;IAAC;IAAqB;CAAiB,CAAC;AACnE,8EAA8E;AAC9E,OAAO,MAAMC,kBAAkBD,YAAYD,IAAI,CAAC,SAAS;AAEzD,+FAA+F;AAC/F,sCAAsC;AACtC,MAAMG,cAAc;AAEpB,OAAO,SAASC,eAAeC,KAAa;IAC1C,OAAOF,YAAYG,IAAI,CAACD;AAC1B;AAEA,SAASE,QAAQF,KAAa;IAC5B,MAAM,CAACG,KAAKC,KAAK,GAAGJ,MAAMK,KAAK,CAAC;IAChC,OAAOV,KAAKD,WAAW,UAAU,UAAU,CAAC,QAAQ,EAAES,IAAI,EAAE,EAAEC,MAAM;AACtE;AAEA,OAAO,SAASE,aAAaN,KAAa;IACxC,OAAOL,KAAKO,QAAQF,QAAQ,QAAQ;AACtC;AAEA,8FAA8F;AAC9F,4DAA4D;AAC5D,OAAO,SAASO,QAAQP,KAAa;IACnC,MAAMQ,IAAIF,aAAaN;IACvB,IAAI,CAACX,WAAWmB,IAAI,OAAOC;IAC3B,MAAMC,MAAMnB,aAAaiB,GAAG,QAAQG,IAAI;IACxC,OAAOD,IAAIE,MAAM,GAAG,IAAIF,MAAMD;AAChC;AAEA,4FAA4F;AAC5F,OAAO,SAASI,SAASb,KAAa,EAAEU,GAAW;IACjDpB,UAAUK,KAAKO,QAAQF,QAAQ,SAAS;QAAEc,WAAW;IAAK;IAC1DrB,cAAca,aAAaN,QAAQU;AACrC;AAEA,OAAO,SAASK,YAAYf,KAAa,EAAEU,GAAW;IACpD,OAAOf,KAAKO,QAAQF,QAAQ,aAAaU;AAC3C;AAEA,wFAAwF;AACxF,yBAAyB;AACzB,OAAO,SAASM,cAAchB,KAAa;IACzC,OAAOL,KAAKO,QAAQF,QAAQ;AAC9B;AAEA,yFAAyF;AACzF,6FAA6F;AAC7F,OAAO,SAASiB,cAAcjB,KAAa;IACzC,MAAMQ,IAAIQ,cAAchB;IACxB,IAAI,CAACX,WAAWmB,IAAI,OAAOC;IAC3B,IAAI;QACF,OAAOS,KAAKC,KAAK,CAAC5B,aAAaiB,GAAG;IACpC,EAAE,OAAM;QACN,OAAOC;IACT;AACF;AAEA,OAAO,SAASW,eAAepB,KAAa,EAAEqB,SAAmB;IAC/D/B,UAAUY,QAAQF,QAAQ;QAAEc,WAAW;IAAK;IAC5CrB,cAAcuB,cAAchB,QAAQkB,KAAKI,SAAS,CAACD;AACrD;AAEA,8FAA8F;AAC9F,4FAA4F;AAC5F,OAAO,SAASE,SAASvB,KAAa;IACpC,IAAI,CAACD,eAAeC,QAAQ,OAAOA;IACnC,MAAMU,MAAMH,QAAQP;IACpB,OAAOU,MAAMK,YAAYf,OAAOU,OAAOR,QAAQF;AACjD;AAEA,sFAAsF;AACtF,OAAO,SAASwB,cAAcxB,KAAa;IACzC,MAAMyB,MAAMF,SAASvB;IACrB,OAAOJ,YAAY8B,KAAK,CAAC,CAACC,OAAStC,WAAWM,KAAK8B,KAAKE;AAC1D;AAEA,oFAAoF;AACpF,2EAA2E;AAC3E,OAAO,SAASC,cAAc5B,KAAa;IACzC,IAAID,eAAeC,QAAQ,OAAOO,QAAQP;IAC1C,MAAMyB,MAAMF,SAASvB;IACrB,MAAM6B,QAAkB,EAAE;IAC1B,KAAK,MAAMF,QAAQ/B,YAAa;QAC9B,IAAI;YACF,MAAMkC,KAAKtC,SAASG,KAAK8B,KAAKE;YAC9BE,MAAME,IAAI,CAAC,GAAGD,GAAGE,IAAI,CAAC,CAAC,EAAEC,KAAKC,KAAK,CAACJ,GAAGK,OAAO,GAAG;QACnD,EAAE,OAAM;YACN,OAAO1B;QACT;IACF;IACA,OAAOoB,MAAMlC,IAAI,CAAC;AACpB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import type { EmbedProvider } from './types.js';
|
|
3
|
+
type LangCounts = Record<string, number>;
|
|
4
|
+
export declare function languageDistribution(db: DatabaseSync): LangCounts | undefined;
|
|
5
|
+
export declare function checkLanguageFit(db: DatabaseSync, provider: EmbedProvider, texts: string[]): Promise<void>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { franc } from 'franc-min';
|
|
2
|
+
import { getMeta, setMeta } from '../db/shared.js';
|
|
3
|
+
import { SenseError } from '../errors.js';
|
|
4
|
+
import { toIso3 } from './languages.js';
|
|
5
|
+
const META_KEY = 'embed_languages';
|
|
6
|
+
// franc's own reliability floor is a handful of characters; this is a cheap upper bound, not a
|
|
7
|
+
// per-chunk minimum -- a short chunk is simply more likely to come back 'und' (unclassified).
|
|
8
|
+
const SAMPLE_CHARS = 300;
|
|
9
|
+
// A majority computed over a handful of chunks is noise, not a corpus signal; the decision rule
|
|
10
|
+
// never fires below this many classified chunks, declared languages or not.
|
|
11
|
+
const MIN_CLASSIFIED = 10;
|
|
12
|
+
export function languageDistribution(db) {
|
|
13
|
+
const raw = getMeta(db, META_KEY);
|
|
14
|
+
return raw ? JSON.parse(raw) : undefined;
|
|
15
|
+
}
|
|
16
|
+
function classify(text) {
|
|
17
|
+
const code = franc(text.slice(0, SAMPLE_CHARS));
|
|
18
|
+
return code === 'und' ? undefined : code;
|
|
19
|
+
}
|
|
20
|
+
// Merges chunk texts into the persisted distribution; throws (without persisting) if the model
|
|
21
|
+
// declares languages and a clear majority classified so far is not among them.
|
|
22
|
+
export async function checkLanguageFit(db, provider, texts) {
|
|
23
|
+
var _languageDistribution;
|
|
24
|
+
if (texts.length === 0) return;
|
|
25
|
+
const persisted = (_languageDistribution = languageDistribution(db)) !== null && _languageDistribution !== void 0 ? _languageDistribution : {};
|
|
26
|
+
const merged = {
|
|
27
|
+
...persisted
|
|
28
|
+
};
|
|
29
|
+
for (const text of texts){
|
|
30
|
+
var _merged_code;
|
|
31
|
+
const code = classify(text);
|
|
32
|
+
if (code) merged[code] = ((_merged_code = merged[code]) !== null && _merged_code !== void 0 ? _merged_code : 0) + 1;
|
|
33
|
+
}
|
|
34
|
+
const total = Object.values(merged).reduce((a, b)=>a + b, 0);
|
|
35
|
+
if (provider.languages && provider.languages.length > 0 && total >= MIN_CLASSIFIED) {
|
|
36
|
+
const declared = new Set(provider.languages.map(toIso3));
|
|
37
|
+
const [majorityLang, majorityCount] = Object.entries(merged).sort((a, b)=>b[1] - a[1])[0];
|
|
38
|
+
if (majorityCount / total >= 0.5 && !declared.has(majorityLang)) {
|
|
39
|
+
const pct = Math.round(majorityCount / total * 100);
|
|
40
|
+
throw new SenseError('EMBED_MODEL_MISMATCH', `embed model ${provider.id} declares languages [${provider.languages.join(', ')}], but ${pct}% of this tree's classified text is "${majorityLang}" (ISO 639-3), which is not among them; choose a model for this tree's languages: \`sense init --model ...\`, see the sense-setup skill or INTEGRATIONS.md`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
setMeta(db, META_KEY, JSON.stringify(merged));
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/langfit.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport { franc } from 'franc-min';\nimport { getMeta, setMeta } from '../db/shared.ts';\nimport { SenseError } from '../errors.ts';\nimport { toIso3 } from './languages.ts';\nimport type { EmbedProvider } from './types.ts';\n\nconst META_KEY = 'embed_languages';\n// franc's own reliability floor is a handful of characters; this is a cheap upper bound, not a\n// per-chunk minimum -- a short chunk is simply more likely to come back 'und' (unclassified).\nconst SAMPLE_CHARS = 300;\n// A majority computed over a handful of chunks is noise, not a corpus signal; the decision rule\n// never fires below this many classified chunks, declared languages or not.\nconst MIN_CLASSIFIED = 10;\n\ntype LangCounts = Record<string, number>;\n\nexport function languageDistribution(db: DatabaseSync): LangCounts | undefined {\n const raw = getMeta(db, META_KEY);\n return raw ? JSON.parse(raw) : undefined;\n}\n\nfunction classify(text: string): string | undefined {\n const code = franc(text.slice(0, SAMPLE_CHARS));\n return code === 'und' ? undefined : code;\n}\n\n// Merges chunk texts into the persisted distribution; throws (without persisting) if the model\n// declares languages and a clear majority classified so far is not among them.\nexport async function checkLanguageFit(db: DatabaseSync, provider: EmbedProvider, texts: string[]): Promise<void> {\n if (texts.length === 0) return;\n const persisted = languageDistribution(db) ?? {};\n const merged = { ...persisted };\n for (const text of texts) {\n const code = classify(text);\n if (code) merged[code] = (merged[code] ?? 0) + 1;\n }\n\n const total = Object.values(merged).reduce((a, b) => a + b, 0);\n if (provider.languages && provider.languages.length > 0 && total >= MIN_CLASSIFIED) {\n const declared = new Set(provider.languages.map(toIso3));\n const [majorityLang, majorityCount] = Object.entries(merged).sort((a, b) => b[1] - a[1])[0];\n if (majorityCount / total >= 0.5 && !declared.has(majorityLang)) {\n const pct = Math.round((majorityCount / total) * 100);\n throw new SenseError(\n 'EMBED_MODEL_MISMATCH',\n `embed model ${provider.id} declares languages [${provider.languages.join(', ')}], but ${pct}% of this tree's classified text is \"${majorityLang}\" (ISO 639-3), which is not among them; choose a model for this tree's languages: \\`sense init --model ...\\`, see the sense-setup skill or INTEGRATIONS.md`\n );\n }\n }\n setMeta(db, META_KEY, JSON.stringify(merged));\n}\n"],"names":["franc","getMeta","setMeta","SenseError","toIso3","META_KEY","SAMPLE_CHARS","MIN_CLASSIFIED","languageDistribution","db","raw","JSON","parse","undefined","classify","text","code","slice","checkLanguageFit","provider","texts","length","persisted","merged","total","Object","values","reduce","a","b","languages","declared","Set","map","majorityLang","majorityCount","entries","sort","has","pct","Math","round","id","join","stringify"],"mappings":"AACA,SAASA,KAAK,QAAQ,YAAY;AAClC,SAASC,OAAO,EAAEC,OAAO,QAAQ,kBAAkB;AACnD,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,MAAM,QAAQ,iBAAiB;AAGxC,MAAMC,WAAW;AACjB,+FAA+F;AAC/F,8FAA8F;AAC9F,MAAMC,eAAe;AACrB,gGAAgG;AAChG,4EAA4E;AAC5E,MAAMC,iBAAiB;AAIvB,OAAO,SAASC,qBAAqBC,EAAgB;IACnD,MAAMC,MAAMT,QAAQQ,IAAIJ;IACxB,OAAOK,MAAMC,KAAKC,KAAK,CAACF,OAAOG;AACjC;AAEA,SAASC,SAASC,IAAY;IAC5B,MAAMC,OAAOhB,MAAMe,KAAKE,KAAK,CAAC,GAAGX;IACjC,OAAOU,SAAS,QAAQH,YAAYG;AACtC;AAEA,+FAA+F;AAC/F,+EAA+E;AAC/E,OAAO,eAAeE,iBAAiBT,EAAgB,EAAEU,QAAuB,EAAEC,KAAe;QAE7EZ;IADlB,IAAIY,MAAMC,MAAM,KAAK,GAAG;IACxB,MAAMC,aAAYd,wBAAAA,qBAAqBC,iBAArBD,mCAAAA,wBAA4B,CAAC;IAC/C,MAAMe,SAAS;QAAE,GAAGD,SAAS;IAAC;IAC9B,KAAK,MAAMP,QAAQK,MAAO;YAEEG;QAD1B,MAAMP,OAAOF,SAASC;QACtB,IAAIC,MAAMO,MAAM,CAACP,KAAK,GAAG,EAACO,eAAAA,MAAM,CAACP,KAAK,cAAZO,0BAAAA,eAAgB,KAAK;IACjD;IAEA,MAAMC,QAAQC,OAAOC,MAAM,CAACH,QAAQI,MAAM,CAAC,CAACC,GAAGC,IAAMD,IAAIC,GAAG;IAC5D,IAAIV,SAASW,SAAS,IAAIX,SAASW,SAAS,CAACT,MAAM,GAAG,KAAKG,SAASjB,gBAAgB;QAClF,MAAMwB,WAAW,IAAIC,IAAIb,SAASW,SAAS,CAACG,GAAG,CAAC7B;QAChD,MAAM,CAAC8B,cAAcC,cAAc,GAAGV,OAAOW,OAAO,CAACb,QAAQc,IAAI,CAAC,CAACT,GAAGC,IAAMA,CAAC,CAAC,EAAE,GAAGD,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;QAC3F,IAAIO,gBAAgBX,SAAS,OAAO,CAACO,SAASO,GAAG,CAACJ,eAAe;YAC/D,MAAMK,MAAMC,KAAKC,KAAK,CAAC,AAACN,gBAAgBX,QAAS;YACjD,MAAM,IAAIrB,WACR,wBACA,CAAC,YAAY,EAAEgB,SAASuB,EAAE,CAAC,qBAAqB,EAAEvB,SAASW,SAAS,CAACa,IAAI,CAAC,MAAM,OAAO,EAAEJ,IAAI,qCAAqC,EAAEL,aAAa,0JAA0J,CAAC;QAEhT;IACF;IACAhC,QAAQO,IAAIJ,UAAUM,KAAKiC,SAAS,CAACrB;AACvC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// HF model cards declare languages as ISO 639-1 tags (e.g. "zh"); franc-min reports ISO 639-3
|
|
2
|
+
// (e.g. "cmn"). This bridges the two for comparison. Limited to franc-min's own 82-language
|
|
3
|
+
// data (README table, https://github.com/wooorm/franc), so every value here is a code franc-min
|
|
4
|
+
// can actually emit; a declared tag with no entry here is compared as-is.
|
|
5
|
+
const ISO_639_1_TO_3 = {
|
|
6
|
+
ar: 'arb',
|
|
7
|
+
am: 'amh',
|
|
8
|
+
az: 'azj',
|
|
9
|
+
be: 'bel',
|
|
10
|
+
bg: 'bul',
|
|
11
|
+
bn: 'ben',
|
|
12
|
+
bs: 'bos',
|
|
13
|
+
cs: 'ces',
|
|
14
|
+
de: 'deu',
|
|
15
|
+
el: 'ell',
|
|
16
|
+
en: 'eng',
|
|
17
|
+
es: 'spa',
|
|
18
|
+
fa: 'pes',
|
|
19
|
+
fr: 'fra',
|
|
20
|
+
gu: 'guj',
|
|
21
|
+
ha: 'hau',
|
|
22
|
+
hi: 'hin',
|
|
23
|
+
hr: 'hrv',
|
|
24
|
+
hu: 'hun',
|
|
25
|
+
id: 'ind',
|
|
26
|
+
ig: 'ibo',
|
|
27
|
+
it: 'ita',
|
|
28
|
+
ja: 'jpn',
|
|
29
|
+
jv: 'jav',
|
|
30
|
+
kk: 'kaz',
|
|
31
|
+
kn: 'kan',
|
|
32
|
+
ko: 'kor',
|
|
33
|
+
ml: 'mal',
|
|
34
|
+
mr: 'mar',
|
|
35
|
+
ms: 'zlm',
|
|
36
|
+
my: 'mya',
|
|
37
|
+
ne: 'npi',
|
|
38
|
+
nl: 'nld',
|
|
39
|
+
pa: 'pan',
|
|
40
|
+
pl: 'pol',
|
|
41
|
+
pt: 'por',
|
|
42
|
+
ro: 'ron',
|
|
43
|
+
ru: 'rus',
|
|
44
|
+
rw: 'kin',
|
|
45
|
+
si: 'sin',
|
|
46
|
+
so: 'som',
|
|
47
|
+
sr: 'srp',
|
|
48
|
+
su: 'sun',
|
|
49
|
+
sv: 'swe',
|
|
50
|
+
sw: 'swh',
|
|
51
|
+
ta: 'tam',
|
|
52
|
+
te: 'tel',
|
|
53
|
+
th: 'tha',
|
|
54
|
+
tl: 'tgl',
|
|
55
|
+
tr: 'tur',
|
|
56
|
+
uk: 'ukr',
|
|
57
|
+
ur: 'urd',
|
|
58
|
+
uz: 'uzn',
|
|
59
|
+
vi: 'vie',
|
|
60
|
+
yo: 'yor',
|
|
61
|
+
zh: 'cmn',
|
|
62
|
+
zu: 'zul'
|
|
63
|
+
};
|
|
64
|
+
export function isKnownLanguageTag(tag) {
|
|
65
|
+
return tag in ISO_639_1_TO_3;
|
|
66
|
+
}
|
|
67
|
+
// Normalizes a declared or detected language code to ISO 639-3 for comparison; strips a
|
|
68
|
+
// region/script subtag ("zh-cn" -> "zh") first. Codes already in 639-3 form pass through.
|
|
69
|
+
export function toIso3(code) {
|
|
70
|
+
var _ISO_639_1_TO_3_base;
|
|
71
|
+
const base = code.toLowerCase().split('-')[0];
|
|
72
|
+
return (_ISO_639_1_TO_3_base = ISO_639_1_TO_3[base]) !== null && _ISO_639_1_TO_3_base !== void 0 ? _ISO_639_1_TO_3_base : base;
|
|
73
|
+
}
|
|
74
|
+
// Languages this project measured for models whose cards declare none, so the fit check is
|
|
75
|
+
// not silently off for the shipped default; asserted here because it was measured, not attested.
|
|
76
|
+
export const MEASURED_MODEL_LANGUAGES = {
|
|
77
|
+
'minishlab/potion-retrieval-32M': [
|
|
78
|
+
'en'
|
|
79
|
+
]
|
|
80
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/languages.ts"],"sourcesContent":["// HF model cards declare languages as ISO 639-1 tags (e.g. \"zh\"); franc-min reports ISO 639-3\n// (e.g. \"cmn\"). This bridges the two for comparison. Limited to franc-min's own 82-language\n// data (README table, https://github.com/wooorm/franc), so every value here is a code franc-min\n// can actually emit; a declared tag with no entry here is compared as-is.\nconst ISO_639_1_TO_3: Record<string, string> = {\n ar: 'arb',\n am: 'amh',\n az: 'azj',\n be: 'bel',\n bg: 'bul',\n bn: 'ben',\n bs: 'bos',\n cs: 'ces',\n de: 'deu',\n el: 'ell',\n en: 'eng',\n es: 'spa',\n fa: 'pes',\n fr: 'fra',\n gu: 'guj',\n ha: 'hau',\n hi: 'hin',\n hr: 'hrv',\n hu: 'hun',\n id: 'ind',\n ig: 'ibo',\n it: 'ita',\n ja: 'jpn',\n jv: 'jav',\n kk: 'kaz',\n kn: 'kan',\n ko: 'kor',\n ml: 'mal',\n mr: 'mar',\n ms: 'zlm',\n my: 'mya',\n ne: 'npi',\n nl: 'nld',\n pa: 'pan',\n pl: 'pol',\n pt: 'por',\n ro: 'ron',\n ru: 'rus',\n rw: 'kin',\n si: 'sin',\n so: 'som',\n sr: 'srp',\n su: 'sun',\n sv: 'swe',\n sw: 'swh',\n ta: 'tam',\n te: 'tel',\n th: 'tha',\n tl: 'tgl',\n tr: 'tur',\n uk: 'ukr',\n ur: 'urd',\n uz: 'uzn',\n vi: 'vie',\n yo: 'yor',\n zh: 'cmn',\n zu: 'zul',\n};\n\nexport function isKnownLanguageTag(tag: string): boolean {\n return tag in ISO_639_1_TO_3;\n}\n\n// Normalizes a declared or detected language code to ISO 639-3 for comparison; strips a\n// region/script subtag (\"zh-cn\" -> \"zh\") first. Codes already in 639-3 form pass through.\nexport function toIso3(code: string): string {\n const base = code.toLowerCase().split('-')[0];\n return ISO_639_1_TO_3[base] ?? base;\n}\n\n// Languages this project measured for models whose cards declare none, so the fit check is\n// not silently off for the shipped default; asserted here because it was measured, not attested.\nexport const MEASURED_MODEL_LANGUAGES: Record<string, string[]> = {\n 'minishlab/potion-retrieval-32M': ['en'], // benchmark/reports/2026-08-27-embedding-model-selection.md\n};\n"],"names":["ISO_639_1_TO_3","ar","am","az","be","bg","bn","bs","cs","de","el","en","es","fa","fr","gu","ha","hi","hr","hu","id","ig","it","ja","jv","kk","kn","ko","ml","mr","ms","my","ne","nl","pa","pl","pt","ro","ru","rw","si","so","sr","su","sv","sw","ta","te","th","tl","tr","uk","ur","uz","vi","yo","zh","zu","isKnownLanguageTag","tag","toIso3","code","base","toLowerCase","split","MEASURED_MODEL_LANGUAGES"],"mappings":"AAAA,8FAA8F;AAC9F,4FAA4F;AAC5F,gGAAgG;AAChG,0EAA0E;AAC1E,MAAMA,iBAAyC;IAC7CC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;AACN;AAEA,OAAO,SAASC,mBAAmBC,GAAW;IAC5C,OAAOA,OAAO3D;AAChB;AAEA,wFAAwF;AACxF,0FAA0F;AAC1F,OAAO,SAAS4D,OAAOC,IAAY;QAE1B7D;IADP,MAAM8D,OAAOD,KAAKE,WAAW,GAAGC,KAAK,CAAC,IAAI,CAAC,EAAE;IAC7C,QAAOhE,uBAAAA,cAAc,CAAC8D,KAAK,cAApB9D,kCAAAA,uBAAwB8D;AACjC;AAEA,2FAA2F;AAC3F,iGAAiG;AACjG,OAAO,MAAMG,2BAAqD;IAChE,kCAAkC;QAAC;KAAK;AAC1C,EAAE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { SenseError } from '../errors.js';
|
|
2
|
+
import { fetchWithRetry } from './http.js';
|
|
3
|
+
const BATCH_CAP = 64;
|
|
4
|
+
// One POST against any OpenAI-compatible /embeddings endpoint: Ollama, LM Studio, Cloudflare
|
|
5
|
+
// Workers AI, Jina, Voyage, and Gemini's compat base URL all serve this shape.
|
|
6
|
+
export async function openaiProvider(model, url, keyEnv) {
|
|
7
|
+
if (!url) throw new SenseError('EMBED_MODEL', 'embed.provider "openai" requires a url');
|
|
8
|
+
const base = url.replace(/\/+$/, '');
|
|
9
|
+
const headers = {
|
|
10
|
+
'content-type': 'application/json'
|
|
11
|
+
};
|
|
12
|
+
const key = keyEnv ? process.env[keyEnv] : undefined;
|
|
13
|
+
if (key) headers.authorization = `Bearer ${key}`;
|
|
14
|
+
async function post(texts) {
|
|
15
|
+
const res = await fetchWithRetry(`${base}/embeddings`, {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers,
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
model,
|
|
20
|
+
input: texts
|
|
21
|
+
})
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok) throw new SenseError('EMBED_MODEL', `${base}/embeddings -> HTTP ${res.status}`);
|
|
24
|
+
const body = await res.json();
|
|
25
|
+
// The schema carries `index` because response order is not promised, and the caller maps
|
|
26
|
+
// vectors back to chunks positionally: sort by it whenever every element has one.
|
|
27
|
+
const data = body.data.every((d)=>typeof d.index === 'number') ? [
|
|
28
|
+
...body.data
|
|
29
|
+
].sort((a, b)=>a.index - b.index) : body.data;
|
|
30
|
+
// A server that drops an input (empty or over-length strings are the usual cause) would
|
|
31
|
+
// otherwise shift every later vector onto the wrong chunk, silently.
|
|
32
|
+
if (data.length !== texts.length) throw new SenseError('EMBED_MODEL', `${base}/embeddings returned ${data.length} embeddings for ${texts.length} inputs`);
|
|
33
|
+
return data.map((d)=>Float32Array.from(d.embedding));
|
|
34
|
+
}
|
|
35
|
+
const dims = (await post([
|
|
36
|
+
'dimension probe'
|
|
37
|
+
]))[0].length;
|
|
38
|
+
// Symmetric for now: per-model prefixes (nomic's search_document/search_query) are a
|
|
39
|
+
// later workstream.
|
|
40
|
+
return {
|
|
41
|
+
id: `openai:${base}:${model}`,
|
|
42
|
+
dims,
|
|
43
|
+
batchCap: BATCH_CAP,
|
|
44
|
+
embedDocuments: post,
|
|
45
|
+
embedQuery: async (text)=>(await post([
|
|
46
|
+
text
|
|
47
|
+
]))[0]
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/openai.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\nimport { fetchWithRetry } from './http.ts';\nimport type { EmbedProvider } from './types.ts';\n\nconst BATCH_CAP = 64;\n\n// One POST against any OpenAI-compatible /embeddings endpoint: Ollama, LM Studio, Cloudflare\n// Workers AI, Jina, Voyage, and Gemini's compat base URL all serve this shape.\nexport async function openaiProvider(model: string, url: string | undefined, keyEnv: string | undefined): Promise<EmbedProvider> {\n if (!url) throw new SenseError('EMBED_MODEL', 'embed.provider \"openai\" requires a url');\n const base = url.replace(/\\/+$/, '');\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n const key = keyEnv ? process.env[keyEnv] : undefined;\n if (key) headers.authorization = `Bearer ${key}`;\n\n async function post(texts: string[]): Promise<Float32Array[]> {\n const res = await fetchWithRetry(`${base}/embeddings`, { method: 'POST', headers, body: JSON.stringify({ model, input: texts }) });\n if (!res.ok) throw new SenseError('EMBED_MODEL', `${base}/embeddings -> HTTP ${res.status}`);\n const body = (await res.json()) as { data: Array<{ embedding: number[]; index?: number }> };\n // The schema carries `index` because response order is not promised, and the caller maps\n // vectors back to chunks positionally: sort by it whenever every element has one.\n const data = body.data.every((d) => typeof d.index === 'number') ? [...body.data].sort((a, b) => (a.index as number) - (b.index as number)) : body.data;\n // A server that drops an input (empty or over-length strings are the usual cause) would\n // otherwise shift every later vector onto the wrong chunk, silently.\n if (data.length !== texts.length) throw new SenseError('EMBED_MODEL', `${base}/embeddings returned ${data.length} embeddings for ${texts.length} inputs`);\n return data.map((d) => Float32Array.from(d.embedding));\n }\n\n const dims = (await post(['dimension probe']))[0].length;\n // Symmetric for now: per-model prefixes (nomic's search_document/search_query) are a\n // later workstream.\n return { id: `openai:${base}:${model}`, dims, batchCap: BATCH_CAP, embedDocuments: post, embedQuery: async (text) => (await post([text]))[0] };\n}\n"],"names":["SenseError","fetchWithRetry","BATCH_CAP","openaiProvider","model","url","keyEnv","base","replace","headers","key","process","env","undefined","authorization","post","texts","res","method","body","JSON","stringify","input","ok","status","json","data","every","d","index","sort","a","b","length","map","Float32Array","from","embedding","dims","id","batchCap","embedDocuments","embedQuery","text"],"mappings":"AAAA,SAASA,UAAU,QAAQ,eAAe;AAC1C,SAASC,cAAc,QAAQ,YAAY;AAG3C,MAAMC,YAAY;AAElB,6FAA6F;AAC7F,+EAA+E;AAC/E,OAAO,eAAeC,eAAeC,KAAa,EAAEC,GAAuB,EAAEC,MAA0B;IACrG,IAAI,CAACD,KAAK,MAAM,IAAIL,WAAW,eAAe;IAC9C,MAAMO,OAAOF,IAAIG,OAAO,CAAC,QAAQ;IACjC,MAAMC,UAAkC;QAAE,gBAAgB;IAAmB;IAC7E,MAAMC,MAAMJ,SAASK,QAAQC,GAAG,CAACN,OAAO,GAAGO;IAC3C,IAAIH,KAAKD,QAAQK,aAAa,GAAG,CAAC,OAAO,EAAEJ,KAAK;IAEhD,eAAeK,KAAKC,KAAe;QACjC,MAAMC,MAAM,MAAMhB,eAAe,GAAGM,KAAK,WAAW,CAAC,EAAE;YAAEW,QAAQ;YAAQT;YAASU,MAAMC,KAAKC,SAAS,CAAC;gBAAEjB;gBAAOkB,OAAON;YAAM;QAAG;QAChI,IAAI,CAACC,IAAIM,EAAE,EAAE,MAAM,IAAIvB,WAAW,eAAe,GAAGO,KAAK,oBAAoB,EAAEU,IAAIO,MAAM,EAAE;QAC3F,MAAML,OAAQ,MAAMF,IAAIQ,IAAI;QAC5B,yFAAyF;QACzF,kFAAkF;QAClF,MAAMC,OAAOP,KAAKO,IAAI,CAACC,KAAK,CAAC,CAACC,IAAM,OAAOA,EAAEC,KAAK,KAAK,YAAY;eAAIV,KAAKO,IAAI;SAAC,CAACI,IAAI,CAAC,CAACC,GAAGC,IAAM,AAACD,EAAEF,KAAK,GAAeG,EAAEH,KAAK,IAAeV,KAAKO,IAAI;QACvJ,wFAAwF;QACxF,qEAAqE;QACrE,IAAIA,KAAKO,MAAM,KAAKjB,MAAMiB,MAAM,EAAE,MAAM,IAAIjC,WAAW,eAAe,GAAGO,KAAK,qBAAqB,EAAEmB,KAAKO,MAAM,CAAC,gBAAgB,EAAEjB,MAAMiB,MAAM,CAAC,OAAO,CAAC;QACxJ,OAAOP,KAAKQ,GAAG,CAAC,CAACN,IAAMO,aAAaC,IAAI,CAACR,EAAES,SAAS;IACtD;IAEA,MAAMC,OAAO,AAAC,CAAA,MAAMvB,KAAK;QAAC;KAAkB,CAAA,CAAE,CAAC,EAAE,CAACkB,MAAM;IACxD,qFAAqF;IACrF,oBAAoB;IACpB,OAAO;QAAEM,IAAI,CAAC,OAAO,EAAEhC,KAAK,CAAC,EAAEH,OAAO;QAAEkC;QAAME,UAAUtC;QAAWuC,gBAAgB1B;QAAM2B,YAAY,OAAOC,OAAS,AAAC,CAAA,MAAM5B,KAAK;gBAAC4B;aAAK,CAAA,CAAE,CAAC,EAAE;IAAC;AAC/I"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import type { Config } from '../config/index.js';
|
|
3
|
+
export declare function toStore(full: Float32Array, dims: number, int8: boolean): {
|
|
4
|
+
v: Float32Array;
|
|
5
|
+
scale: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function embedPending(db: DatabaseSync, cfg: Config, baseDir: string): Promise<void>;
|
|
8
|
+
export declare function semanticCandidates(db: DatabaseSync, cfg: Config, terms: string, fetch: number, allowed?: Set<string>): Promise<Array<{
|
|
9
|
+
path: string;
|
|
10
|
+
lines: string;
|
|
11
|
+
similarity: number;
|
|
12
|
+
}>>;
|
|
13
|
+
export declare function hasEmbedding(db: DatabaseSync, path: string): boolean;
|
|
14
|
+
export declare function similarNotes(db: DatabaseSync, _cfg: Config, path: string, opts: {
|
|
15
|
+
exclude: Set<string>;
|
|
16
|
+
allowed?: Set<string>;
|
|
17
|
+
k: number;
|
|
18
|
+
}): Array<{
|
|
19
|
+
path: string;
|
|
20
|
+
similarity: number;
|
|
21
|
+
}>;
|