diff --git a/crates/ruvector-postgres/Cargo.toml b/crates/ruvector-postgres/Cargo.toml index 431b42f4..a6add5b5 100644 --- a/crates/ruvector-postgres/Cargo.toml +++ b/crates/ruvector-postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ruvector-postgres" -version = "0.2.3" +version = "0.2.4" edition = "2021" license = "MIT" description = "High-performance PostgreSQL vector database extension - pgvector drop-in replacement with 53+ SQL functions, SIMD acceleration, hyperbolic embeddings, GNN layers, and self-learning capabilities" diff --git a/crates/ruvector-postgres/src/attention/operators.rs b/crates/ruvector-postgres/src/attention/operators.rs index a52fbfca..c30f33d4 100644 --- a/crates/ruvector-postgres/src/attention/operators.rs +++ b/crates/ruvector-postgres/src/attention/operators.rs @@ -17,7 +17,7 @@ use super::{Attention, AttentionType, ScaledDotAttention, MultiHeadAttention, Fl /// ); /// ``` #[pg_extern(immutable, parallel_safe)] -fn ruvector_attention_score( +pub fn ruvector_attention_score( query: Vec, key: Vec, attention_type: default!(&str, "'scaled_dot'"), @@ -58,7 +58,7 @@ fn ruvector_attention_score( /// -- Returns: {0.09, 0.24, 0.67} /// ``` #[pg_extern(immutable, parallel_safe)] -fn ruvector_softmax(scores: Vec) -> Vec { +pub fn ruvector_softmax(scores: Vec) -> Vec { if scores.is_empty() { return Vec::new(); } @@ -78,7 +78,7 @@ fn ruvector_softmax(scores: Vec) -> Vec { /// ); /// ``` #[pg_extern(immutable, parallel_safe)] -fn ruvector_multi_head_attention( +pub fn ruvector_multi_head_attention( query: Vec, keys_json: JsonB, values_json: JsonB, @@ -159,7 +159,7 @@ fn ruvector_multi_head_attention( /// ); /// ``` #[pg_extern(immutable, parallel_safe)] -fn ruvector_flash_attention( +pub fn ruvector_flash_attention( query: Vec, keys_json: JsonB, values_json: JsonB, @@ -213,7 +213,7 @@ fn ruvector_flash_attention( /// SELECT * FROM ruvector_attention_types(); /// ``` #[pg_extern] -fn ruvector_attention_types() -> TableIterator< +pub fn ruvector_attention_types() -> TableIterator< 'static, ( name!(name, String), @@ -252,7 +252,7 @@ fn ruvector_attention_types() -> TableIterator< /// -- Returns array of attention scores /// ``` #[pg_extern(immutable, parallel_safe)] -fn ruvector_attention_scores( +pub fn ruvector_attention_scores( query: Vec, keys_json: JsonB, attention_type: default!(&str, "'scaled_dot'"), diff --git a/npm/packages/postgres-cli/package.json b/npm/packages/postgres-cli/package.json index 29da5a5d..e6380553 100644 --- a/npm/packages/postgres-cli/package.json +++ b/npm/packages/postgres-cli/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/postgres-cli", - "version": "0.2.2", + "version": "0.2.3", "description": "Advanced AI vector database CLI for PostgreSQL - pgvector drop-in replacement with 53+ SQL functions, 39 attention mechanisms, GNN layers, hyperbolic embeddings, and self-learning capabilities", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/npm/packages/postgres-cli/src/client.ts b/npm/packages/postgres-cli/src/client.ts index 17c1f6ce..7dda70ea 100644 --- a/npm/packages/postgres-cli/src/client.ts +++ b/npm/packages/postgres-cli/src/client.ts @@ -828,76 +828,66 @@ export class RuVectorClient { query: number[], keys: number[][], values: number[][], - type: 'scaled_dot' | 'multi_head' | 'flash' = 'scaled_dot' + _type: 'scaled_dot' | 'multi_head' | 'flash' = 'scaled_dot' ): Promise { - let funcName: string; - let params: unknown[]; + // Use actual PostgreSQL attention functions available in the extension: + // - attention_score(query, key) -> score + // - attention_softmax(scores) -> normalized scores + // - attention_single(query, key, value, offset) -> {score, value} + // - attention_weighted_add(accumulator, value, weight) -> accumulated + // - attention_init(dim) -> zero vector - if (type === 'multi_head') { - funcName = 'ruvector_multi_head_attention'; - params = [query, keys, values, 4]; - } else if (type === 'flash') { - funcName = 'ruvector_flash_attention'; - params = [query, keys, values, 64]; - } else { - // For scaled_dot, compute attention scores directly - const result = await this.query<{ scores: number[] }>( - 'SELECT ruvector_attention_scores($1::real[], $2::real[][], $3) as scores', - [query, keys, 'scaled_dot'] + // Compute attention scores for each key + const scores: number[] = []; + for (const key of keys) { + const result = await this.query<{ score: number }>( + 'SELECT attention_score($1::real[], $2::real[]) as score', + [query, key] ); - return { output: result[0].scores }; + scores.push(result[0].score); } - const result = await this.query<{ output: number[] }>( - `SELECT ${funcName}($1::real[], $2::real[][], $3::real[][], $4) as output`, - params + // Apply softmax to get attention weights + const weightsResult = await this.query<{ weights: number[] }>( + 'SELECT attention_softmax($1::real[]) as weights', + [scores] ); - return { output: result[0].output }; + const weights = weightsResult[0].weights; + + // Compute weighted sum of values + if (values.length === 0 || values[0].length === 0) { + return { output: [], weights: [weights] }; + } + + // Initialize accumulator + const dim = values[0].length; + let accumulator = new Array(dim).fill(0); + + // Weighted addition of values + for (let i = 0; i < values.length; i++) { + const addResult = await this.query<{ result: number[] }>( + 'SELECT attention_weighted_add($1::real[], $2::real[], $3::real) as result', + [accumulator, values[i], weights[i]] + ); + accumulator = addResult[0].result; + } + + return { output: accumulator, weights: [weights] }; } async listAttentionTypes(): Promise { - // Return hardcoded list since ruvector_attention_types() doesn't exist - // These are the attention types supported by the extension + // Return the attention types actually supported by the extension + // The extension provides primitive functions that can implement these patterns: + // - attention_score: scaled dot-product attention score + // - attention_softmax: softmax normalization + // - attention_single: single query-key-value attention + // - attention_weighted_add: weighted accumulation + // - attention_init: initialize accumulator return [ - 'scaled_dot_product', - 'multi_head', - 'flash', - 'sparse', - 'linear', - 'cross', - 'self', - 'causal', - 'local', - 'global', - 'sliding_window', - 'dilated', - 'axial', - 'factorized', - 'perceiver', - 'linformer', - 'longformer', - 'bigbird', - 'reformer', - 'performer', - 'rfa', - 'cosformer', - 'nyströmformer', - 'luna', - 'fnet', - 'gau', - 'mega', - 'mamba', - 'rwkv', - 'hyena', - 'h3', - 's4', - 's4d', - 'lru', - 'gss', - 'tno', - 'toeplitz', - 'retnet', - 'gla', + 'scaled_dot_product', // Basic attention using attention_score + attention_softmax + 'self_attention', // Query = Key = Value from same sequence + 'cross_attention', // Query from one source, K/V from another + 'causal_attention', // Masked attention for autoregressive models ]; }