vybekiit 0.7.29 → 0.7.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +3407 -2769
- package/dist/global-guidance/language.md +7 -102
- package/dist/global-skills/add-ai/SKILL.md +7 -8
- package/dist/global-skills/add-analytics/SKILL.md +4 -7
- package/dist/global-skills/add-app-clip/SKILL.md +1 -0
- package/dist/global-skills/add-blog/SKILL.md +4 -7
- package/dist/global-skills/add-files/SKILL.md +4 -7
- package/dist/global-skills/add-images/SKILL.md +4 -7
- package/dist/global-skills/add-language/SKILL.md +5 -8
- package/dist/global-skills/add-notifications/SKILL.md +4 -7
- package/dist/global-skills/add-realtime/SKILL.md +4 -7
- package/dist/global-skills/add-search/SKILL.md +4 -7
- package/dist/global-skills/add-signin/SKILL.md +4 -8
- package/dist/global-skills/add-teams/SKILL.md +4 -7
- package/dist/global-skills/aws-cdk/SKILL.md +1 -0
- package/dist/global-skills/aws-cloudformation/SKILL.md +1 -0
- package/dist/global-skills/aws-serverless/SKILL.md +169 -32
- package/dist/global-skills/aws-serverless/references/api-gateway.md +17 -0
- package/dist/global-skills/aws-serverless/references/architecture.md +9 -13
- package/dist/global-skills/aws-serverless/references/compute-decision-edge-cases.md +77 -0
- package/dist/global-skills/aws-serverless/references/lambda.md +3 -3
- package/dist/global-skills/aws-serverless/references/orchestration.md +46 -20
- package/dist/global-skills/aws-serverless/references/production.md +1 -1
- package/dist/global-skills/better-auth-best-practices/SKILL.md +1 -0
- package/dist/global-skills/better-auth-security-best-practices/SKILL.md +1 -0
- package/dist/global-skills/building-native-ui/SKILL.md +1 -0
- package/dist/global-skills/buy-domain/SKILL.md +4 -7
- package/dist/global-skills/check-safety/SKILL.md +5 -8
- package/dist/global-skills/claimable-postgres/SKILL.md +1 -0
- package/dist/global-skills/configure-capabilities/SKILL.md +1 -1
- package/dist/global-skills/connect-account/SKILL.md +1 -1
- package/dist/global-skills/create-auth-skill/SKILL.md +1 -0
- package/dist/global-skills/doctor/SKILL.md +1 -1
- package/dist/global-skills/eas-app-stores/SKILL.md +2 -1
- package/dist/global-skills/eas-app-stores/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-hosting/SKILL.md +2 -1
- package/dist/global-skills/eas-hosting/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-observe/SKILL.md +2 -1
- package/dist/global-skills/eas-observe/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-simulator/SKILL.md +24 -16
- package/dist/global-skills/eas-simulator/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-simulator/references/controllers.md +11 -7
- package/dist/global-skills/eas-simulator/references/logs-and-crashes.md +101 -0
- package/dist/global-skills/eas-simulator/references/run-your-app.md +8 -8
- package/dist/global-skills/eas-simulator/references/troubleshooting.md +14 -6
- package/dist/global-skills/eas-update/SKILL.md +2 -1
- package/dist/global-skills/eas-update/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-update-insights/SKILL.md +2 -1
- package/dist/global-skills/eas-update-insights/agents/openai.yaml +1 -1
- package/dist/global-skills/eas-workflows/SKILL.md +2 -1
- package/dist/global-skills/eas-workflows/agents/openai.yaml +1 -1
- package/dist/global-skills/email-best-practices/SKILL.md +1 -0
- package/dist/global-skills/email-best-practices/references/sending-reliability.md +17 -22
- package/dist/global-skills/expo-animation/SKILL.md +2 -1
- package/dist/global-skills/expo-api-routes/SKILL.md +1 -0
- package/dist/global-skills/expo-app-clip/SKILL.md +2 -1
- package/dist/global-skills/expo-brownfield/SKILL.md +2 -1
- package/dist/global-skills/expo-cicd-workflows/SKILL.md +1 -0
- package/dist/global-skills/expo-data-fetching/SKILL.md +2 -1
- package/dist/global-skills/expo-deployment/SKILL.md +1 -0
- package/dist/global-skills/expo-design-system/SKILL.md +2 -1
- package/dist/global-skills/expo-dev-client/SKILL.md +2 -1
- package/dist/global-skills/expo-dom/SKILL.md +2 -1
- package/dist/global-skills/expo-examples/SKILL.md +2 -1
- package/dist/global-skills/expo-migrate-module/SKILL.md +2 -1
- package/dist/global-skills/expo-module/SKILL.md +2 -1
- package/dist/global-skills/expo-native-ui/SKILL.md +2 -1
- package/dist/global-skills/expo-observe/SKILL.md +1 -0
- package/dist/global-skills/expo-overview/SKILL.md +2 -1
- package/dist/global-skills/expo-project-structure/SKILL.md +2 -1
- package/dist/global-skills/expo-router/SKILL.md +2 -1
- package/dist/global-skills/expo-skill-eval/SKILL.md +1 -0
- package/dist/global-skills/expo-skill-feedback/SKILL.md +1 -0
- package/dist/global-skills/expo-tailwind-setup/SKILL.md +1 -0
- package/dist/global-skills/expo-ui/SKILL.md +2 -1
- package/dist/global-skills/expo-upgrade/SKILL.md +2 -1
- package/dist/global-skills/expo-web-to-native/SKILL.md +2 -1
- package/dist/global-skills/extension-to-functions-codebase/SKILL.md +2 -0
- package/dist/global-skills/firebase-ai-logic-basics/SKILL.md +162 -76
- package/dist/global-skills/firebase-ai-logic-basics/references/flutter_setup.md +37 -0
- package/dist/global-skills/firebase-ai-logic-basics/references/ios_setup.md +97 -3
- package/dist/global-skills/firebase-ai-logic-basics/references/tts_generation.md +676 -0
- package/dist/global-skills/firebase-ai-logic-basics/references/usage_patterns_android.md +88 -3
- package/dist/global-skills/firebase-ai-logic-basics/references/usage_patterns_web.md +91 -3
- package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +6 -1
- package/dist/global-skills/firebase-auth-basics/SKILL.md +12 -6
- package/dist/global-skills/firebase-auth-basics/references/client_sdk_android.md +61 -23
- package/dist/global-skills/firebase-auth-basics/references/client_sdk_web.md +3 -3
- package/dist/global-skills/firebase-auth-basics/references/flutter_setup.md +22 -19
- package/dist/global-skills/firebase-auth-basics/references/security_rules.md +2 -2
- package/dist/global-skills/firebase-basics/SKILL.md +8 -2
- package/dist/global-skills/firebase-basics/references/android_setup.md +229 -23
- package/dist/global-skills/firebase-basics/references/ios_setup.md +57 -0
- package/dist/global-skills/firebase-basics/references/web_setup.md +52 -0
- package/dist/global-skills/firebase-crashlytics/SKILL.md +2 -0
- package/dist/global-skills/firebase-crashlytics/references/android_setup.md +99 -71
- package/dist/global-skills/firebase-crashlytics/references/ios_setup.md +1 -0
- package/dist/global-skills/firebase-data-connect/SKILL.md +2 -0
- package/dist/global-skills/firebase-data-connect/reference/config.md +8 -8
- package/dist/global-skills/firebase-data-connect/reference/realtime.md +4 -1
- package/dist/global-skills/firebase-data-connect/reference/sdk_android.md +84 -29
- package/dist/global-skills/firebase-data-connect/reference/sdk_web.md +8 -3
- package/dist/global-skills/firebase-data-connect/reference/security.md +13 -13
- package/dist/global-skills/firebase-firestore/SKILL.md +18 -7
- package/dist/global-skills/firebase-firestore/references/enterprise/android_sdk_usage.md +47 -21
- package/dist/global-skills/firebase-firestore/references/enterprise/indexes.md +2 -1
- package/dist/global-skills/firebase-firestore/references/standard/android_sdk_usage.md +63 -27
- package/dist/global-skills/firebase-firestore/references/standard/indexes.md +2 -1
- package/dist/global-skills/firebase-hosting-basics/SKILL.md +2 -0
- package/dist/global-skills/firebase-remote-config-basics/SKILL.md +3 -1
- package/dist/global-skills/firebase-remote-config-basics/references/android_setup.md +83 -92
- package/dist/global-skills/firebase-security-rules-auditor/SKILL.md +8 -6
- package/dist/global-skills/firestore-rules-creation/SKILL.md +23 -10
- package/dist/global-skills/go-live/SKILL.md +7 -7
- package/dist/global-skills/harden/SKILL.md +7 -10
- package/dist/global-skills/instrument-feature-flags/SKILL.md +1 -0
- package/dist/global-skills/instrument-product-analytics/SKILL.md +1 -0
- package/dist/global-skills/manifest.json +2 -1
- package/dist/global-skills/mongodb-atlas-stream-processing/SKILL.md +1 -0
- package/dist/global-skills/mongodb-connection/SKILL.md +1 -0
- package/dist/global-skills/mongodb-mcp-setup/SKILL.md +1 -0
- package/dist/global-skills/mongodb-natural-language-querying/SKILL.md +1 -0
- package/dist/global-skills/mongodb-qe-size-estimation/SKILL.md +224 -0
- package/dist/global-skills/mongodb-query-optimizer/SKILL.md +1 -0
- package/dist/global-skills/mongodb-schema-design/SKILL.md +1 -0
- package/dist/global-skills/mongodb-search-and-ai/SKILL.md +1 -0
- package/dist/global-skills/native-data-fetching/SKILL.md +1 -0
- package/dist/global-skills/neon/SKILL.md +34 -5
- package/dist/global-skills/neon-ai-gateway/SKILL.md +44 -14
- package/dist/global-skills/neon-auth/SKILL.md +1 -0
- package/dist/global-skills/neon-functions/SKILL.md +12 -0
- package/dist/global-skills/neon-functions/references/native-binaries.md +188 -0
- package/dist/global-skills/neon-object-storage/SKILL.md +1 -0
- package/dist/global-skills/neon-postgres/SKILL.md +2 -1
- package/dist/global-skills/neon-postgres/references/full-text-search.md +27 -17
- package/dist/global-skills/neon-postgres/references/hybrid-search.md +19 -15
- package/dist/global-skills/neon-postgres/references/lakebase-search-drizzle.md +99 -14
- package/dist/global-skills/neon-postgres/references/vector-search.md +93 -10
- package/dist/global-skills/neon-postgres-branches/SKILL.md +1 -0
- package/dist/global-skills/neon-postgres-egress-optimizer/SKILL.md +1 -0
- package/dist/global-skills/onboarding/SKILL.md +14 -13
- package/dist/global-skills/plan-my-idea/SKILL.md +1 -1
- package/dist/global-skills/publish-app/SKILL.md +1 -1
- package/dist/global-skills/publish-extension/SKILL.md +1 -1
- package/dist/global-skills/resend/SKILL.md +5 -15
- package/dist/global-skills/resend/references/domains.md +1 -1
- package/dist/global-skills/resend/references/installation.md +3 -3
- package/dist/global-skills/resend/references/sending/best-practices.md +3 -3
- package/dist/global-skills/resend/references/sending/single-email-examples.md +3 -3
- package/dist/global-skills/resend/references/webhooks.md +2 -0
- package/dist/global-skills/reset-password/SKILL.md +4 -6
- package/dist/global-skills/save-data/SKILL.md +3 -0
- package/dist/global-skills/sentry-sdk-setup/SKILL.md +1 -0
- package/dist/global-skills/sentry-workflow/SKILL.md +1 -0
- package/dist/global-skills/setup-email/SKILL.md +7 -8
- package/dist/global-skills/setup-payments/SKILL.md +10 -12
- package/dist/global-skills/setup-sms/SKILL.md +1 -1
- package/dist/global-skills/sign-in-with-email-link/SKILL.md +4 -6
- package/dist/global-skills/sign-in-with-google/SKILL.md +14 -13
- package/dist/global-skills/sign-in-with-phone/SKILL.md +5 -7
- package/dist/global-skills/stripe-best-practices/SKILL.md +20 -19
- package/dist/global-skills/stripe-best-practices/references/billing.md +2 -0
- package/dist/global-skills/stripe-best-practices/references/payments.md +1 -1
- package/dist/global-skills/stripe-best-practices/references/tax.md +27 -15
- package/dist/global-skills/supabase/CHANGELOG.md +19 -0
- package/dist/global-skills/supabase/SKILL.md +1 -0
- package/dist/global-skills/supabase-postgres-best-practices/SKILL.md +1 -0
- package/dist/global-skills/track-errors/SKILL.md +12 -13
- package/dist/global-skills/update-kit/SKILL.md +1 -1
- package/dist/global-skills/upgrading-expo/SKILL.md +1 -0
- package/dist/global-skills/use-dom/SKILL.md +1 -0
- package/dist/global-skills/use-railway/SKILL.md +9 -8
- package/dist/global-skills/use-railway/references/configure.md +34 -1
- package/dist/global-skills/use-railway/references/iac.md +3 -0
- package/dist/global-skills/use-railway/references/tracing.md +97 -44
- package/dist/global-skills/vercel-composition-patterns/SKILL.md +1 -0
- package/dist/global-skills/vercel-react-best-practices/SKILL.md +1 -0
- package/dist/global-skills/vybekiitReferences/backend/AGENTS.md +11 -3
- package/dist/global-skills/vybekiitReferences/backend/agent/goal-index.md +1 -1
- package/dist/global-skills/vybekiitReferences/backend/agent/goal-routes.json +229 -0
- package/dist/global-skills/vybekiitReferences/backend/agent/mcp-setup.md +11 -2
- package/dist/global-skills/vybekiitReferences/backend/agent/session-bootstrap.md +1 -1
- package/dist/global-skills/vybekiitReferences/backend/agent/tech-references.md +1 -1
- package/dist/global-skills/vybekiitReferences/backend/language.md +7 -102
- package/dist/global-skills/vybekiitReferences/backend/platform-skills/deploy-railway-vybekiit.md +2 -2
- package/dist/global-skills/vybekiitReferences/backend/platform-skills/extend-capabilities-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/backend/platform-skills/mongodb-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/extension/AGENTS.md +11 -3
- package/dist/global-skills/vybekiitReferences/extension/agent/goal-index.md +1 -1
- package/dist/global-skills/vybekiitReferences/extension/agent/goal-routes.json +239 -0
- package/dist/global-skills/vybekiitReferences/extension/agent/mcp-setup.md +10 -1
- package/dist/global-skills/vybekiitReferences/extension/agent/session-bootstrap.md +1 -1
- package/dist/global-skills/vybekiitReferences/extension/agent/tech-references.md +1 -1
- package/dist/global-skills/vybekiitReferences/extension/agent/ui-sources.md +11 -8
- package/dist/global-skills/vybekiitReferences/extension/language.md +7 -92
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/browser-automation-vybekiit.md +9 -1
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/env-secrets-vybekiit.md +1 -0
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/extend-capabilities-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/observability-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/extension/platform-skills/sentry-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/AGENTS.md +27 -12
- package/dist/global-skills/vybekiitReferences/mobile/agent/goal-index.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/agent/goal-routes.json +279 -0
- package/dist/global-skills/vybekiitReferences/mobile/agent/mcp-setup.md +10 -1
- package/dist/global-skills/vybekiitReferences/mobile/agent/session-bootstrap.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/agent/tech-references.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/agent/ui-sources.mobile.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/language.md +7 -93
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/env-secrets-vybekiit.md +1 -0
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/extend-capabilities-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/i18n-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/observability-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/sentry-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/platform-skills/ui-port-from-web-vybekiit.md +2 -2
- package/dist/global-skills/vybekiitReferences/spa/AGENTS.md +11 -3
- package/dist/global-skills/vybekiitReferences/spa/agent/goal-index.md +1 -1
- package/dist/global-skills/vybekiitReferences/spa/agent/goal-routes.json +267 -0
- package/dist/global-skills/vybekiitReferences/spa/agent/mcp-setup.md +10 -1
- package/dist/global-skills/vybekiitReferences/spa/agent/session-bootstrap.md +1 -1
- package/dist/global-skills/vybekiitReferences/spa/agent/ui-sources.md +11 -8
- package/dist/global-skills/vybekiitReferences/spa/language.md +7 -11
- package/dist/global-skills/vybekiitReferences/web/AGENTS.md +15 -30
- package/dist/global-skills/vybekiitReferences/web/agent/goal-index.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/agent/goal-routes.json +366 -0
- package/dist/global-skills/vybekiitReferences/web/agent/mcp-setup.md +11 -2
- package/dist/global-skills/vybekiitReferences/web/agent/session-bootstrap.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/language.md +7 -102
- package/dist/global-skills/vybekiitReferences/web/platform-skills/ai-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/analytics-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/assets-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/better-auth-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/browser-automation-vybekiit.md +9 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/cms-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/compliance-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/deploy-github-pages-vybekiit.md +2 -2
- package/dist/global-skills/vybekiitReferences/web/platform-skills/deploy-railway-vybekiit.md +2 -2
- package/dist/global-skills/vybekiitReferences/web/platform-skills/deploy-vercel-vybekiit.md +2 -2
- package/dist/global-skills/vybekiitReferences/web/platform-skills/env-secrets-vybekiit.md +1 -0
- package/dist/global-skills/vybekiitReferences/web/platform-skills/extend-capabilities-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/i18n-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/jobs-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/kv-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/lemon-squeezy-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/mongodb-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/notifications-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/observability-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/paypal-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/realtime-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/registrar-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/search-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/sentry-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/seo-vybekiit.md +1 -3
- package/dist/global-skills/vybekiitReferences/web/platform-skills/supabase-vybekiit.md +1 -1
- package/dist/global-skills/vybekiitReferences/web/platform-skills/tenancy-vybekiit.md +1 -1
- package/dist/global-skills/web-to-native/SKILL.md +1 -0
- package/dist/global-skills/workers-best-practices/SKILL.md +5 -4
- package/dist/global-skills/workers-best-practices/references/configuration.md +1 -1
- package/dist/global-skills/workers-best-practices/references/platform-apis.md +5 -5
- package/dist/global-skills/workers-best-practices/references/runtime-patterns.md +1 -1
- package/dist/global-skills/wrangler/SKILL.md +28 -25
- package/dist/global-skills/xcode-project-setup/SKILL.md +2 -0
- package/package.json +10 -8
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Shipping native binaries with a Function
|
|
2
|
+
|
|
3
|
+
The default deploy bundles `source` into a single `index.mjs` with esbuild. A compiled binary cannot be inlined into that file, so it has to ship as a separate file in the deploy archive. There are three ways to put it there. Pick by what the binary is:
|
|
4
|
+
|
|
5
|
+
| The binary is | Use |
|
|
6
|
+
| ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
|
|
7
|
+
| An npm package backed by a Node-API addon (`sharp`, most `@napi-rs/*` packages) | [`externalPackages`](#externalpackages-npm-packages-with-a-node-addon) |
|
|
8
|
+
| A file your own build step produces: a `.node` addon, a `.so`, a `.wasm`, model weights | [`bundler: "none"`](#bundler-none-ship-a-prebuilt-directory) or a [custom `bundler`](#custom-bundler-return-the-file-map) |
|
|
9
|
+
| A standalone executable you spawn (`ffmpeg`, a Go or Rust CLI) | A custom `bundler` or `"none"`, plus [a copy to `/run` at startup](#standalone-executables) |
|
|
10
|
+
|
|
11
|
+
The examples use top-level `functions`, which needs `neon` CLI 4.20 or newer and `@neon/config` 1.6 or newer.
|
|
12
|
+
|
|
13
|
+
## The runtime a binary lands on
|
|
14
|
+
|
|
15
|
+
- **linux, arm64, glibc**, Node.js 24. A binary built for macOS or x64 cannot load there.
|
|
16
|
+
- The archive is extracted to `/opt/function`, a read-only filesystem. `import.meta.url` in the root `index.mjs` points there, so `new URL("./bin/tool", import.meta.url)` resolves to `/opt/function/bin/tool`.
|
|
17
|
+
- **Every file arrives with mode `644`.** Unix permission bits in the zip are dropped on extraction, and `chmod` in place fails with `EROFS`. A `.node` or `.so` loads fine (it is `dlopen`ed, not executed). Spawning a file from `/opt/function` fails with `EACCES`.
|
|
18
|
+
- `/tmp` is writable and mounted `noexec`: a copied binary still fails with `EACCES` after `chmod 755`.
|
|
19
|
+
- `/run` is a writable tmpfs that allows exec (about 990 MiB, backed by the function's 2048 MiB of memory).
|
|
20
|
+
|
|
21
|
+
The filesystem layout is observed behavior, not documented by Neon. Re-check it if spawning starts failing.
|
|
22
|
+
|
|
23
|
+
## `externalPackages`: npm packages with a Node addon
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// neon.ts
|
|
27
|
+
import { defineConfig } from "@neon/config/v1";
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
functions: {
|
|
31
|
+
resize: {
|
|
32
|
+
name: "Resize",
|
|
33
|
+
source: "./src/resize.ts",
|
|
34
|
+
externalPackages: ["sharp"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
esbuild leaves `import sharp from "sharp"` unresolved. At deploy, the CLI installs the version of `sharp` from your project with `npm install --cpu=arm64 --os=linux --libc=glibc --ignore-scripts` into a temp directory, traces the files it reaches with `@vercel/nft`, and copies them into the archive under `node_modules/` with the tree layout intact. Only the installed version is read from your `node_modules`; the shipped files come from the temp install, and your `node_modules` is not modified.
|
|
41
|
+
|
|
42
|
+
The deploy fails with a named error when:
|
|
43
|
+
|
|
44
|
+
- the package is not installed in your project (no version to pin)
|
|
45
|
+
- the declared package itself does not install for linux-arm64 glibc (`EBADPLATFORM`)
|
|
46
|
+
- a staged `.node` or `.so` is not an AArch64 ELF binary
|
|
47
|
+
- `npm` is not on `PATH`
|
|
48
|
+
- the archive exceeds the [size limits](#size-limits)
|
|
49
|
+
|
|
50
|
+
It does not fail when a platform-specific optional dependency is silently skipped, or when a package that compiles from source at install time ships no binary (the staging install runs with `--ignore-scripts`). Those deploy and then fail at invoke. Use packages that publish a linux-arm64 glibc prebuild (`sharp` does), and invoke the deployed function once to confirm the addon loads.
|
|
51
|
+
|
|
52
|
+
A deploy and `neon dev` print an advisory warning for any bundled package that carries native code and is not declared. A package with a working JavaScript fallback (`ws` with `bufferutil`) triggers it too and needs no change. Do not silence it with `{ name, includeFiles: false }`: that externalizes the package and ships nothing, so a reached import throws `Cannot find module` on every invoke. `includeFiles: false` is only for an import the function never evaluates.
|
|
53
|
+
|
|
54
|
+
Under `neon dev`, the package is only kept out of the bundle and resolves from your own `node_modules` for your host.
|
|
55
|
+
|
|
56
|
+
Pros: one line in `neon.ts`; the target-platform install, file tracing, arch check, and size check are automatic; local dev uses your host build.
|
|
57
|
+
|
|
58
|
+
Cons: only for npm packages that publish a linux-arm64 glibc prebuild; transitive pins, overrides, and patches from your lockfile are not carried into the staged install; esbuild bundler only (`externalPackages` with `bundler: "none"` or a function fails validation).
|
|
59
|
+
|
|
60
|
+
## `bundler: "none"`: ship a prebuilt directory
|
|
61
|
+
|
|
62
|
+
Run your own build, then point `source` at its output. The directory root must contain `index.mjs` or `index.js`. Everything else in the directory ships as-is:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
dist/fn/
|
|
66
|
+
index.mjs # built by your step, reads ./native/addon.node
|
|
67
|
+
native/addon.node
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// neon.ts
|
|
72
|
+
import { defineConfig } from "@neon/config/v1";
|
|
73
|
+
|
|
74
|
+
export default defineConfig({
|
|
75
|
+
functions: {
|
|
76
|
+
api: { name: "API", source: "./dist/fn", bundler: "none" },
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm run build:fn && neon deploy --env .env.local
|
|
83
|
+
neon functions deploy api --src dist/fn --no-bundle # same thing, without neon.ts
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`neon deploy` does not run your build step. Run it first, or use a [custom `bundler`](#custom-bundler-return-the-file-map) to keep the build inside `neon deploy`.
|
|
87
|
+
|
|
88
|
+
Pros: works with any build tool or framework output (`.mastra/output` is the common one); full control over the archive layout.
|
|
89
|
+
|
|
90
|
+
Cons: no architecture check, so a binary built for your laptop deploys and then fails at invoke; the build step is yours to keep in sync with `neon deploy`; TypeScript cannot ship unbundled.
|
|
91
|
+
|
|
92
|
+
## Custom `bundler`: return the file map
|
|
93
|
+
|
|
94
|
+
An inline function in `neon.ts` returns archive paths mapped to bytes. `neon deploy` zips the map, and `neon dev` serves the same map locally:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// neon.ts
|
|
98
|
+
import { readFile } from "node:fs/promises";
|
|
99
|
+
import { build } from "esbuild";
|
|
100
|
+
import { defineConfig } from "@neon/config/v1";
|
|
101
|
+
|
|
102
|
+
export default defineConfig({
|
|
103
|
+
functions: {
|
|
104
|
+
transcode: {
|
|
105
|
+
name: "Transcode",
|
|
106
|
+
source: "./src/transcode.ts",
|
|
107
|
+
bundler: async (fn) => {
|
|
108
|
+
const out = await build({
|
|
109
|
+
entryPoints: [fn.source],
|
|
110
|
+
bundle: true,
|
|
111
|
+
platform: "node",
|
|
112
|
+
format: "esm",
|
|
113
|
+
write: false,
|
|
114
|
+
outfile: "index.mjs",
|
|
115
|
+
// CommonJS dependencies call require(); ESM output has none in scope.
|
|
116
|
+
banner: {
|
|
117
|
+
js: "import{createRequire}from'module';const require=createRequire(import.meta.url);",
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
"index.mjs": out.outputFiles[0].contents,
|
|
122
|
+
"bin/ffmpeg": new Uint8Array(
|
|
123
|
+
await readFile(new URL("./vendor/linux-arm64/ffmpeg", import.meta.url)),
|
|
124
|
+
),
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`esbuild` must be a dependency of your project. The map needs `index.mjs` or `index.js` at the root.
|
|
133
|
+
|
|
134
|
+
Pros: the build lives in `neon.ts`, so `neon deploy` and `neon dev` always build the same thing; any file from anywhere can go in the archive.
|
|
135
|
+
|
|
136
|
+
Cons: no architecture check; `externalPackages` cannot be combined with it, so an npm addon has to be copied into the map by hand, with its `node_modules/` layout.
|
|
137
|
+
|
|
138
|
+
## Standalone executables
|
|
139
|
+
|
|
140
|
+
Ship the executable with a custom `bundler` or `bundler: "none"`, then copy it to `/run` and mark it executable once per isolate:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// src/transcode.ts
|
|
144
|
+
import { execFile } from "node:child_process";
|
|
145
|
+
import { chmod, copyFile, mkdir } from "node:fs/promises";
|
|
146
|
+
import { promisify } from "node:util";
|
|
147
|
+
|
|
148
|
+
const run = promisify(execFile);
|
|
149
|
+
|
|
150
|
+
let ffmpeg: Promise<string> | undefined;
|
|
151
|
+
function ffmpegPath(): Promise<string> {
|
|
152
|
+
if (process.env.FFMPEG_PATH) return Promise.resolve(process.env.FFMPEG_PATH);
|
|
153
|
+
ffmpeg ??= (async () => {
|
|
154
|
+
await mkdir("/run/bin", { recursive: true });
|
|
155
|
+
await copyFile(new URL("./bin/ffmpeg", import.meta.url), "/run/bin/ffmpeg");
|
|
156
|
+
await chmod("/run/bin/ffmpeg", 0o755);
|
|
157
|
+
return "/run/bin/ffmpeg";
|
|
158
|
+
})();
|
|
159
|
+
return ffmpeg;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export default {
|
|
163
|
+
async fetch() {
|
|
164
|
+
const { stdout } = await run(await ffmpegPath(), ["-version"]);
|
|
165
|
+
return new Response(stdout);
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`neon dev` runs this code on your machine, where `/run` may not exist and a linux-arm64 binary cannot execute. Point it at a host install from the shell, and leave `FFMPEG_PATH` out of the function's deployed `env`:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
FFMPEG_PATH="$(command -v ffmpeg)" neon dev
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The binary must be statically linked, or its shared libraries must exist in the runtime image. The copy costs memory: `/run` is RAM-backed and counts against the function's 2048 MiB.
|
|
177
|
+
|
|
178
|
+
## Size limits
|
|
179
|
+
|
|
180
|
+
`bundler: "none"`, a custom `bundler`, and `externalPackages` staging check the archive before upload:
|
|
181
|
+
|
|
182
|
+
| Limit | Value |
|
|
183
|
+
| ------------------------ | ------- |
|
|
184
|
+
| Compressed zip | 10 MiB |
|
|
185
|
+
| Total uncompressed bytes | 64 MiB |
|
|
186
|
+
| Files in the archive | 4,096 |
|
|
187
|
+
|
|
188
|
+
Exceeding one fails the deploy. The byte-limit errors list the four largest files. Check an executable's size before building around it: a single static binary can use most of the 64 MiB.
|
|
@@ -15,6 +15,7 @@ description: >-
|
|
|
15
15
|
metadata:
|
|
16
16
|
parent: neon
|
|
17
17
|
source: https://github.com/neondatabase/agent-skills/tree/main/skills/neon-object-storage
|
|
18
|
+
disable-model-invocation: true
|
|
18
19
|
---
|
|
19
20
|
|
|
20
21
|
**FIRST**: Use the parent `neon` skill for a Neon overview, getting started with Neon, Neon development best practices, and more.
|
|
@@ -13,6 +13,7 @@ description: >-
|
|
|
13
13
|
metadata:
|
|
14
14
|
parent: neon
|
|
15
15
|
source: https://github.com/neondatabase/agent-skills/tree/main/skills/neon-postgres
|
|
16
|
+
disable-model-invocation: true
|
|
16
17
|
---
|
|
17
18
|
|
|
18
19
|
**FIRST**: Use the parent `neon` skill for a Neon overview, getting started with Neon, Neon development best practices, and more.
|
|
@@ -257,7 +258,7 @@ Link: https://neon.com/docs/guides/logical-replication-guide.md
|
|
|
257
258
|
|
|
258
259
|
Use Lakebase Search for semantic, full-text, and hybrid search:
|
|
259
260
|
|
|
260
|
-
- For semantic search, read [Vector search](references/vector-search.md).
|
|
261
|
+
- For semantic search, including generating embeddings with the Neon AI Gateway, read [Vector search](references/vector-search.md).
|
|
261
262
|
- For full-text search with BM25 ranking, read [Full-text search](references/full-text-search.md).
|
|
262
263
|
- For combining semantic and lexical results, read [Hybrid search](references/hybrid-search.md).
|
|
263
264
|
- For managing any of the above through Drizzle ORM, read [Managing Lakebase Search with Drizzle](references/lakebase-search-drizzle.md).
|
|
@@ -45,18 +45,24 @@ After a large bulk load, run `VACUUM` to refresh the statistics used by BM25 sco
|
|
|
45
45
|
`to_bm25query` binds the query `tsvector` to the BM25 index whose corpus statistics should be used. The `<@>` operator returns a negative BM25 score, so lower (more negative) values are more relevant and must sort ascending:
|
|
46
46
|
|
|
47
47
|
```sql
|
|
48
|
-
SELECT
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
SELECT id, title, score
|
|
49
|
+
FROM (
|
|
50
|
+
SELECT
|
|
51
|
+
id,
|
|
52
|
+
title,
|
|
53
|
+
body_tsv <@> to_bm25query(
|
|
54
|
+
to_tsvector('english', $1),
|
|
55
|
+
'documents_body_bm25'::regclass
|
|
56
|
+
) AS score
|
|
57
|
+
FROM documents
|
|
58
|
+
) AS keyword_scored
|
|
59
|
+
WHERE score < 0
|
|
56
60
|
ORDER BY score
|
|
57
61
|
LIMIT $2;
|
|
58
62
|
```
|
|
59
63
|
|
|
64
|
+
Scalar BM25 scoring returns zero for documents containing none of the query terms. Filter `score < 0` before `ORDER BY` and `LIMIT` so keyword results exclude non-matches under both sequential and index plans. Partial query-term matches remain eligible.
|
|
65
|
+
|
|
60
66
|
Use the same text-search configuration for document and query vectors. Select a language-specific or custom configuration that matches the corpus.
|
|
61
67
|
|
|
62
68
|
## Set the Candidate Limit
|
|
@@ -72,15 +78,19 @@ BEGIN;
|
|
|
72
78
|
SET LOCAL lakebase_bm25.default_limit = 20;
|
|
73
79
|
SET LOCAL lakebase_bm25.prefilter = on;
|
|
74
80
|
|
|
75
|
-
SELECT
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
SELECT id, title, score
|
|
82
|
+
FROM (
|
|
83
|
+
SELECT
|
|
84
|
+
id,
|
|
85
|
+
title,
|
|
86
|
+
body_tsv <@> to_bm25query(
|
|
87
|
+
to_tsvector('english', $1),
|
|
88
|
+
'documents_body_bm25'::regclass
|
|
89
|
+
) AS score
|
|
90
|
+
FROM documents
|
|
91
|
+
WHERE id % 1000 = 0
|
|
92
|
+
) AS keyword_scored
|
|
93
|
+
WHERE score < 0
|
|
84
94
|
ORDER BY score
|
|
85
95
|
LIMIT $2;
|
|
86
96
|
COMMIT;
|
|
@@ -18,13 +18,13 @@ CREATE TABLE documents (
|
|
|
18
18
|
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
19
19
|
title text NOT NULL,
|
|
20
20
|
body text NOT NULL,
|
|
21
|
-
embedding vector(
|
|
21
|
+
embedding vector(1024),
|
|
22
22
|
body_tsv tsvector GENERATED ALWAYS AS
|
|
23
23
|
(to_tsvector('english', body)) STORED
|
|
24
24
|
);
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
The 1024-dimensional column is an example. Set it to the output dimension of your selected embedding model. Generate document embeddings in batches and embed each query with the same model and dimensions; [Vector search](vector-search.md#prepare-embeddings) shows the AI Gateway calls. Follow any model-specific query instructions, while keeping the original query text for BM25. Choose a PostgreSQL text-search configuration appropriate for the corpus.
|
|
28
28
|
|
|
29
29
|
Create and validate each retriever independently before combining them. Follow [Vector search](vector-search.md) and [Full-text search](full-text-search.md) for their indexes, query operators, and tuning.
|
|
30
30
|
|
|
@@ -34,7 +34,7 @@ Reciprocal Rank Fusion (RRF) is the approach in the Lakebase Search get-started
|
|
|
34
34
|
|
|
35
35
|
For rank `r` and constant `k`, each retriever contributes `1 / (k + r)`. The documented starting point uses 40 candidates per retriever and `k = 60`; tune both for the corpus and workload.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Embed the query text through the same gateway model used for the stored documents, then bind that vector as `$1`, the original query text as `$2`, and the final result count as `$3`:
|
|
38
38
|
|
|
39
39
|
```sql
|
|
40
40
|
WITH vector_ranked AS (
|
|
@@ -49,15 +49,19 @@ WITH vector_ranked AS (
|
|
|
49
49
|
keyword_ranked AS (
|
|
50
50
|
SELECT id, RANK() OVER (ORDER BY score) AS rank
|
|
51
51
|
FROM (
|
|
52
|
-
SELECT
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
SELECT id, score
|
|
53
|
+
FROM (
|
|
54
|
+
SELECT
|
|
55
|
+
id,
|
|
56
|
+
body_tsv <@> to_bm25query(
|
|
57
|
+
to_tsvector('english', $2),
|
|
58
|
+
'documents_body_bm25'::regclass
|
|
59
|
+
) AS score
|
|
60
|
+
FROM documents
|
|
61
|
+
) AS keyword_scored
|
|
62
|
+
WHERE score < 0
|
|
63
|
+
ORDER BY score, id
|
|
64
|
+
LIMIT 40
|
|
61
65
|
) AS keyword_candidates
|
|
62
66
|
)
|
|
63
67
|
SELECT
|
|
@@ -73,13 +77,13 @@ ORDER BY rrf_score DESC, d.id
|
|
|
73
77
|
LIMIT $3;
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
`
|
|
80
|
+
`score < 0` excludes BM25 non-matches. Many matching documents can share a BM25 score, so `WITH TIES` could admit a large group at the cutoff. `ORDER BY score, id LIMIT 40` keeps keyword candidates bounded and breaks boundary ties by ID. `RANK()` uses score alone so retained ties receive equal ranks.
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
Vector retrieval preserves cutoff ties and can exceed 40 candidates. `lakebase_bm25.default_limit` defaults to `1000`; the SQL limit bounds keyword rows entering fusion, not total scoring work.
|
|
79
83
|
|
|
80
84
|
## Adapt the Hybrid Search
|
|
81
85
|
|
|
82
|
-
- Retrieve more candidates from each source than the final result count; otherwise one retriever can dominate before fusion has enough overlap. Keep `lakebase_bm25.default_limit`
|
|
86
|
+
- Retrieve more candidates from each source than the final result count; otherwise one retriever can dominate before fusion has enough overlap. Keep `lakebase_bm25.default_limit` at least as large as the BM25 candidate target.
|
|
83
87
|
- Keep each retriever's operator and index configuration correct independently before tuning RRF.
|
|
84
88
|
- Tune candidate counts and the RRF constant with judged or behavioral relevance data, plus latency measurements.
|
|
85
89
|
- Add weights only when product evidence shows one retriever should contribute more. Weight the reciprocal-rank contributions, not the raw vector distance and negative BM25 score.
|
|
@@ -10,6 +10,7 @@ Contents:
|
|
|
10
10
|
- [Extensions](#extensions): custom migration required to create extensions (Drizzle can't)
|
|
11
11
|
- [Schema](#schema): columns, generated `tsvector`, and the ANN index
|
|
12
12
|
- [BM25 Index](#bm25-index): created after the corpus is seeded
|
|
13
|
+
- [Generate and Store Embeddings](#generate-and-store-embeddings): call the AI Gateway and insert vectors with Drizzle
|
|
13
14
|
- [Query](#query): vector, BM25, and hybrid reads
|
|
14
15
|
- [Tune Per Query](#tune-per-query): per-query GUCs
|
|
15
16
|
|
|
@@ -22,18 +23,22 @@ Rules:
|
|
|
22
23
|
|
|
23
24
|
## Config
|
|
24
25
|
|
|
25
|
-
`drizzle-kit generate` and `migrate` read `drizzle.config.ts`. Point `dbCredentials.url` at the **direct (unpooled)** connection string:
|
|
26
|
+
`drizzle-kit generate` and `migrate` read `drizzle.config.ts`. If the connection string is in `.env`, install `dotenv` (`npm install dotenv`) and load it here. Point `dbCredentials.url` at the **direct (unpooled)** connection string:
|
|
26
27
|
|
|
27
28
|
```typescript
|
|
28
29
|
// drizzle.config.ts
|
|
30
|
+
import "dotenv/config";
|
|
29
31
|
import { defineConfig } from "drizzle-kit";
|
|
30
32
|
|
|
33
|
+
const databaseUrl = process.env.DATABASE_URL_UNPOOLED;
|
|
34
|
+
if (!databaseUrl) throw new Error("Missing DATABASE_URL_UNPOOLED");
|
|
35
|
+
|
|
31
36
|
export default defineConfig({
|
|
32
37
|
schema: "./src/schema.ts",
|
|
33
38
|
out: "./drizzle",
|
|
34
39
|
dialect: "postgresql",
|
|
35
40
|
// Direct (unpooled) URL. Neon exposes it as DATABASE_URL_UNPOOLED.
|
|
36
|
-
dbCredentials: { url:
|
|
41
|
+
dbCredentials: { url: databaseUrl },
|
|
37
42
|
});
|
|
38
43
|
```
|
|
39
44
|
|
|
@@ -57,7 +62,14 @@ The columns, the generated `tsvector`, and the `lakebase_ann` index all go in `s
|
|
|
57
62
|
|
|
58
63
|
```typescript
|
|
59
64
|
// src/schema.ts
|
|
60
|
-
import {
|
|
65
|
+
import {
|
|
66
|
+
pgTable,
|
|
67
|
+
bigint,
|
|
68
|
+
text,
|
|
69
|
+
vector,
|
|
70
|
+
index,
|
|
71
|
+
customType,
|
|
72
|
+
} from "drizzle-orm/pg-core";
|
|
61
73
|
import { sql } from "drizzle-orm";
|
|
62
74
|
|
|
63
75
|
const tsvector = customType<{ data: string }>({
|
|
@@ -69,10 +81,12 @@ const tsvector = customType<{ data: string }>({
|
|
|
69
81
|
export const documents = pgTable(
|
|
70
82
|
"documents",
|
|
71
83
|
{
|
|
72
|
-
id: bigint("id", { mode: "number" })
|
|
84
|
+
id: bigint("id", { mode: "number" })
|
|
85
|
+
.generatedByDefaultAsIdentity()
|
|
86
|
+
.primaryKey(),
|
|
73
87
|
title: text("title").notNull(),
|
|
74
88
|
body: text("body").notNull(),
|
|
75
|
-
embedding: vector("embedding", { dimensions:
|
|
89
|
+
embedding: vector("embedding", { dimensions: 1024 }),
|
|
76
90
|
bodyTsv: tsvector("body_tsv").generatedAlwaysAs(
|
|
77
91
|
sql`to_tsvector('english', "body")`,
|
|
78
92
|
),
|
|
@@ -86,7 +100,7 @@ export const documents = pgTable(
|
|
|
86
100
|
);
|
|
87
101
|
```
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
The 1024-dimensional column is an example; set it to the output dimension of your selected embedding model. Use the same model and dimensions for stored documents and queries. Postgres maintains `body_tsv`, so never write it from the app. Generate and apply the migration after the extensions migration above:
|
|
90
104
|
|
|
91
105
|
```bash
|
|
92
106
|
npx drizzle-kit generate --name=lakebase_search
|
|
@@ -106,6 +120,66 @@ npx drizzle-kit generate --custom --name=bm25_index
|
|
|
106
120
|
CREATE INDEX documents_body_bm25 ON documents USING lakebase_bm25 (body_tsv);
|
|
107
121
|
```
|
|
108
122
|
|
|
123
|
+
## Generate and Store Embeddings
|
|
124
|
+
|
|
125
|
+
Use the [Vercel AI SDK](https://ai-sdk.dev) (`ai`, 6+) with Neon's `@neon/ai-sdk-provider` 1.0.0+. Follow the [gateway setup](vector-search.md#prepare-embeddings), then install the packages with `npm install @neon/ai-sdk-provider ai dotenv`:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import "dotenv/config";
|
|
129
|
+
import { neon } from "@neon/ai-sdk-provider";
|
|
130
|
+
import { embed, embedMany } from "ai";
|
|
131
|
+
|
|
132
|
+
const embeddingModel = neon.embeddingModel("qwen3-embedding-0-6b");
|
|
133
|
+
const providerOptions = { neon: { dimensions: 1024 } };
|
|
134
|
+
// Qwen3 query prompt adapted for Neon documentation search; documents stay unprefixed.
|
|
135
|
+
const queryPrompt =
|
|
136
|
+
"Instruct: Given a question about Neon, retrieve documentation passages that answer the question\nQuery: ";
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Generate a document vector, then insert it with Drizzle:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const { embedding } = await embed({
|
|
143
|
+
model: embeddingModel,
|
|
144
|
+
value: body,
|
|
145
|
+
providerOptions,
|
|
146
|
+
});
|
|
147
|
+
await db.insert(documents).values({
|
|
148
|
+
title,
|
|
149
|
+
body,
|
|
150
|
+
embedding,
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For bulk imports, skip blank rows and embed/insert one batch at a time:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const batchSize = 64;
|
|
158
|
+
const nonBlankRows = rows.filter((row) => row && row.body.trim().length > 0);
|
|
159
|
+
|
|
160
|
+
for (let offset = 0; offset < nonBlankRows.length; offset += batchSize) {
|
|
161
|
+
const batch = nonBlankRows.slice(offset, offset + batchSize);
|
|
162
|
+
const { embeddings: vectors } = await embedMany({
|
|
163
|
+
model: embeddingModel,
|
|
164
|
+
values: batch.map((row) => row.body),
|
|
165
|
+
providerOptions,
|
|
166
|
+
maxParallelCalls: 1,
|
|
167
|
+
});
|
|
168
|
+
await db.insert(documents).values(
|
|
169
|
+
batch.map((row, index) => {
|
|
170
|
+
const embedding = vectors[index];
|
|
171
|
+
if (!embedding)
|
|
172
|
+
throw new Error(`Missing embedding for batch row ${index}`);
|
|
173
|
+
return { ...row, embedding };
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`embedMany` splits embedding requests using the provider's per-request input limit. This loop also bounds database inserts and memory; tune `batchSize` for your workload. Chunk long documents before embedding. Earlier batches stay committed if a later batch fails.
|
|
180
|
+
|
|
181
|
+
Apply `queryPrompt` only to queries, keeping the original text for BM25. Use the same model and dimensions for documents and queries; re-embed documents when switching models.
|
|
182
|
+
|
|
109
183
|
## Query
|
|
110
184
|
|
|
111
185
|
Use the query builder with Drizzle's `cosineDistance` helper for vector search. It emits the `<=>` operator, so keep the index on `vector_cosine_ops`:
|
|
@@ -114,7 +188,11 @@ Use the query builder with Drizzle's `cosineDistance` helper for vector search.
|
|
|
114
188
|
import { cosineDistance } from "drizzle-orm";
|
|
115
189
|
import { documents } from "./schema";
|
|
116
190
|
|
|
117
|
-
|
|
191
|
+
const { embedding: queryEmbedding } = await embed({
|
|
192
|
+
model: embeddingModel,
|
|
193
|
+
value: `${queryPrompt}${queryText}`,
|
|
194
|
+
providerOptions,
|
|
195
|
+
});
|
|
118
196
|
const distance = cosineDistance(documents.embedding, queryEmbedding);
|
|
119
197
|
|
|
120
198
|
const rows = await db
|
|
@@ -130,17 +208,23 @@ BM25 has no Drizzle helper: `<@>` and `to_bm25query` require raw SQL. Reference
|
|
|
130
208
|
import { sql } from "drizzle-orm";
|
|
131
209
|
|
|
132
210
|
const rows = await db.execute(sql`
|
|
133
|
-
SELECT id, title,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
211
|
+
SELECT id, title, score
|
|
212
|
+
FROM (
|
|
213
|
+
SELECT id, title,
|
|
214
|
+
body_tsv <@> to_bm25query(
|
|
215
|
+
to_tsvector('english', ${queryText}),
|
|
216
|
+
'documents_body_bm25'::regclass
|
|
217
|
+
) AS score
|
|
218
|
+
FROM documents
|
|
219
|
+
) AS keyword_scored
|
|
220
|
+
WHERE score < 0
|
|
139
221
|
ORDER BY score
|
|
140
222
|
LIMIT ${k}
|
|
141
223
|
`);
|
|
142
224
|
```
|
|
143
225
|
|
|
226
|
+
Filter `score < 0` before the candidate cutoff: scalar BM25 scoring returns zero for non-matches, which must not become keyword hits. Partial query-term matches remain eligible.
|
|
227
|
+
|
|
144
228
|
Run the [hybrid search](hybrid-search.md) RRF query the same way: raw SQL through `db.execute`.
|
|
145
229
|
|
|
146
230
|
## Tune Per Query
|
|
@@ -168,5 +252,6 @@ const rows = await db.transaction(async (tx) => {
|
|
|
168
252
|
|
|
169
253
|
Sources:
|
|
170
254
|
|
|
255
|
+
- [AI Gateway embeddings](https://neon.com/docs/ai-gateway/embeddings)
|
|
171
256
|
- [Get started with Lakebase Search](https://neon.com/docs/ai/lakebase-search-get-started)
|
|
172
|
-
- [Schema migration with Lakebase Postgres and Drizzle ORM](https://neon.com/docs/guides/drizzle-migrations)
|
|
257
|
+
- [Schema migration with Lakebase Postgres and Drizzle ORM](https://neon.com/docs/guides/drizzle-migrations)
|