diff --git a/.env.example b/.env.example
index 59a6ca02..52264ab1 100644
--- a/.env.example
+++ b/.env.example
@@ -32,7 +32,10 @@ NEXTAUTH_SECRET="your-super-secret-key-change-in-production"
# DO_SPACES_SECRET_ACCESS_KEY=""
# DO_SPACES_CDN_ENDPOINT="" # Optional: for CDN-enabled Spaces
-# AI Search (optional - enable aiSearch in prompts.config.ts)
+# AI Features (optional - enable aiSearch/aiGeneration in prompts.config.ts)
# OPENAI_API_KEY=your_openai_api_key
+# OPENAI_BASE_URL=https://api.openai.com/v1 # Optional: custom base URL for OpenAI-compatible APIs
+# OPENAI_EMBEDDING_MODEL=text-embedding-3-small # Optional: embedding model for AI search
+# OPENAI_GENERATIVE_MODEL=gpt-4o-mini # Optional: generative model for AI generation
# GOOGLE_ANALYTICS_ID="G-XXXXXXXXX"
\ No newline at end of file
diff --git a/messages/ar.json b/messages/ar.json
index 08163baf..73e6c618 100644
--- a/messages/ar.json
+++ b/messages/ar.json
@@ -79,7 +79,10 @@
"sqlQuery": "استعلام SQL",
"openDataset": "فتح مجموعة البيانات",
"runQuery": "تشغيل الاستعلام على HF",
- "examples": "استعلامات نموذجية"
+ "examples": "أمثلة",
+ "selectExample": "اختر مثالاً...",
+ "aiGenerate": "توليد AI",
+ "aiPlaceholder": "صف استعلام SQL الذي تحتاجه..."
},
"edit": "تعديل الأمر",
"delete": "حذف الأمر",
@@ -509,7 +512,8 @@
"search": "بحث",
"clear": "مسح",
"found": "تم العثور على {count}",
- "aiSearch": "بحث الذكاء الاصطناعي"
+ "aiSearch": "بحث الذكاء الاصطناعي",
+ "searchTags": "البحث عن العلامات..."
},
"user": {
"profile": "الملف الشخصي",
diff --git a/messages/de.json b/messages/de.json
index 196a8e60..4cd6b2d9 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -79,7 +79,10 @@
"sqlQuery": "SQL-Abfrage",
"openDataset": "Datensatz öffnen",
"runQuery": "Abfrage auf HF ausführen",
- "examples": "Beispielabfragen"
+ "examples": "Beispiele",
+ "selectExample": "Beispiel auswählen...",
+ "aiGenerate": "KI Generieren",
+ "aiPlaceholder": "Beschreiben Sie die benötigte SQL-Abfrage..."
},
"edit": "Prompt bearbeiten",
"delete": "Prompt löschen",
@@ -509,7 +512,8 @@
"search": "Suchen",
"clear": "Löschen",
"found": "{count} gefunden",
- "aiSearch": "KI-Suche"
+ "aiSearch": "KI-Suche",
+ "searchTags": "Tags suchen..."
},
"user": {
"profile": "Profil",
diff --git a/messages/en.json b/messages/en.json
index 8ae45aa7..23d47906 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -79,7 +79,10 @@
"sqlQuery": "SQL Query",
"openDataset": "Open Dataset",
"runQuery": "Run Query on HF",
- "examples": "Example Queries"
+ "examples": "Examples",
+ "selectExample": "Select an example...",
+ "aiGenerate": "AI Generate",
+ "aiPlaceholder": "Describe the SQL query you need..."
},
"edit": "Edit Prompt",
"delete": "Delete Prompt",
@@ -509,7 +512,8 @@
"search": "Search",
"clear": "Clear",
"found": "{count} found",
- "aiSearch": "AI Search"
+ "aiSearch": "AI Search",
+ "searchTags": "Search tags..."
},
"user": {
"profile": "Profile",
diff --git a/messages/es.json b/messages/es.json
index d3a8a4ea..7f1a3d8a 100644
--- a/messages/es.json
+++ b/messages/es.json
@@ -79,7 +79,10 @@
"sqlQuery": "Consulta SQL",
"openDataset": "Abrir Dataset",
"runQuery": "Ejecutar Consulta en HF",
- "examples": "Consultas de Ejemplo"
+ "examples": "Ejemplos",
+ "selectExample": "Selecciona un ejemplo...",
+ "aiGenerate": "Generar con IA",
+ "aiPlaceholder": "Describe la consulta SQL que necesitas..."
},
"edit": "Editar Prompt",
"delete": "Eliminar Prompt",
@@ -509,7 +512,8 @@
"search": "Buscar",
"clear": "Limpiar",
"found": "{count} encontrados",
- "aiSearch": "Búsqueda IA"
+ "aiSearch": "Búsqueda IA",
+ "searchTags": "Buscar etiquetas..."
},
"user": {
"profile": "Perfil",
diff --git a/messages/fr.json b/messages/fr.json
index 8084755d..3b90570b 100644
--- a/messages/fr.json
+++ b/messages/fr.json
@@ -79,7 +79,10 @@
"sqlQuery": "Requête SQL",
"openDataset": "Ouvrir le Dataset",
"runQuery": "Exécuter la Requête sur HF",
- "examples": "Exemples de Requêtes"
+ "examples": "Exemples",
+ "selectExample": "Sélectionner un exemple...",
+ "aiGenerate": "Générer IA",
+ "aiPlaceholder": "Décrivez la requête SQL dont vous avez besoin..."
},
"edit": "Modifier le Prompt",
"delete": "Supprimer le Prompt",
@@ -509,7 +512,8 @@
"search": "Rechercher",
"clear": "Effacer",
"found": "{count} trouvés",
- "aiSearch": "Recherche IA"
+ "aiSearch": "Recherche IA",
+ "searchTags": "Rechercher des tags..."
},
"user": {
"profile": "Profil",
diff --git a/messages/it.json b/messages/it.json
index bff8c48e..3d85763f 100644
--- a/messages/it.json
+++ b/messages/it.json
@@ -79,7 +79,10 @@
"sqlQuery": "Query SQL",
"openDataset": "Apri Dataset",
"runQuery": "Esegui Query su HF",
- "examples": "Query di Esempio"
+ "examples": "Esempi",
+ "selectExample": "Seleziona un esempio...",
+ "aiGenerate": "Genera AI",
+ "aiPlaceholder": "Descrivi la query SQL di cui hai bisogno..."
},
"edit": "Modifica Prompt",
"delete": "Elimina Prompt",
@@ -509,7 +512,8 @@
"search": "Cerca",
"clear": "Cancella",
"found": "{count} trovati",
- "aiSearch": "Ricerca AI"
+ "aiSearch": "Ricerca AI",
+ "searchTags": "Cerca tag..."
},
"user": {
"profile": "Profilo",
diff --git a/messages/ja.json b/messages/ja.json
index 1eb71aba..b5721753 100644
--- a/messages/ja.json
+++ b/messages/ja.json
@@ -79,7 +79,10 @@
"sqlQuery": "SQLクエリ",
"openDataset": "データセットを開く",
"runQuery": "HFでクエリを実行",
- "examples": "クエリ例"
+ "examples": "例",
+ "selectExample": "例を選択...",
+ "aiGenerate": "AI生成",
+ "aiPlaceholder": "必要なSQLクエリを説明してください..."
},
"edit": "プロンプトを編集",
"delete": "プロンプトを削除",
@@ -509,7 +512,8 @@
"search": "検索",
"clear": "クリア",
"found": "{count}件見つかりました",
- "aiSearch": "AI検索"
+ "aiSearch": "AI検索",
+ "searchTags": "タグを検索..."
},
"user": {
"profile": "プロフィール",
diff --git a/messages/ko.json b/messages/ko.json
index 831031c5..bd6562ef 100644
--- a/messages/ko.json
+++ b/messages/ko.json
@@ -79,7 +79,10 @@
"sqlQuery": "SQL 쿼리",
"openDataset": "데이터셋 열기",
"runQuery": "HF에서 쿼리 실행",
- "examples": "예제 쿼리"
+ "examples": "예제",
+ "selectExample": "예제 선택...",
+ "aiGenerate": "AI 생성",
+ "aiPlaceholder": "필요한 SQL 쿼리를 설명하세요..."
},
"edit": "프롬프트 편집",
"delete": "프롬프트 삭제",
@@ -509,7 +512,8 @@
"search": "검색",
"clear": "지우기",
"found": "{count}개 찾음",
- "aiSearch": "AI 검색"
+ "aiSearch": "AI 검색",
+ "searchTags": "태그 검색..."
},
"user": {
"profile": "프로필",
diff --git a/messages/pt.json b/messages/pt.json
index 2dc3300c..d93ccca1 100644
--- a/messages/pt.json
+++ b/messages/pt.json
@@ -79,7 +79,10 @@
"sqlQuery": "Consulta SQL",
"openDataset": "Abrir Dataset",
"runQuery": "Executar Consulta no HF",
- "examples": "Consultas de Exemplo"
+ "examples": "Exemplos",
+ "selectExample": "Selecione um exemplo...",
+ "aiGenerate": "Gerar com IA",
+ "aiPlaceholder": "Descreva a consulta SQL que você precisa..."
},
"edit": "Editar Prompt",
"delete": "Excluir Prompt",
@@ -509,7 +512,8 @@
"search": "Buscar",
"clear": "Limpar",
"found": "{count} encontrados",
- "aiSearch": "Busca por IA"
+ "aiSearch": "Busca por IA",
+ "searchTags": "Pesquisar tags..."
},
"user": {
"profile": "Perfil",
diff --git a/messages/tr.json b/messages/tr.json
index 9a665047..a4f34970 100644
--- a/messages/tr.json
+++ b/messages/tr.json
@@ -79,7 +79,10 @@
"sqlQuery": "SQL Sorgusu",
"openDataset": "Veri Setini Aç",
"runQuery": "HF'de Sorguyu Çalıştır",
- "examples": "Örnek Sorgular"
+ "examples": "Örnekler",
+ "selectExample": "Bir örnek seçin...",
+ "aiGenerate": "AI Oluştur",
+ "aiPlaceholder": "Nasıl bir prompt bulmak istiyorsunuz?"
},
"edit": "Prompt Düzenle",
"delete": "Prompt Sil",
@@ -509,7 +512,8 @@
"search": "Ara",
"clear": "Temizle",
"found": "{count} bulundu",
- "aiSearch": "AI ile Ara"
+ "aiSearch": "AI ile Ara",
+ "searchTags": "Etiket ara..."
},
"user": {
"profile": "Profil",
diff --git a/messages/zh.json b/messages/zh.json
index 73a277c2..89a015d8 100644
--- a/messages/zh.json
+++ b/messages/zh.json
@@ -79,7 +79,10 @@
"sqlQuery": "SQL 查询",
"openDataset": "打开数据集",
"runQuery": "在HF上运行查询",
- "examples": "示例查询"
+ "examples": "示例",
+ "selectExample": "选择一个示例...",
+ "aiGenerate": "AI生成",
+ "aiPlaceholder": "描述您需要的SQL查询..."
},
"edit": "编辑提示词",
"delete": "删除提示词",
@@ -509,7 +512,8 @@
"search": "搜索",
"clear": "清除",
"found": "找到 {count} 个",
- "aiSearch": "AI 搜索"
+ "aiSearch": "AI 搜索",
+ "searchTags": "搜索标签..."
},
"user": {
"profile": "个人资料",
diff --git a/prompts.config.ts b/prompts.config.ts
index 32fa354c..a3e77d09 100644
--- a/prompts.config.ts
+++ b/prompts.config.ts
@@ -59,7 +59,9 @@ export default defineConfig({
// Enable tags
tags: true,
// Enable AI-powered semantic search (requires OPENAI_API_KEY)
- aiSearch: false,
+ aiSearch: true,
+ // Enable AI-powered generation features (requires OPENAI_API_KEY)
+ aiGeneration: true,
},
// Homepage customization
diff --git a/src/app/api/generate/sql/route.ts b/src/app/api/generate/sql/route.ts
new file mode 100644
index 00000000..89ec0544
--- /dev/null
+++ b/src/app/api/generate/sql/route.ts
@@ -0,0 +1,34 @@
+import { NextRequest, NextResponse } from "next/server";
+import { generateSQL, isAIGenerationEnabled } from "@/lib/ai/generation";
+
+export async function POST(request: NextRequest) {
+ try {
+ const enabled = await isAIGenerationEnabled();
+ if (!enabled) {
+ return NextResponse.json(
+ { error: "AI Generation is not enabled" },
+ { status: 400 }
+ );
+ }
+
+ const body = await request.json();
+ const { prompt } = body;
+
+ if (!prompt || prompt.trim().length === 0) {
+ return NextResponse.json(
+ { error: "Prompt is required" },
+ { status: 400 }
+ );
+ }
+
+ const sql = await generateSQL(prompt);
+
+ return NextResponse.json({ sql });
+ } catch (error) {
+ console.error("SQL Generation error:", error);
+ return NextResponse.json(
+ { error: "Failed to generate SQL" },
+ { status: 500 }
+ );
+ }
+}
diff --git a/src/app/prompts/loading.tsx b/src/app/prompts/loading.tsx
index bf8d607e..ddc575a9 100644
--- a/src/app/prompts/loading.tsx
+++ b/src/app/prompts/loading.tsx
@@ -1,37 +1,77 @@
import { Skeleton } from "@/components/ui/skeleton";
+function PromptCardSkeleton() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
export default function PromptsLoading() {
return (
{/* Header */}
-
-
-
-
+
- {/* Filters */}
-
-
-
-
-
-
- {/* Prompt Grid */}
-
- {Array.from({ length: 6 }).map((_, i) => (
-
-
-
-
-
-
-
+
+ {/* Sidebar Filters Skeleton */}
+
+
+ {/* Main Content - Prompt Grid Skeleton */}
+
+
+ {Array.from({ length: 12 }).map((_, i) => (
+
+ ))}
+
+
);
diff --git a/src/app/prompts/page.tsx b/src/app/prompts/page.tsx
index 65a3df55..7168b40e 100644
--- a/src/app/prompts/page.tsx
+++ b/src/app/prompts/page.tsx
@@ -5,9 +5,11 @@ import { Plus } from "lucide-react";
import { Button } from "@/components/ui/button";
import { InfinitePromptList } from "@/components/prompts/infinite-prompt-list";
import { PromptFilters } from "@/components/prompts/prompt-filters";
+import { FilterProvider } from "@/components/prompts/filter-context";
import { HFDataStudioDropdown } from "@/components/prompts/hf-data-studio-dropdown";
import { db } from "@/lib/db";
import { isAISearchEnabled, semanticSearch } from "@/lib/ai/embeddings";
+import { isAIGenerationEnabled } from "@/lib/ai/generation";
import config from "@/../prompts.config";
export const metadata: Metadata = {
@@ -34,6 +36,7 @@ export default async function PromptsPage({ searchParams }: PromptsPageProps) {
const perPage = 12;
const aiSearchAvailable = await isAISearchEnabled();
+ const aiGenerationAvailable = await isAIGenerationEnabled();
const useAISearch = aiSearchAvailable && params.ai === "1" && params.q;
let prompts: any[] = [];
@@ -177,7 +180,7 @@ export default async function PromptsPage({ searchParams }: PromptsPageProps) {
{!config.homepage?.useCloneBranding && (
-
+
)}
-
+
+
+
);
}
diff --git a/src/components/prompts/filter-context.tsx b/src/components/prompts/filter-context.tsx
new file mode 100644
index 00000000..c5e41d53
--- /dev/null
+++ b/src/components/prompts/filter-context.tsx
@@ -0,0 +1,28 @@
+"use client";
+
+import { createContext, useContext, useState, ReactNode } from "react";
+
+interface FilterContextType {
+ isFilterPending: boolean;
+ setFilterPending: (pending: boolean) => void;
+}
+
+const FilterContext = createContext
(null);
+
+export function FilterProvider({ children }: { children: ReactNode }) {
+ const [isFilterPending, setFilterPending] = useState(false);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useFilterContext() {
+ const context = useContext(FilterContext);
+ if (!context) {
+ throw new Error("useFilterContext must be used within FilterProvider");
+ }
+ return context;
+}
diff --git a/src/components/prompts/hf-data-studio-dropdown.tsx b/src/components/prompts/hf-data-studio-dropdown.tsx
index 2efa740a..76ab579c 100644
--- a/src/components/prompts/hf-data-studio-dropdown.tsx
+++ b/src/components/prompts/hf-data-studio-dropdown.tsx
@@ -3,9 +3,11 @@
import { useState } from "react";
import { useTranslations } from "next-intl";
import { useTheme } from "next-themes";
-import { ChevronDown, Play, ExternalLink } from "lucide-react";
+import { ChevronDown, Play, ExternalLink, Sparkles, Loader2 } from "lucide-react";
import Editor from "@monaco-editor/react";
import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Popover,
PopoverContent,
@@ -24,10 +26,16 @@ const DEFAULT_SQL = SQL_EXAMPLES[0].sql;
const HF_DATASET_URL = "https://huggingface.co/datasets/fka/awesome-chatgpt-prompts/viewer";
-export function HFDataStudioDropdown() {
+interface HFDataStudioDropdownProps {
+ aiGenerationEnabled?: boolean;
+}
+
+export function HFDataStudioDropdown({ aiGenerationEnabled = false }: HFDataStudioDropdownProps) {
const t = useTranslations("prompts.hfDataStudio");
const { resolvedTheme } = useTheme();
const [sql, setSql] = useState(DEFAULT_SQL);
+ const [aiPrompt, setAiPrompt] = useState("");
+ const [isGenerating, setIsGenerating] = useState(false);
const handleOpenDataset = () => {
window.open(HF_DATASET_URL, "_blank");
@@ -39,6 +47,28 @@ export function HFDataStudioDropdown() {
window.open(url, "_blank");
};
+ const handleGenerateSQL = async () => {
+ if (!aiPrompt.trim() || isGenerating) return;
+
+ setIsGenerating(true);
+ try {
+ const response = await fetch("/api/generate/sql", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ prompt: aiPrompt }),
+ });
+
+ if (!response.ok) throw new Error("Failed to generate SQL");
+
+ const data = await response.json();
+ setSql(data.sql);
+ } catch (error) {
+ console.error("SQL generation error:", error);
+ } finally {
+ setIsGenerating(false);
+ }
+ };
+
return (