Iilm Faculty Connect Gemini Rules — Free Gemini Rules…
    Neura MarketNeura Market/Gemini
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityDeepSeekDeepSeek
    CoPilotCoPilotStable DiffusionStable DiffusionMidjourneyMidjourney
    View All Directories
    OverviewRulesPromptsMCPsAgentsGamesBlogVideosGuidesCoursesCommunityGemsExtensionsTrending
    GeminiRulesIilm Faculty Connect Gemini Rules
    Back to Rules

    Iilm Faculty Connect Gemini Rules

    Rudrakshsachdev July 19, 2026
    0 copies 0 downloads
    Rule Content
    import { supabase } from "../lib/supabaseClient";
    import { enrichSubmissionsWithOriginalData } from "./dashboardService";
    import { persistentCache } from "./cacheService";
    
    // Helper to get active Gemini API key (from environment or session storage)
    export function getGeminiApiKey() {
      const envKey = import.meta.env.VITE_GEMINI_API_KEY;
      if (envKey && envKey.trim().length > 0) {
        return envKey;
      }
      return sessionStorage.getItem("temp_gemini_api_key") || "";
    }
    
    // ─── Gemini API Callers ──────────────────────────────────────────────
    
    export async function generateEmbedding(text) {
      const cleanText = String(text || "").trim().replace(/\n/g, " ");
      if (!cleanText) {
        throw new Error("Cannot generate embedding for empty text.");
      }
    
      const { data, error } = await supabase.functions.invoke("gemini-proxy", {
        body: { action: "embed", text: cleanText }
      });
    
      if (error) {
        console.error("[generateEmbedding] Proxy call failed:", error);
        throw error;
      }
    
      const values = data?.embedding?.values;
      if (!values || !Array.isArray(values) || values.length === 0) {
        throw new Error("Embedding API response did not contain vector values.");
      }
    
      return values; // 768 float array
    }
    
    /**
     * Generate match explanation using models/gemini-2.0-flash via proxy.
     */
    export async function generateExplanation(
      queryTitle,
      queryAbstract,
      candidateName,
      candidateDetails,
      candidateItems
    ) {
      const prompt = `You are an AI academic co-author matchmaking assistant for IILM University.
    A faculty member is looking for a collaborator for the following proposed research idea:
    Proposed Research Title: "${queryTitle}"
    Proposed Abstract/Idea: "${queryAbstract}"
    
    We found a potential co-author match:
    Name: ${candidateName}
    Designation: ${candidateDetails.designation || "Faculty Member"}
    School/Department: ${candidateDetails.school || "N/A"}
    Specialization: ${candidateDetails.specialization || "N/A"}
    Overlapping Specializations/Keywords: ${candidateDetails.specialization || "N/A"}
    
    Here are their most relevant publications, patents, or projects matching the query:
    ${candidateItems.map((item, idx) => `- [${item.type}] "${item.title}" (Relevance Score: ${(item.similarity * 100).toFixed(0)}%)`).join("\n")}
    
    Please write a highly professional, dynamic, and specific explanation of why this faculty member is a great fit.
    Rules:
    1. Keep the explanation concise (2 to 3 sentences max).
    2. Point out specific matching topics or methodologies based on the candidate's publications/projects.
    3. Highlight complementary expertise if applicable (e.g., if the user is working on machine learning applied to medicine, and this candidate has medical domain expertise, state how their clinical background complements the technical proposal).
    4. Write in a supportive academic tone. Do NOT use generic text or placeholders.`;
    
      const models = ["gemini-3.5-flash", "gemini-2.0-flash", "gemini-1.5-flash", "gemini-2.0-flash-lite"];
      let explanation = "";
    
      for (const model of models) {
        try {
          const { data, error } = await supabase.functions.invoke("gemini-proxy", {
            body: {
              action: "generate",
              prompt,
              generationConfig: {
                maxOutputTokens: 180,
                temperature: 0.3,
              },
              model,
            }
          });
    
          if (!error && data) {
            explanation = data?.candidates?.[0]?.content?.parts?.[0]?.text?.trim() || "";
            if (explanation) break;
          } else {
            console.warn(`[generateExplanation] Model ${model} failed:`, error || data);
          }
        } catch (err) {
          console.warn(`[generateExplanation] Exception for ${model}:`, err.message);
        }
      }
    
      return explanation || `Recommended based on their research in ${candidateDetails.specialization || "related fields"} and matching publications: ${candidateItems.map(i => `"${i.title}"`).slice(0, 2).join(", ")}.`;
    }
    
    // ─── Document Vectorization Formatters ───────────────────────────────
    
    function formatRecordToText(type, record, userName) {
      if (type === "profile") {
        return `Faculty Profile of ${userName}:
        Designation: ${record.designation || ""}
        School: ${record.school_faculty || ""}
        Specialization: ${record.area_of_specialization || ""}
        Highest Qualification: ${record.highest_qualification || ""}
        Orcid: ${record.orcid_id || ""}
        Scopus: ${record.scopus_id || ""}`.trim();
      }
    
      if (type === "journal_publications") {
        return `Journal Publication by ${userName}:
        Title: ${record.title_of_paper || ""}
        Journal: ${record.journal_name || ""}
        Publisher: ${record.publisher || ""}
        Keywords/Metadata: ${record.remarks || ""}
        Quartile: ${record.quartile || ""}`.trim();
      }
    
      if (type === "conference_publications") {
        return `Conference Paper by ${userName}:
        Title: ${record.title_of_paper || ""}
        Conference: ${record.conference_name || ""}
        Organizing Body: ${record.organizing_body || ""}
        Type: ${record.type || ""}`.trim();
      }
    
      if (type === "patents") {
        return `Patent by ${userName}:
        Title: ${record.title_of_patent || ""}
        Inventors: ${record.inventors || ""}
        Status: ${record.patent_status || ""}
        Type: ${record.patent_type || ""}`.trim();
      }
    
      if (type === "book_chapters") {
        return `Book Chapter by ${userName}:
        Title: ${record.chapter_title || ""}
        Book: ${record.book_title || ""}
        Publisher: ${record.publisher || ""}`.trim();
      }
    
      if (type === "books_authored") {
        return `Book Authored by ${userName}:
        Title: ${record.book_title || ""}
        Publisher: ${record.publisher || ""}
        Type: ${record.authored_or_edited || ""}`.trim();
      }
    
      if (type === "consultancy_projects") {
        return `Consultancy Project by ${userName}:
        Title: ${record.title || record.purpose || ""}
        Client: ${record.client || ""}
        Status: ${record.status || ""}`.trim();
      }
    
      if (type === "faculty_achievements") {
        return `Achievement by ${userName}:
        Title: ${record.title || record.name_of_award || ""}
        Category: ${record.category || ""}
        Description: ${record.brief_description || ""}`.trim();
      }
    
      if (type === "industry_collaboration") {
        return `Industry Collaboration by ${userName}:
        Industry Name: ${record.industry_name || ""}
        Nature: ${record.nature_of_collaboration || ""}
        Outcomes: ${record.outcomes || ""}`.trim();
      }
    
      // Fallback
      return `${type} by ${userName}: ${record.title || record.name || ""}`;
    }
    
    // ─── Similarity Math Fallback ────────────────────────────────────────
    
    function cosineSimilarity(vecA, vecB) {
      let dotProduct = 0.0;
      let normA = 0.0;
      let normB = 0.0;
      for (let i = 0; i < vecA.length; i++) {
        dotProduct += vecA[i] * vecB[i];
        normA += vecA[i] * vecA[i];
        normB += vecB[i] * vecB[i];
      }
      if (normA === 0 || normB === 0) return 0;
      return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
    }
    
    // ─── Index Syncing ───────────────────────────────────────────────────
    
    /**
     * Synchronize the current user's profile and approved research items into the embeddings table.
     * If the user is an admin or is running manually, they can sync their own items.
     * Skips already indexed items based on source_id / user_id constraints.
     */
    export async function syncUserResearchEmbeddings(userId, apiKey, onProgress) {
      const key = apiKey || getGeminiApiKey();
      if (!key) throw new Error("API Key missing");
    
      if (onProgress) onProgress("Fetching user records...", 0);
    
      // 1. Fetch user profile
      const { data: user, error: userError } = await supabase
        .from("users")
        .select("*, faculty_profiles(*)")
        .eq("id", userId)
        .single();
    
      if (userError || !user) throw new Error("User profile not found");
      const fProfile = Array.isArray(user.faculty_profiles) ? user.faculty_profiles[0] : user.faculty_profiles;
    
      // 2. Fetch all approved submissions for this user
      const { data: submissions, error: subError } = await supabase
        .from("unified_submissions")
        .select("*")
        .eq("user_id", userId)
        .in("dean_status", ["approved", "resubmitted", "pending"]); // fetch active research items
    
      if (subError) throw new Error("Failed to fetch submissions");
    
      // 3. Fetch existing indexed embeddings for this user to avoid double embedding
      const { data: existingEmbeddings } = await supabase
        .from("faculty_research_embeddings")
        .select("source_id, type")
        .eq("user_id", userId);
    
      const indexedSet = new Set();
      (existingEmbeddings || []).forEach(e => {
        if (e.source_id) {
          indexedSet.add(`${e.type}:${e.source_id}`);
        }
      });
    
      // Enrich the raw submissions data
      const enrichedMap = await enrichSubmissionsWithOriginalData(submissions || []);
    
      const queue = [];
    
      // Add profile embedding if not already indexed
      // To support standard unique constraint on (source_id, type), we use the userId as source_id for profile entries.
      if (fProfile && fProfile.area_of_specialization && !indexedSet.has(`profile:${userId}`)) {
        queue.push({
          type: "profile",
          source_id: userId,
          content: formatRecordToText("profile", fProfile, user.name),
        });
      }
    
      // Add research items if not already indexed
      submissions.forEach(sub => {
        const key = `${sub.table_name}:${sub.id}`;
        if (!indexedSet.has(key)) {
          const originalRecord = enrichedMap.get(`${sub.table_name}:${sub.id}`);
          if (originalRecord) {
            queue.push({
              type: sub.table_name,
              source_id: sub.id,
              content: formatRecordToText(sub.table_name, originalRecord, user.name),
            });
          }
        }
      });
    
      if (queue.length === 0) {
        if (onProgress) onProgress("Everything up to date!", 100);
        return { synced: 0 };
      }
    
      let successCount = 0;
      for (let i = 0; i < queue.length; i++) {
        const task = queue[i];
        const percent = Math.round((i / queue.length) * 100);
        if (onProgress) onProgress(`Vectorizing ${task.type}... (${i + 1}/${queue.length})`, percent);
    
        try {
          const embedding = await generateEmbedding(task.content, key);
          
          const payload = {
            user_id: userId,
            type: task.type,
            source_id: task.source_id,
            content: task.content,
            embedding: embedding,
          };
    
          const query = supabase
            .from("faculty_research_embeddings")
            .upsert(payload, { onConflict: "source_id,type" });
    
          const { error: upsertErr } = await query;
          if (upsertErr) {
            console.error(`Failed to save embedding for ${task.type}:`, upsertErr);
          } else {
            successCount++;
          }
        } catch (e) {
          console.error(`Error processing embedding queue item ${i}:`, e);
        }
    
        // Delay a bit to prevent rate limit on free tier Gemini API keys
        await new Promise(r => setTimeout(r, 300));
      }
    
      if (onProgress) onProgress(`Successfully synchronized ${successCount} items!`, 100);
      return { synced: successCount };
    }
    
    // ─── Co-Author Finder logic ──────────────────────────────────────────
    
    /**
     * Perform semantic search to find suitable co-authors.
     */
    export async function findCoAuthors(
      { title, abstract, sourcePublicationId, filters, excludeSelf = true },
      apiKey
    ) {
      const key = apiKey || getGeminiApiKey();
      if (!key) throw new Error("API Key missing");
    
      const { data: { session } } = await supabase.auth.getSession();
      const currentUserId = session?.user?.id || "anonymous";
    
      // Create a unique cache key based on user, inputs, filters, and excludeSelf
      const cacheKeyParts = {
        userId: currentUserId,
        title: title || "",
        abstract: abstract || "",
        sourcePublicationId: sourcePublicationId || "",
        filters: filters || {},
        excludeSelf
      };
      const cacheKey = `coauthor_matching:${JSON.stringify(cacheKeyParts)}`;
    
      return persistentCache.get(cacheKey, async () => {
        // 1. Build Query Text
        let queryText = "";
        if (sourcePublicationId) {
          // Fetch source publication
          const { data: pubData, error: pubErr } = await supabase
            .from("unified_submissions")
            .select("*")
            .eq("id", sourcePublicationId)
            .single();
    
          if (pubErr || !pubData) throw new Error("Selected publication not found");
          const enriched = await enrichSubmissionsWithOriginalData([pubData]);
          const original = enriched.get(`${pubData.table_name}:${pubData.id}`);
          
          queryText = original 
            ? formatRecordToText(pubData.table_name, original, "Selected Paper")
            : pubData.title;
        } else {
          queryText = `Research Idea:
          Title: ${title}
          Abstract: ${abstract}`;
        }
    
        // 2. Generate Embedding for Query
        const queryEmbedding = await generateEmbedding(queryText, key);
    
        // 3. Search embeddings
        // First attempt: Supabase RPC
        let matchResults = [];
        let rpcFailed = false;
    
        const threshold = (filters.minMatchScore || 40) / 100;
    
        try {
          const { data, error } = await supabase.rpc("match_research_embeddings", {
            query_embedding: queryEmbedding,
            match_threshold: threshold,
            match_count: 80,
            exclude_user_id: excludeSelf && currentUserId ? currentUserId : "00000000-0000-0000-0000-000000000000",
          });
    
          if (error) {
            console.warn("match_research_embeddings RPC failed. Falling back to client-side search:", error);
            rpcFailed = true;
          } else {
            matchResults = data || [];
          }
        } catch (e) {
          console.warn("RPC match call exception, using client fallback:", e);
          rpcFailed = true;
        }
    
        // Fallback: Client-side cosine similarity search
        if (rpcFailed) {
          let query = supabase.from("faculty_research_embeddings").select("*");
          if (excludeSelf && currentUserId) {
            query = query.neq("user_id", currentUserId);
          }
          const { data: allEmbeds, error: fetchErr } = await query;
          if (fetchErr) throw new Error("Failed to load embeddings for matching: " + fetchErr.message);
    
          (allEmbeds || []).forEach(item => {
            // Parse vector string if returned as string (e.g. '[0.1, 0.2]')
            let itemEmbedding = item.embedding;
            if (typeof itemEmbedding === "string") {
              try {
                itemEmbedding = JSON.parse(itemEmbedding.replace(/{/g, "[").replace(/}/g, "]"));
              } catch {
                // ignore
              }
            }
    
            if (Array.isArray(itemEmbedding)) {
              const similarity = cosineSimilarity(queryEmbedding, itemEmbedding);
              if (similarity > threshold) {
                matchResults.push({
                  id: item.id,
                  user_id: item.user_id,
                  type: item.type,
                  source_id: item.source_id,
                  content: item.content,
                  similarity: similarity,
                });
              }
            }
          });
        }
    
        if (matchResults.length === 0) return [];
    
        // 4. Retrieve candidate profiles and filter
        const candidateUserIds = [...new Set(matchResults.map(r => r.user_id))];
    
        // Fetch users details
        const { data: users, error: usersErr } = await supabase
          .from("users")
          .select("*")
          .in("id", candidateUserIds);
    
        if (usersErr) throw new Error("Failed to fetch user records for candidates");
    
        // Fetch faculty profiles separately (bypass join restrictions/quirks)
        const { data: profiles, error: profilesErr } = await supabase
          .from("faculty_profiles")
          .select("*")
          .in("user_id", candidateUserIds);
    
        if (profilesErr) console.warn("Failed to fetch faculty profiles for candidates:", profilesErr);
    
        // Map profiles by user_id
        const profilesMap = {};
        (profiles || []).forEach(p => {
          profilesMap[p.user_id] = p;
        });
    
        // Compile map of users
        const usersMap = {};
        (users || []).forEach(u => {
          const fProfile = profilesMap[u.id];
          // Account active check: requires faculty_profiles record to exist
          const isActive = !!fProfile;
          usersMap[u.id] = {
            ...u,
            profile: fProfile || {},
            isActive,
          };
        });
    
        // 5. Group match items by candidate and aggregate scores
        const candidateGroups = {};
    
        matchResults.forEach(match => {
          const userObj = usersMap[match.user_id];
          if (!userObj || !userObj.isActive) return;
    
          // Apply basic filter criteria early to save LLM budget
          if (filters.school && filters.school !== "All") {
            const userSchool = userObj.school || userObj.profile?.school_faculty || "";
            if (!userSchool.toLowerCase().includes(filters.school.toLowerCase())) return;
          }
    
          if (filters.designation && filters.designation !== "All") {
            const userDesignation = userObj.profile?.designation || "";
            if (userDesignation.toLowerCase() !== filters.designation.toLowerCase()) return;
          }
    
          if (filters.yearsOfExperience) {
            const experience = Number(userObj.profile?.years_of_experience || 0);
            if (experience < Number(filters.yearsOfExperience)) return;
          }
    
          if (!candidateGroups[match.user_id]) {
            candidateGroups[match.user_id] = {
              userId: match.user_id,
              name: userObj.name,
              email: userObj.email,
              school: userObj.school || userObj.profile?.school_faculty || "Not Assigned",
              designation: userObj.profile?.designation || "Faculty Member",
              specialization: userObj.profile?.area_of_specialization || "General Research",
              profileImage: userObj.profile?.profile_image || null,
              yearsOfExperience: userObj.profile?.years_of_experience || 0,
              maxSimilarity: 0,
              matchingItems: [],
            };
          }
    
          // Extract item details
          let displayTitle = "";
          if (match.type === "profile") {
            displayTitle = `Faculty Specialization: ${candidateGroups[match.user_id].specialization}`;
          } else {
            // Parse clean title from content string
            const lines = match.content.split("\n");
            const titleLine = lines.find(l => l.trim().startsWith("Title:"));
            displayTitle = titleLine ? titleLine.replace(/^\s*Title:\s*/, "").trim() : "Untitled Sub";
          }
    
          candidateGroups[match.user_id].matchingItems.push({
            type: match.type,
            sourceId: match.source_id,
            title: displayTitle,
            similarity: match.similarity,
          });
    
          if (match.similarity > candidateGroups[match.user_id].maxSimilarity) {
            candidateGroups[match.user_id].maxSimilarity = match.similarity;
          }
        });
    
        // Convert map to list and sort by maxSimilarity descending
        const candidates = Object.values(candidateGroups);
        candidates.sort((a, b) => b.maxSimilarity - a.maxSimilarity);
    
        // Limit to top recommendations for LLM budget efficiency
        const topCandidates = candidates.slice(0, filters.limit || 5);
    
        // 6. Generate explanations for top candidates dynamically using Gemini 2.5 Flash
        const finalRecommendations = await Promise.all(
          topCandidates.map(async candidate => {
            // Pick top 3 matching items to send to the prompt
            const sortedItems = [...candidate.matchingItems].sort((a, b) => b.similarity - a.similarity);
            const explanation = await generateExplanation(
              title || "Selected Publication Research Topic",
              abstract || "Abstract matching",
              candidate.name,
              {
                designation: candidate.designation,
                school: candidate.school,
                specialization: candidate.specialization,
              },
              sortedItems.slice(0, 3),
              key
            );
    
            return {
              ...candidate,
              matchScore: Math.round(candidate.maxSimilarity * 100),
              explanation,
              matchingItems: sortedItems,
            };
          })
        );
    
        return finalRecommendations;
      }, 24 * 60 * 60 * 1000);
    }
    
    
    /**
     * Updates years of experience for a faculty member.
     * Operates standalone, allowing experience updates directly from the Finder.
     */
    export async function updateFacultyExperience(userId, years) {
      const numYears = parseInt(years, 10);
      if (isNaN(numYears) || numYears < 0) throw new Error("Invalid years of experience");
    
      // Upsert profile or update
      const { data: existing } = await supabase
        .from("faculty_profiles")
        .select("id")
        .eq("user_id", userId)
        .maybeSingle();
    
      if (existing) {
        const { error } = await supabase
          .from("faculty_profiles")
          .update({ years_of_experience: numYears })
          .eq("user_id", userId);
        if (error) throw error;
      } else {
        const { error } = await supabase
          .from("faculty_profiles")
          .insert({ user_id: userId, years_of_experience: numYears });
        if (error) throw error;
      }
    
      return { success: true };
    }
    
    /**
     * Generates an insights-driven AI analysis for a specific faculty member's profile.
     * Compiles their profile details and approved research submissions and calls Gemini 2.5 Flash.
     */
    export async function generateFacultyProfileAnalysis(userId, apiKey) {
      const key = apiKey || getGeminiApiKey();
      if (!key) throw new Error("API Key missing");
    
      // 1. Fetch user core info & profile details
      const { data: user, error: userError } = await supabase
        .from("users")
        .select("*, faculty_profiles(*)")
        .eq("id", userId)
        .single();
    
      if (userError || !user) throw new Error("User or profile not found");
      const fProfile = Array.isArray(user.faculty_profiles) ? user.faculty_profiles[0] : user.faculty_profiles;
    
      // 2. Fetch all approved submissions
      const { data: submissions, error: subError } = await supabase
        .from("unified_submissions")
        .select("*")
        .eq("user_id", userId)
        .in("dean_status", ["approved", "resubmitted", "pending"]);
    
      if (subError) throw new Error("Failed to fetch faculty submissions");
    
      // Enrich original records (same helper we use in syncing)
      const enrichedMap = await enrichSubmissionsWithOriginalData(submissions || []);
    
      // 3. Compile a clean textual summary of the faculty member's entire body of work
      let textSummary = `FACULTY NAME: ${user.name}
    DESIGNATION: ${fProfile?.designation || "Faculty Member"}
    SCHOOL: ${user.school || fProfile?.school_faculty || "Not Assigned"}
    SPECIALIZATION: ${fProfile?.area_of_specialization || "General Research"}
    EXPERIENCE: ${fProfile?.years_of_experience || 0} years
    QUALIFICATION: ${fProfile?.highest_qualification || "N/A"}\n\n`;
    
      textSummary += `=== RESEARCH OUTPUT & CONTRIBUTIONS ===\n`;
      if (submissions && submissions.length > 0) {
        submissions.forEach((sub, idx) => {
          const originalRecord = enrichedMap.get(`${sub.table_name}:${sub.id}`);
          if (originalRecord) {
            textSummary += `\n[Asset #${idx + 1}: ${sub.table_name.replace(/_/g, " ").toUpperCase()}]
    ${formatRecordToText(sub.table_name, originalRecord, user.name)}\n`;
          }
        });
      } else {
        textSummary += `No publication or research assets indexed in the system yet.\n`;
      }
    
      // 4. Construct prompt for Gemini 2.5 Flash
      const prompt = `You are an elite academic panel analyst evaluating a faculty member's research footprint.
    Analyze the following faculty member's profile summary and complete body of work:
    
    ${textSummary}
    
    Generate a premium, professional, and insights-driven analysis in Markdown format. The analysis MUST contain the following four structured sections:
    
    ## 1. Research Focus & Core Domain
    - Synthesize a concise summary of their main research themes, keywords, and domain focus.
    
    ## 2. Key Academic Highlights
    - Highlight notable aspects of their output (e.g. types of publications, patents, book chapters, or collaborations). Mention specific topics or papers from their record.
    
    ## 3. Core Strengths
    - Summarize 2-3 specific technical and domain strengths based on their publications and profile.
    
    ## 4. Collaboration & Synergy Areas
    - Identify specific research topics or emerging fields where this faculty member would be an ideal co-author, project partner, or mentor for other colleagues.
    
    Rules:
    1. Do NOT include any top-level title (e.g. "Academic Profile Evaluation"). Start directly with the first heading "## 1. Research Focus & Core Domain".
    2. Do NOT output markdown dividers (such as "---", "--", or "- --").
    3. Keep the tone highly professional, academic, constructive, and inspiring. Do not use generic placeholders. Refer specifically to details from their provided body of work. Return ONLY the markdown.`;
    
      // 5. Call Gemini 2.0 Flash
      const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-lite:generateContent?key=${key}`;
      const response = await fetch(url, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          contents: [{ parts: [{ text: prompt }] }]
        })
      });
    
      if (!response.ok) {
        const errText = await response.text();
        console.error("Gemini API error during profile analysis:", errText);
        throw new Error("Failed to generate AI analysis: " + response.statusText);
      }
    
      const result = await response.json();
      const text = result?.candidates?.[0]?.content?.parts?.[0]?.text;
      if (!text) throw new Error("Empty response received from Gemini API");
    
      return text.trim();
    }
    

    Comments

    More Rules

    View all

    Sleepycat Seo Platform Gemini Rules

    A
    acovrp

    LITE IDE Gemini Rules

    H
    Heyz24

    Shadowtagai Monorepo V2 Gemini Rules

    S
    ShadowTag-v2

    WoKFlow Gemini Rules

    H
    howyongheng0313

    MetaPrompter Gemini Rules

    U
    ummerr

    Taskora Gemini Rules

    K
    kabilesh-c

    Stay up to date

    Get the latest Gemini prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for Gemini and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.

    Neura Market

    Custom AI Systems & Services

    Our team of experienced AI builders will help build custom AI systems, workflows, and solutions for your business.

    Request custom work

    Ready-made automations for this

    Workflows from the Neura Market marketplace related to this Gemini resource

    • Process Multiple Media Files in Telegram with Gemini AI & PostgreSQL Databasen8n · $24.99 · Related topic
    • Kubernetes RCA and Alerting Using Gemini, Loki, Prometheus, Slackn8n · $24.99 · Related topic
    • Create Custom PDF Documents from Templates with Gemini & Google Driven8n · $24.99 · Related topic
    • AI-Powered Auto-Generated Exam Questions and Answers from Google Docs with Geminin8n · $24.99 · Related topic
    Browse all workflows