Skip to content

Commit 085d8d7

Browse files
committed
Add Three.js and related types for visualization component
1 parent 17ba5dd commit 085d8d7

File tree

5 files changed

+687
-0
lines changed

5 files changed

+687
-0
lines changed

app/api/embeddings/route.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { sql } from '@vercel/postgres';
2+
import { NextResponse } from 'next/server';
3+
4+
export async function GET() {
5+
try {
6+
console.log('Fetching embeddings data for visualization...');
7+
8+
// First, let's see how many total chunks we have
9+
const totalChunks = await sql`
10+
SELECT COUNT(*) as count FROM content_chunks WHERE embedding IS NOT NULL
11+
`;
12+
console.log(`Total chunks with embeddings: ${totalChunks.rows[0].count}`);
13+
14+
// Count unique articles
15+
const uniqueArticles = await sql`
16+
SELECT COUNT(DISTINCT post_slug) as count FROM content_chunks WHERE embedding IS NOT NULL
17+
`;
18+
console.log(`Unique articles with embeddings: ${uniqueArticles.rows[0].count}`);
19+
20+
// Simplified query - get one embedding per article
21+
const results = await sql`
22+
SELECT DISTINCT ON (post_slug)
23+
id,
24+
post_slug,
25+
post_title,
26+
content,
27+
chunk_type,
28+
metadata,
29+
sequence,
30+
embedding,
31+
created_at
32+
FROM content_chunks
33+
WHERE embedding IS NOT NULL
34+
ORDER BY
35+
post_slug,
36+
CASE WHEN chunk_type = 'full-post' THEN 0 ELSE 1 END,
37+
sequence
38+
`;
39+
40+
console.log(`Found ${results.rows.length} articles with embeddings`);
41+
42+
// Helper function to parse embedding
43+
const parseEmbedding = (embedding: any): number[] => {
44+
if (Array.isArray(embedding)) {
45+
return embedding;
46+
}
47+
48+
if (typeof embedding === 'string') {
49+
try {
50+
// Try to parse as JSON array
51+
const parsed = JSON.parse(embedding);
52+
if (Array.isArray(parsed)) {
53+
return parsed;
54+
}
55+
} catch (e) {
56+
// If not JSON, try to parse as PostgreSQL vector format
57+
// Remove brackets and split by comma
58+
const cleaned = embedding.replace(/[\[\]]/g, '');
59+
return cleaned.split(',').map(Number);
60+
}
61+
}
62+
63+
console.warn('Could not parse embedding:', typeof embedding, embedding);
64+
return [];
65+
};
66+
67+
// Process the data for visualization
68+
const processedData = results.rows.map((row, index) => {
69+
const parsedEmbedding = parseEmbedding(row.embedding);
70+
71+
return {
72+
id: row.id,
73+
postSlug: row.post_slug,
74+
postTitle: row.post_title,
75+
content: row.content,
76+
chunkType: row.chunk_type,
77+
metadata: row.metadata,
78+
sequence: row.sequence,
79+
embedding: parsedEmbedding,
80+
publishedDate: row.metadata?.published_date,
81+
tags: row.metadata?.tags || [],
82+
createdAt: row.created_at,
83+
index: index
84+
};
85+
}).filter(item => item.embedding.length > 0); // Filter out items with invalid embeddings
86+
87+
console.log(`Processed ${processedData.length} articles with valid embeddings`);
88+
89+
return NextResponse.json({
90+
success: true,
91+
data: processedData,
92+
count: processedData.length,
93+
debug: {
94+
totalChunks: totalChunks.rows[0].count,
95+
uniqueArticles: uniqueArticles.rows[0].count,
96+
returnedArticles: processedData.length
97+
}
98+
});
99+
100+
} catch (error) {
101+
console.error('Error fetching embeddings:', error);
102+
return NextResponse.json(
103+
{
104+
error: 'Failed to fetch embeddings data',
105+
details: error instanceof Error ? error.message : 'Unknown error'
106+
},
107+
{ status: 500 }
108+
);
109+
}
110+
}

app/visualization/page.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import EmbeddingsViz from '../../components/EmbeddingsViz';
2+
3+
export default function VisualizationPage() {
4+
return (
5+
<div>
6+
<h1 className="font-bold text-left mb-6 text-2xl hover:text-light-accent dark:hover:text-dark-accent transition-colors">
7+
Knowledge Constellation
8+
</h1>
9+
10+
<div className="mb-6">
11+
<p className="text-japanese-sumiiro dark:text-japanese-murasakisuishiyou text-sm mb-4 font-medium">
12+
Explore your articles as a beautiful constellation of stars. Each star represents an article,
13+
with brightness reflecting its depth and colors indicating topics. Related articles form
14+
subtle constellations across your knowledge universe.
15+
</p>
16+
</div>
17+
18+
<div className="h-[75vh] rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 bg-black shadow-sm">
19+
<EmbeddingsViz className="w-full h-full" />
20+
</div>
21+
</div>
22+
);
23+
}

0 commit comments

Comments
 (0)