Dr-Swopt пре 2 недеља
родитељ
комит
7b633f440f
2 измењених фајлова са 93 додато и 1 уклоњено
  1. 92 0
      src/FFB/ffb-vector.service.openai.txt
  2. 1 1
      src/FFB/ffb-vector.service.ts

+ 92 - 0
src/FFB/ffb-vector.service.openai.txt

@@ -0,0 +1,92 @@
+import { Injectable, OnModuleInit } from '@nestjs/common';
+import axios from 'axios';
+import { MongoCoreService } from 'src/mongo/mongo-core.service';
+import { FFBProductionRepository } from 'src/mongo/mongo-ffb-production.repository';
+import { FFBProduction } from './ffb-production.schema';
+
+@Injectable()
+export class FFBVectorService implements OnModuleInit {
+    private repo: FFBProductionRepository;
+    private readonly VECTOR_DIM = parseInt(process.env.VECTOR_DIM || '1536'); // OpenAI default
+    private accessToken: string;
+
+    constructor(private readonly mongoCore: MongoCoreService) { }
+
+    async onModuleInit() {
+        // Initialize Mongo repository
+        const db = await this.mongoCore.getDb();
+        this.repo = new FFBProductionRepository(db);
+        await this.repo.init();
+
+        // Load OpenAI API key
+        this.accessToken = process.env.OPENAI_API_KEY as unknown as string;
+        if (!this.accessToken) {
+            throw new Error('❌ Missing OPENAI_API_KEY in environment.');
+        }
+
+        console.log('✅ OpenAI embedding service ready. Repository initialized.');
+    }
+
+    /** Convert a record to a text string suitable for embedding */
+    private recordToText(record: FFBProduction): string {
+        return `Production on ${new Date(record.productionDate).toISOString()} at ${record.site} in ${record.phase} ${record.block} produced ${record.quantity} ${record.quantityUom} with a total weight of ${record.weight} ${record.weightUom}.`;
+    }
+
+    /** Generate embedding via OpenAI */
+    private async embedText(text: string): Promise<number[]> {
+        const model = process.env.EMBEDDING_MODEL || 'text-embedding-3-small';
+
+        try {
+            const response = await axios.post(
+                'https://api.openai.com/v1/embeddings',
+                { model, input: text },
+                {
+                    headers: {
+                        Authorization: `Bearer ${this.accessToken}`,
+                        'Content-Type': 'application/json',
+                    },
+                }
+            );
+
+            const embedding = response.data?.data?.[0]?.embedding;
+
+            if (!embedding || !Array.isArray(embedding)) {
+                throw new Error(`Invalid embedding returned: ${JSON.stringify(response.data)}`);
+            }
+
+            if (embedding.length !== this.VECTOR_DIM) {
+                console.warn(
+                    `⚠️ Warning: embedding dimension mismatch. Expected ${this.VECTOR_DIM}, got ${embedding.length}`
+                );
+            }
+
+            return embedding;
+        } catch (err: any) {
+            console.error('❌ Failed to generate OpenAI embedding:', err.response?.data || err.message);
+            throw err;
+        }
+    }
+
+    /** Insert a single record with embedding vector */
+    async insertWithVector(record: FFBProduction) {
+        const text = this.recordToText(record);
+        const vector = await this.embedText(text);
+
+        const data: FFBProduction & { vector: number[] } = { ...record, vector };
+        return this.repo.create(data);
+    }
+
+    /** Search for top-k similar records using a text query */
+    async vectorSearch(query: string, k = 5) {
+        if (!query) throw new Error('Query string cannot be empty');
+
+        const vector = await this.embedText(query);
+        const results = await this.repo.vectorSearch(vector, k, 50);
+
+        return results.map((r) => ({
+            ...r,
+            _id: r._id.toString(),
+            score: r.score,
+        }));
+    }
+}

+ 1 - 1
src/FFB/ffb-vector.service.ts

@@ -11,7 +11,7 @@ export class FFBVectorService implements OnModuleInit {
   private readonly VECTOR_DIM = parseInt(process.env.VECTOR_DIM || '3072'); // Gemini default
   private accessToken: string; // OAuth token
 
-  constructor(private readonly mongoCore: MongoCoreService) {}
+  constructor(private readonly mongoCore: MongoCoreService) { }
 
   async onModuleInit() {
     // Initialize Mongo repository