@xano-sdk/vector 1.0.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/AGENTS.md +68 -0
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/index.d.ts +1189 -0
- package/dist/index.js +1429 -0
- package/llms.txt +62 -0
- package/package.json +85 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
> For agents **consuming** the published package, see [llms.txt](llms.txt) instead.
|
|
4
|
+
> This file is for agents **working on and maintaining** the `@xano-sdk/vector` package itself.
|
|
5
|
+
|
|
6
|
+
## What This Is
|
|
7
|
+
|
|
8
|
+
`@xano-sdk/vector` is a Xano SDK module providing an end-to-end vector embedding & similarity search pipeline using Google Gemini Embeddings (768 dimensions) and pgvector.
|
|
9
|
+
|
|
10
|
+
There is **no runtime execution** inside this package: every export is a plain, typed Xano SDK def object (`table()`, `defineFunction()`, `tool()`, `apiGroup()`, `query()`) or a def factory. All registration and compilation happens in the consumer's `@xano/sdk` workspace compiler at `export()`.
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Typecheck
|
|
16
|
+
npm run typecheck
|
|
17
|
+
|
|
18
|
+
# Run test suite
|
|
19
|
+
npm test
|
|
20
|
+
|
|
21
|
+
# Regenerate golden fixture (deliberate act only — see below)
|
|
22
|
+
npm run fixture:regen
|
|
23
|
+
|
|
24
|
+
# Build distribution bundle
|
|
25
|
+
npm run build
|
|
26
|
+
|
|
27
|
+
# Lint
|
|
28
|
+
npm run lint
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Directory Layout
|
|
32
|
+
|
|
33
|
+
- `src/options.ts`: Option types, defaults, and the single `resolveOptions` validation gate.
|
|
34
|
+
- `src/tables/`: `documentTable` and `chunkTable` (with `f.vector(768)` and HNSW cosine index).
|
|
35
|
+
- `src/functions/`: `generateEmbeddingFn`, `chunkTextFn`, `ingestDocumentFn`, and `searchVectorsFn`.
|
|
36
|
+
- `src/tool/`: `vectorSearchTool` for AI Agent knowledge retrieval.
|
|
37
|
+
- `src/api/`: Endpoint definitions (`group.ts`, `documents.ts`, `search.ts`, `types.ts`, `client-types.ts`).
|
|
38
|
+
- `src/register.ts`: `createVector` and `registerVector`.
|
|
39
|
+
- `src/index.ts`: The unified public package surface.
|
|
40
|
+
- `test/`: Unit tests, options tests, golden bundle tests, published docs contract.
|
|
41
|
+
- `scripts/regen-golden.ts`: Regenerates `test/fixtures/golden-bundle.json`.
|
|
42
|
+
|
|
43
|
+
## Rules That Bite
|
|
44
|
+
|
|
45
|
+
- **Defs are factories:** `f.tableRef` resolves its target guid eagerly at column-construction time, and the document table is referenced by chunks and queries. Minting defs per `createVector` / `registerVector` call eliminates cross-call identity contamination.
|
|
46
|
+
- **Idempotency WeakSet:** The SDK's duplicate-def guard compares def identity. Two `createVector` calls produce distinct objects sharing names, so `registerVector` keeps a `WeakSet<Xano>` to flag duplicate calls early with a clear diagnostic.
|
|
47
|
+
- **Literal Stack Tuples:** Function and query stacks must remain literal tuples (`readonly Statement[]`) or `statements(...)` helpers. Spreading an untyped `Statement[]` collapses the stack tuple and widens `InferResponse` to `StackTupleWidened`. `test/types.test.ts` guards this.
|
|
48
|
+
- **pgvector Cosine Search:** The HNSW index on `vector_chunk` uses `vector_cosine_ops`, and search evaluates `vector_cos_distance` sorted `asc`.
|
|
49
|
+
- **Module manifest:** `package.json` carries a `"xanosdk"` field (`register: "registerVector"`, `returns: "handle"`, `options: {}`) so `xanosdk marketplace install` / `xanosdk init --marketplace` wire the module into `xano/index.ts` and bind the returned handle. Keep `register` in sync with the export name, and keep `options: {}` only while every option stays optional.
|
|
50
|
+
- **Search is not owner-scoped:** with `authenticated`, document endpoints filter by `user_id`, but `searchVectorsFn` (and so `/search` and the agent tool) searches every chunk. The docs say so; change both together.
|
|
51
|
+
- **Peer Range:** `@xano/sdk` is a peer dependency (`>=1.0.0 <2.0.0`). Dev dependency is pinned exactly to `1.0.0`. The window is `>=<floor> <2.0.0`: the floor is the lowest SDK the module is tested against, the ceiling is the next major.
|
|
52
|
+
|
|
53
|
+
## The Golden-Bundle Contract
|
|
54
|
+
|
|
55
|
+
`test/fixtures/golden-bundle.json` is a byte-exact peer-drift tripwire. Any change to statement encoding or schema in `@xano/sdk` breaks `test/bundle.test.ts`.
|
|
56
|
+
|
|
57
|
+
Regenerating the fixture is a deliberate, reviewed action (`npm run fixture:regen && git diff test/fixtures/golden-bundle.json`).
|
|
58
|
+
|
|
59
|
+
## Docs
|
|
60
|
+
|
|
61
|
+
`README.md` and `llms.txt` ship in the tarball. When options, endpoints, or the SDK floor change, update both, and type-check any code snippet you change against the pinned SDK (the SDK's `agent()` / `workspaceConfig()` shapes have changed before). `test/published-docs.test.ts` checks the shipped file list and relative links, not content.
|
|
62
|
+
|
|
63
|
+
## Release
|
|
64
|
+
|
|
65
|
+
1. Bump `version` in `package.json` according to SemVer (versions start at 1.0.0 and only the patch number (1.0.x) increments for now, whatever the change; do not bump unless told) and merge it to `main`.
|
|
66
|
+
2. Run `npm test && npm run lint && npm run build`.
|
|
67
|
+
3. Verify `npm pack --dry-run` contains exactly the expected files.
|
|
68
|
+
4. Publish: `npm publish --access public`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Xano, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# @xano-sdk/vector
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@xano-sdk/vector)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
A vector embedding, document ingestion, chunking, and semantic search module for [Xano SDK](https://github.com/xanots/sdk). It uses Google Gemini Embeddings 2 (`gemini-embedding-2`) at **768 dimensions**, stores vectors in PostgreSQL `pgvector` with a cosine index, and ships an AI agent search tool.
|
|
7
|
+
|
|
8
|
+
Everything it exports is a typed Xano SDK def. Nothing runs inside this package; your workspace compiles the defs at `export()`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Gemini Embeddings 2**: text, images (PNG, JPEG, WebP), audio (WAV, MP3), and video (MP4), reduced to **768 dimensions** with Matryoshka Representation Learning (MRL).
|
|
15
|
+
- **Asymmetric retrieval**: documents are embedded as `RETRIEVAL_DOCUMENT` and queries as `RETRIEVAL_QUERY` by default.
|
|
16
|
+
- **Cross-modal search**: text-to-text, text-to-image, and image-to-image search by cosine similarity (`vector_cosine_ops`).
|
|
17
|
+
- **Chunking strategies**: `paragraph`, `sentence`, `markdown`, `fixed`, and `custom`, with configurable chunk size and overlap.
|
|
18
|
+
- **Document lifecycle**: `pending`, `indexing`, `indexed`, `failed`, with chunk storage and reindexing.
|
|
19
|
+
- **Agent tool**: a `vector_search` tool for Xano agents and MCP servers, with configurable citation style.
|
|
20
|
+
- **Typed client**: request and response types for every endpoint.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
With the Xano SDK CLI, which installs the package and wires it into `xano/index.ts`:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
xanosdk marketplace install @xano-sdk/vector
|
|
30
|
+
# or, for a new project:
|
|
31
|
+
xanosdk init my-app --marketplace @xano-sdk/vector
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or manually:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install @xano-sdk/vector @xano/sdk
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Requires `@xano/sdk` `>=1.0.0 <2.0.0`.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Quickstart
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
// xano/index.ts
|
|
48
|
+
import { workspace, workspaceConfig } from "@xano/sdk";
|
|
49
|
+
import { registerVector } from "@xano-sdk/vector";
|
|
50
|
+
|
|
51
|
+
const ws = workspace("my-app").registerWorkspace(
|
|
52
|
+
workspaceConfig({ name: "my-app", env: { GEMINI_API_KEY: "" } }),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export const vector = registerVector(ws, { defaultStrategy: "markdown" });
|
|
56
|
+
|
|
57
|
+
export default vector.xano;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Declare the env var name in source and put its value in `xano/.env`, which is gitignored:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# xano/.env
|
|
64
|
+
GEMINI_API_KEY=your-google-ai-studio-key
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Do not write `process.env.GEMINI_API_KEY` into `workspaceConfig`. It is read at export time, so the key's value ends up in the bundle.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Configuration Options
|
|
72
|
+
|
|
73
|
+
| Option | Type | Default | Description |
|
|
74
|
+
| :--- | :--- | :--- | :--- |
|
|
75
|
+
| `apiKeyEnv` | `string` | `"GEMINI_API_KEY"` | Name of the workspace env var that holds the Gemini API key. |
|
|
76
|
+
| `model` | `string` | `"gemini-embedding-2"` | Gemini embedding model id. |
|
|
77
|
+
| `taskTypeDocument` | `string` | `"RETRIEVAL_DOCUMENT"` | Gemini `taskType` used when embedding documents. |
|
|
78
|
+
| `taskTypeQuery` | `string` | `"RETRIEVAL_QUERY"` | Gemini `taskType` used when embedding search queries. |
|
|
79
|
+
| `defaultStrategy` | `ChunkStrategy` | `"paragraph"` | `fixed`, `paragraph`, `sentence`, `markdown`, or `custom`. |
|
|
80
|
+
| `defaultChunkSize` | `number` | `500` | Target characters per chunk (integer, 20 to 10000). |
|
|
81
|
+
| `defaultChunkOverlap` | `number` | `50` | Characters shared by consecutive chunks (integer, `>= 0` and `< defaultChunkSize`). |
|
|
82
|
+
| `searchLimit` | `number` | `10` | Default top-k for search (integer, 1 to 100). |
|
|
83
|
+
| `searchThreshold` | `number` | `0.0` | Default minimum cosine similarity (0.0 to 1.0). |
|
|
84
|
+
| `citationFormat` | `"markdown" \| "numeric" \| "none"` | `"markdown"` | How the agent tool tells the model to cite sources. |
|
|
85
|
+
| `authTable` | `TableDef \| string` | `undefined` | Auth table that owns documents. Required when `authenticated` is `true`. |
|
|
86
|
+
| `authenticated` | `boolean` | `true` if `authTable` is set, else `false` | Require a signed-in user on every endpoint and scope documents to their owner. |
|
|
87
|
+
| `userIdType` | `"int" \| "uuid"` | inferred from `authTable` | Type of the `user_id` column. |
|
|
88
|
+
| `routePrefix` | `string` | `"vector"` | Path prefix for the endpoints. |
|
|
89
|
+
| `canonical` | `string` | `undefined` | API group canonical (`/api:<canonical>`). Also replaces `routePrefix` and prefixes def names (`<canonical>_document`, `<canonical>/search_vectors`, …). |
|
|
90
|
+
| `names` | `VectorNames` | see `DEFAULT_NAMES` | Override individual table, function, tool, and API group names. |
|
|
91
|
+
| `tags` | `string[]` | `["vector", "ai", "search"]` | Tags on the API group. |
|
|
92
|
+
|
|
93
|
+
### Authentication
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
export const vector = registerVector(ws, { authTable: users, canonical: "kb" });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
When `authenticated` is on, every endpoint requires a signed-in user. Documents get a `user_id` column, and the list, get, delete, and reindex endpoints only return the caller's own documents.
|
|
100
|
+
|
|
101
|
+
`/search`, `/embed`, and the `vector_search` tool also require sign-in, but search runs across **all** indexed chunks, not only the caller's. Do not rely on it to keep one user's content away from another user.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## API Endpoints
|
|
106
|
+
|
|
107
|
+
The endpoints live in one API group, at `https://<your-instance>/api:<canonical>/<routePrefix>/...`. Without a `canonical` option, Xano assigns the group's canonical on deploy. With `canonical: "kb"`, the base is `/api:kb/kb/`.
|
|
108
|
+
|
|
109
|
+
| Verb | Path | Inputs | Returns |
|
|
110
|
+
| :--- | :--- | :--- | :--- |
|
|
111
|
+
| `POST` | `/documents/create` | `title`, `content?`, `media_data?`, `mime_type?`, `metadata?`, `strategy?`, `chunk_size?`, `chunk_overlap?` | `{ document, chunk_count, status }` |
|
|
112
|
+
| `GET` | `/documents` | `page?` (1), `per_page?` (20) | Paged `{ items, curPage, perPage, itemsReceived, itemsTotal, pageTotal }` |
|
|
113
|
+
| `GET` | `/documents/{id}` | `id` | `{ document, chunks }` |
|
|
114
|
+
| `DELETE` | `/documents/{id}/delete` | `id` | `{ deleted, id }`. Deletes the document's chunks too. |
|
|
115
|
+
| `POST` | `/documents/{id}/reindex` | `id`, `strategy?`, `chunk_size?`, `chunk_overlap?` | `{ document_id, status, chunk_count }` |
|
|
116
|
+
| `POST` | `/search` | `query?`, `query_media_data?`, `query_mime_type?`, `query_embedding?`, `limit?`, `threshold?` | `{ results, count }` |
|
|
117
|
+
| `POST` | `/embed` | `text?`, `media_data?`, `mime_type?`, `model?` | `{ embedding, dimensions }` |
|
|
118
|
+
|
|
119
|
+
`media_data` is base64. `mime_type` defaults to `text/plain`. Send the user's auth token as `Authorization: Bearer <token>` when `authenticated` is on.
|
|
120
|
+
|
|
121
|
+
The examples below use `const API = "https://<your-instance>/api:<canonical>/<routePrefix>";`.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Examples
|
|
126
|
+
|
|
127
|
+
### Ingest a text document
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
await fetch(`${API}/documents/create`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: { "Content-Type": "application/json" },
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
title: "System Architecture Guide",
|
|
135
|
+
content: "# System Architecture\nOur service runs on Kubernetes with PostgreSQL...",
|
|
136
|
+
mime_type: "text/markdown",
|
|
137
|
+
strategy: "markdown",
|
|
138
|
+
chunk_size: 400,
|
|
139
|
+
chunk_overlap: 40,
|
|
140
|
+
}),
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Ingest an image, audio, or video file
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
await fetch(`${API}/documents/create`, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: { "Content-Type": "application/json" },
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
title: "Product Diagram",
|
|
152
|
+
content: "Diagram illustrating cloud sync architecture.",
|
|
153
|
+
media_data: "<base64_image_data>",
|
|
154
|
+
mime_type: "image/png",
|
|
155
|
+
metadata: { category: "diagrams", width: 1024, height: 768 },
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Search
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
const res = await fetch(`${API}/search`, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: { "Content-Type": "application/json" },
|
|
166
|
+
body: JSON.stringify({ query: "architecture diagrams explaining cloud sync", limit: 5 }),
|
|
167
|
+
});
|
|
168
|
+
const { results, count } = await res.json();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Send `query_media_data` + `query_mime_type` to search with an image or audio clip, or `query_embedding` to search with a 768-dim vector you already have.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## AI Agent Search Tool
|
|
176
|
+
|
|
177
|
+
Pass `vector.searchTool` in an agent's or MCP server's `tools`:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { agent } from "@xano/sdk";
|
|
181
|
+
|
|
182
|
+
export const supportAgent = agent({
|
|
183
|
+
name: "support_agent",
|
|
184
|
+
llm: {
|
|
185
|
+
type: "google-genai",
|
|
186
|
+
model: "gemini-2.5-flash",
|
|
187
|
+
apiKey: "{{ $env.GEMINI_API_KEY }}",
|
|
188
|
+
systemPrompt: "Answer questions using the vector_search tool. Cite your sources.",
|
|
189
|
+
},
|
|
190
|
+
tools: [vector.searchTool],
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
ws.registerAgents([supportAgent]);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The tool takes `query`, optional `media_data` / `mime_type`, and `limit` (default 5).
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Building defs without registering
|
|
201
|
+
|
|
202
|
+
`createVector(options)` returns the same defs as `registerVector` without touching a workspace: `document`, `chunk`, `embedFn`, `chunkFn`, `ingestFn`, `searchFn`, `searchTool`, `group`, and `queries`. The individual factories (`documentTable`, `chunkTable`, `generateEmbeddingFn`, …) are exported too.
|
|
203
|
+
|
|
204
|
+
Call `registerVector` once per workspace; a second call throws. To run two pipelines in one workspace, register the second set yourself from `createVector` with a different `canonical` and `names.searchTool` (the tool name is not derived from `canonical`).
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT
|