Jrine commited on
Commit
1257299
·
1 Parent(s): a01cf9a

second base

Browse files
src/app/api/embeddings/route.ts ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextResponse } from 'next/server'
2
+ import { generateEmbedding } from '@/lib/huggingface'
3
+ import { createServerSupabaseClient } from '@/lib/supabase-server'
4
+
5
+ export async function POST(request: Request) {
6
+ try {
7
+ const { content, title, metadata } = await request.json()
8
+
9
+ if (!content) {
10
+ return NextResponse.json(
11
+ { error: 'Content is required' },
12
+ { status: 400 }
13
+ )
14
+ }
15
+
16
+ // Generate embedding using Hugging Face
17
+ const embedding = await generateEmbedding(content)
18
+
19
+ // Store in Supabase with embedding
20
+ const supabase = await createServerSupabaseClient()
21
+ const { data, error } = await supabase
22
+ .from('documents')
23
+ .insert({
24
+ content,
25
+ title: title || null,
26
+ embedding,
27
+ metadata: metadata || {},
28
+ })
29
+ .select()
30
+ .single()
31
+
32
+ if (error) {
33
+ console.error('Supabase error:', error)
34
+ throw error
35
+ }
36
+
37
+ return NextResponse.json({
38
+ success: true,
39
+ document: data
40
+ }, { status: 200 })
41
+
42
+ } catch (error: unknown) {
43
+ console.error("Embedding API Error:", error);
44
+
45
+ const message =
46
+ error instanceof Error ? error.message : "Internal server error";
47
+
48
+ return NextResponse.json(
49
+ { error: message },
50
+ { status: 500 }
51
+ );
52
+ }
53
+
54
+ }
src/app/api/search/route.ts ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextResponse } from 'next/server'
2
+ import { generateEmbedding } from '@/lib/huggingface'
3
+ import { createServerSupabaseClient } from '@/lib/supabase-server'
4
+ import type { VectorSearchRequest, VectorSearchResult } from '@/types'
5
+
6
+ export async function POST(request: Request) {
7
+ try {
8
+ const body: VectorSearchRequest = await request.json()
9
+ const {
10
+ query,
11
+ match_threshold = 0.5,
12
+ match_count = 5
13
+ } = body
14
+
15
+ if (!query) {
16
+ return NextResponse.json(
17
+ { error: 'Query is required' },
18
+ { status: 400 }
19
+ )
20
+ }
21
+
22
+ console.log('Generating embedding for query:', query)
23
+ const queryEmbedding = await generateEmbedding(query)
24
+ console.log('Embedding generated, length:', queryEmbedding.length)
25
+
26
+ const supabase = await createServerSupabaseClient()
27
+
28
+ console.log('Calling match_documents RPC...')
29
+ const { data, error } = await supabase.rpc('match_documents', {
30
+ query_embedding: queryEmbedding,
31
+ match_threshold,
32
+ match_count,
33
+ })
34
+
35
+ if (error) {
36
+ console.error('Search error:', error)
37
+ throw error
38
+ }
39
+
40
+ console.log('Search results:', data)
41
+ return NextResponse.json({
42
+ results: data as VectorSearchResult[]
43
+ }, { status: 200 })
44
+
45
+ } catch (error: unknown) {
46
+ console.error("Search API Error:", error);
47
+
48
+ const message =
49
+ error instanceof Error ? error.message : "Internal server error";
50
+
51
+ return NextResponse.json(
52
+ { error: message },
53
+ { status: 500 }
54
+ );
55
+ }
56
+
57
+ }
src/lib/huggingface.ts CHANGED
@@ -1,53 +1,63 @@
1
- import { HfInference } from '@huggingface/inference'
2
-
3
- // Initialize Hugging Face client
4
- const hf = new HfInference(process.env.HUGGING_FACE_API_TOKEN)
5
-
6
- export async function runInference(
7
- modelId: string,
8
- inputs: any,
9
- parameters?: Record<string, any>
10
- ) {
11
- try {
12
- const response = await hf.textGeneration({
13
- model: modelId,
14
- inputs: inputs,
15
- parameters: {
16
- max_new_tokens: 250,
17
- temperature: 0.7,
18
- ...parameters,
19
- },
20
- })
21
-
22
- return response
23
- } catch (error) {
24
- console.error('Hugging Face Inference Error:', error)
25
- throw error
26
- }
27
- }
28
-
29
- // For custom inference endpoints
30
- export async function runCustomEndpoint(
31
- endpointUrl: string,
32
- inputs: any
33
- ) {
34
- try {
35
- const response = await fetch(endpointUrl, {
36
- method: 'POST',
37
- headers: {
38
- 'Authorization': `Bearer ${process.env.HUGGING_FACE_API_TOKEN}`,
39
- 'Content-Type': 'application/json',
40
- },
41
- body: JSON.stringify({ inputs }),
42
- })
43
-
44
- if (!response.ok) {
45
- throw new Error(`Endpoint error: ${response.statusText}`)
46
- }
47
-
48
- return await response.json()
49
- } catch (error) {
50
- console.error('Custom Endpoint Error:', error)
51
- throw error
52
- }
53
- }
 
 
 
 
 
 
 
 
 
 
 
