@ouim/vectoriadb-server 0.1.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/demo.js ADDED
@@ -0,0 +1,23 @@
1
+ import VectoriaDBServer from './vectoriadb-server.js'
2
+ import { FileStorageAdapter } from 'vectoriadb'
3
+
4
+ async function main() {
5
+ const server = new VectoriaDBServer({
6
+ port: 3001,
7
+ vectoriadbConfig: {
8
+ storageAdapter: new FileStorageAdapter({ cacheDir: './.cache/vectoriadb', namespace: 'default' }),
9
+ },
10
+ })
11
+
12
+ await server.listen()
13
+ console.log('Demo server is running. Connect with the client demo in the client folder.')
14
+
15
+ // graceful shutdown
16
+ process.on('SIGINT', async () => {
17
+ console.log('Shutting down...')
18
+ await server.close()
19
+ process.exit(0)
20
+ })
21
+ }
22
+
23
+ main().catch(err => console.error(err))
package/index.js ADDED
@@ -0,0 +1,23 @@
1
+ import VectoriaDBServer from './vectoriadb-server.js'
2
+ import { FileStorageAdapter } from 'vectoriadb'
3
+
4
+ // If run directly, start a demo server
5
+ if (process.argv[1] && process.argv[1].endsWith('index.js')) {
6
+ const server = new VectoriaDBServer({
7
+ port: process.env.PORT ? Number(process.env.PORT) : 3001,
8
+ host: '0.0.0.0',
9
+ vectoriadbConfig: {
10
+ // default storage / model - user should override for production
11
+ storageAdapter: new FileStorageAdapter({ cacheDir: '/data/vectoriadb', namespace: 'default' }),
12
+ },
13
+ cors: ['http://localhost:3000'],
14
+ })
15
+
16
+ server.listen().catch(err => {
17
+ console.error(err)
18
+ process.exit(1)
19
+ })
20
+ }
21
+
22
+ export { VectoriaDBServer }
23
+ export default VectoriaDBServer
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@ouim/vectoriadb-server",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "node index.js",
8
+ "demo": "node demo.js",
9
+ "publish": "npm publish --access public"
10
+ },
11
+ "dependencies": {
12
+ "@huggingface/transformers": "^3.8.1",
13
+ "socket.io": "^4.8.0",
14
+ "tslib": "^2.8.1",
15
+ "vectoriadb": "^2.1.3"
16
+ },
17
+ "engines": {
18
+ "node": ">=18"
19
+ }
20
+ }
@@ -0,0 +1,177 @@
1
+ import http from 'http'
2
+ import { Server as IOServer } from 'socket.io'
3
+
4
+ // NOTE: server expects `vectoriadb` to be installed in the environment.
5
+ // It forwards calls to the real VectoriaDB instance.
6
+ import { VectoriaDB } from 'vectoriadb'
7
+
8
+ export default class VectoriaDBServer {
9
+ constructor(opts = {}) {
10
+ this.port = opts.port || 3001
11
+ this.host = opts.host || '0.0.0.0'
12
+ this.cors = opts.cors || []
13
+ this.apiKey = opts.apiKey || null
14
+ this.vectoriadbConfig = opts.vectoriadbConfig || {}
15
+ this.streamChunkSize = opts.streamChunkSize || 500
16
+
17
+ this._http = null
18
+ this._io = null
19
+ this._vectoria = null
20
+ this._sockets = new Set()
21
+ this._started = false
22
+ }
23
+
24
+ async listen() {
25
+ if (this._started) return
26
+ // Initialize VectoriaDB (loads models / storage as configured)
27
+ this._vectoria = new VectoriaDB(this.vectoriadbConfig)
28
+ if (typeof this._vectoria.initialize === 'function') {
29
+ await this._vectoria.initialize()
30
+ }
31
+
32
+ this._http = http.createServer((req, res) => {
33
+ res.writeHead(200, { 'Content-Type': 'text/plain' })
34
+ res.end('VectoriaDB Server')
35
+ })
36
+
37
+ this._io = new IOServer(this._http, {
38
+ cors: { origin: this.cors.length ? this.cors : '*', methods: ['GET', 'POST'] },
39
+ maxHttpBufferSize: 1e7,
40
+ })
41
+
42
+ const nsp = this._io.of('/vectoriadb')
43
+
44
+ nsp.use((socket, next) => {
45
+ // simple API key auth if configured
46
+ if (this.apiKey) {
47
+ const provided = socket.handshake.auth?.apiKey || socket.handshake.query?.apiKey
48
+ if (!provided || provided !== this.apiKey) {
49
+ return next(new Error('Unauthorized'))
50
+ }
51
+ }
52
+ next()
53
+ })
54
+
55
+ nsp.on('connection', socket => {
56
+ this._sockets.add(socket)
57
+ socket.on('disconnect', () => this._sockets.delete(socket))
58
+
59
+ socket.on('request', async payload => {
60
+ // payload: { id, method, params, collection, timestamp }
61
+ try {
62
+ await this._handleRequest(socket, payload)
63
+ } catch (err) {
64
+ socket.emit('response', { id: payload?.id ?? null, result: null, error: { message: err.message }, took: 0 })
65
+ }
66
+ })
67
+
68
+ // allow ping from client
69
+ socket.on('health', cb => cb && cb({ ok: true, ts: Date.now() }))
70
+ })
71
+
72
+ await new Promise((res, rej) => {
73
+ this._http.listen(this.port, this.host, err => (err ? rej(err) : res()))
74
+ })
75
+
76
+ this._started = true
77
+ console.log(`VectoriaDB Server listening on ${this.host}:${this.port}`)
78
+ return this
79
+ }
80
+
81
+ async _handleRequest(socket, payload) {
82
+ if (!payload || typeof payload !== 'object') {
83
+ return socket.emit('response', { id: null, result: null, error: { message: 'Invalid payload' }, took: 0 })
84
+ }
85
+
86
+ const { id, method, params = [] } = payload
87
+ const start = Date.now()
88
+
89
+ if (!method || typeof method !== 'string') {
90
+ return socket.emit('response', { id, result: null, error: { message: 'Missing method' }, took: Date.now() - start })
91
+ }
92
+
93
+ // Allow passing serialized functions from client: convert stringified functions back to real functions
94
+ const reparsedParams = params.map(p => {
95
+ if (p && typeof p === 'object') {
96
+ // detect serialized function marker
97
+ if (p.__isFnString && typeof p.fn === 'string') {
98
+ // create function from string - executed in server process (trusted usage only)
99
+ // eslint-disable-next-line no-new-func
100
+ const fn = new Function('return (' + p.fn + ')')()
101
+ return fn
102
+ }
103
+ // also support options.filter as a string directly
104
+ if (typeof p.filter === 'string' && p.filter.trim().startsWith('function')) {
105
+ // eslint-disable-next-line no-new-func
106
+ p.filter = new Function('return (' + p.filter + ')')()
107
+ }
108
+ }
109
+ return p
110
+ })
111
+
112
+ try {
113
+ // dispatch to underlying VectoriaDB instance
114
+ const fn = this._vectoria[method]
115
+ if (typeof fn !== 'function') {
116
+ throw new Error(`MethodNotFound: ${method}`)
117
+ }
118
+
119
+ // Run and capture result
120
+ const result = await Promise.race([
121
+ fn.apply(this._vectoria, reparsedParams),
122
+ new Promise((_, reject) => setTimeout(() => reject(new Error('ServerTimeout')), 30000)),
123
+ ])
124
+
125
+ const took = Date.now() - start
126
+
127
+ // streaming support for very large arrays
128
+ if (Array.isArray(result) && result.length > this.streamChunkSize) {
129
+ const total = result.length
130
+ const chunkSize = this.streamChunkSize
131
+ for (let i = 0; i < total; i += chunkSize) {
132
+ const chunk = result.slice(i, i + chunkSize)
133
+ socket.emit('response-chunk', { id, chunk, index: Math.floor(i / chunkSize), totalChunks: Math.ceil(total / chunkSize) })
134
+ }
135
+ // final response indicating stream finished
136
+ return socket.emit('response', { id, result: { streamed: true, count: total }, error: null, took })
137
+ }
138
+
139
+ socket.emit('response', { id, result, error: null, took })
140
+ } catch (err) {
141
+ const took = Date.now() - start
142
+ socket.emit('response', { id, result: null, error: { message: err.message, name: err.name }, took })
143
+ }
144
+ }
145
+
146
+ async close() {
147
+ if (!this._started) return
148
+ // disconnect sockets
149
+ for (const s of Array.from(this._sockets)) {
150
+ try {
151
+ s.disconnect(true)
152
+ } catch (e) {}
153
+ }
154
+ this._sockets.clear()
155
+
156
+ if (this._io) {
157
+ try {
158
+ await this._io.close()
159
+ } catch (e) {}
160
+ }
161
+
162
+ if (this._http) {
163
+ await new Promise(resolve => this._http.close(resolve))
164
+ }
165
+
166
+ // attempt to persist state if available
167
+ try {
168
+ if (this._vectoria && typeof this._vectoria.saveToStorage === 'function') {
169
+ await this._vectoria.saveToStorage()
170
+ }
171
+ } catch (err) {
172
+ console.warn('Error saving VectoriaDB state during shutdown:', err.message)
173
+ }
174
+
175
+ this._started = false
176
+ }
177
+ }