1
+ import { InferenceClient } from "@huggingface/inference";
2
+
3
+ const apiKey = process.env.HUGGING_FACE_API_TOKEN;
4
+
5
+ if (!apiKey) {
6
+ throw new Error("Missing HUGGING_FACE_API_TOKEN environment variable.");
7
+ }
8
+
9
+ const hf = new InferenceClient(apiKey);
10
+
11
+ // Generate text embeddings
12
+ export async function generateEmbedding(text: string): Promise<number[]> {
13
+ try {
14
+ const raw = await hf.featureExtraction({
15
+ model: "sentence-transformers/all-MiniLM-L6-v2",
16
+ inputs: text,
17
+ });
18
+
19
+ // Normalize HuggingFace outputs into flat number[]
20
+ if (typeof raw === "number") {
21
+ return [raw];
22
+ }
23
+
24
+ if (Array.isArray(raw)) {
25
+ // raw could be number[] OR number[][]
26
+ if (typeof raw[0] === "number") {
27
+ return raw as number[];
28
+ }
29
+
30
+ // number[][] flatten
31
+ return (raw as number[][]).flat();
32
+ }
33
+
34
+ throw new Error("Unexpected embedding format from HuggingFace.");
35
+ } catch (error: unknown) {
36
+ console.error("Embedding generation error:", error);
37
+ throw error;
38
+ }
39
+ }
40
+
41
+ // Text generation
42
+ export async function runInference(
43
+ modelId: string,
44
+ inputs: string,
45
+ parameters?: Record<string, unknown>,
46
+ ) {
47
+ try {
48
+ const response = await hf.textGeneration({
49
+ model: modelId,
50
+ inputs,
51
+ parameters: {
52
+ max_new_tokens: 250,
53
+ temperature: 0.7,
54
+ ...parameters,
55
+ },
56
+ });
57
+
58
+ return response;
59
+ } catch (error: unknown) {
60
+ console.error("Hugging Face Inference Error:", error);
61
+ throw error;
62
+ }
63
+ }
src/lib/supabase.ts CHANGED
@@ -1,8 +1,28 @@
1
- import { createBrowserClient } from '@supabase/ssr'
 
2
 
3
- export function createClient() {
4
- return createBrowserClient(
 
 
5
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
6
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  )
8
  }
 
1
+ import { createServerClient } from '@supabase/ssr'
2
+ import { cookies } from 'next/headers'
3
 
4
+ export async function createServerSupabaseClient() {
5
+ const cookieStore = await cookies()
6
+
7
+ return createServerClient(
8
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
9
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
10
+ {
11
+ db: {
12
+ schema: 'public', // ← Explicitly set schema
13
+ },
14
+ cookies: {
15
+ getAll() {
16
+ return cookieStore.getAll()
17
+ },
18
+ setAll(cookiesToSet) {
19
+ try {
20
+ cookiesToSet.forEach(({ name, value, options }) =>
21
+ cookieStore.set(name, value, options)
22
+ )
23
+ } catch {}
24
+ },
25
+ },
26
+ }
27
  )
28
  }
src/types/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Database types
2
  export interface ModelInteraction {
3
  id: string;
4
  user_input: string;
@@ -7,25 +7,40 @@ export interface ModelInteraction {
7
  created_at: string;
8
  }
9
 
10
- // API request/response types
11
  export interface InferenceRequest {
12
  model: string;
13
- inputs: string | any;
14
- parameters?: Record<string, any>;
15
  }
16
 
17
- export interface InferenceResponse {
18
- result: any;
 
19
  data?: ModelInteraction;
20
  }
21
 
22
- // Hugging Face types
23
- export interface HFModelConfig {
24
- modelId: string;
25
- endpoint?: string;
26
- parameters?: {
27
- max_new_tokens?: number;
28
- temperature?: number;
29
- top_p?: number;
30
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
 
1
+ // Existing types
2
  export interface ModelInteraction {
3
  id: string;
4
  user_input: string;
 
7
  created_at: string;
8
  }
9
 
10
+ // More precise: inputs should be text for text-generation
11
  export interface InferenceRequest {
12
  model: string;
13
+ inputs: string;
14
+ parameters?: Record<string, unknown>;
15
  }
16
 
17
+ // Model responses are usually structured JSON
18
+ export interface InferenceResponse<T = unknown> {
19
+ result: T;
20
  data?: ModelInteraction;
21
  }
22
 
23
+ // NEW: Vector search types
24
+ export interface Document {
25
+ id: string;
26
+ content: string;
27
+ title?: string;
28
+ embedding?: number[];
29
+ metadata?: Record<string, unknown>;
30
+ created_at: string;
31
+ updated_at?: string;
32
+ }
33
+
34
+ export interface VectorSearchRequest {
35
+ query: string;
36
+ match_threshold?: number;
37
+ match_count?: number;
38
+ }
39
+
40
+ export interface VectorSearchResult {
41
+ id: string;
42
+ content: string;
43
+ title?: string;
44
+ metadata?: Record<string, unknown>;
45
+ similarity: number;
46
  }
test-vector.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ async function testAddDocument() {
2
+ const response = await fetch('http://localhost:3000/api/embeddings', {
3
+ method: 'POST',
4
+ headers: { 'Content-Type': 'application/json' },
5
+ body: JSON.stringify({
6
+ content: 'Next.js is a React framework for building web applications.',
7
+ title: 'Next.js Introduction',
8
+ metadata: { category: 'web development' }
9
+ })
10
+ })
11
+
12
+ const result = await response.json()
13
+ console.log('Document added:', result)
14
+ }
15
+
16
+ async function testSearch() {
17
+ const response = await fetch('http://localhost:3000/api/search', {
18
+ method: 'POST',
19
+ headers: { 'Content-Type': 'application/json' },
20
+ body: JSON.stringify({
21
+ query: 'What is Next.js?',
22
+ match_threshold: 0.5,
23
+ match_count: 3
24
+ })
25
+ })
26
+
27
+ const result = await response.json()
28
+ console.log('Search results:', result)
29
+ }
30
+
31
+ // Run tests
32
+ testAddDocument()
33
+ .then(() => testSearch())
34
+ .catch(console.error)