Lashtw commited on
Commit
f64454d
·
verified ·
1 Parent(s): 2a626cb

Upload 10 files

Browse files
Files changed (1) hide show
  1. src/services/gemini.js +57 -27
src/services/gemini.js CHANGED
@@ -1,12 +1,11 @@
1
 
2
- import { GoogleGenerativeAI } from "https://esm.run/@google/generative-ai@0.23.0";
3
  import { getPeerPrompts } from "./classroom.js";
4
  import { db } from "./firebase.js";
5
  import { doc, getDoc } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js";
6
 
7
- let genAI = null;
8
- let model = null;
9
  let apiKey = null;
 
10
 
11
  // System Instructions for the Socratic Tutor
12
  const TUTOR_INSTRUCTION = `
@@ -50,14 +49,51 @@ Example: { "rough": ["id1"], "precise": ["id2", "id5"], "spam": ["id3"] ... }
50
  export async function initGemini(key) {
51
  if (!key) return false;
52
  apiKey = key;
53
- try {
54
- genAI = new GoogleGenerativeAI(apiKey);
55
- model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
56
- return true;
57
- } catch (e) {
58
- console.error("Gemini Init Failed", e);
59
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
 
 
 
 
61
  }
62
 
63
  /**
@@ -90,26 +126,20 @@ export function quickSpamCheck(prompt, problemDesc) {
90
  * @param {string} challengeId
91
  */
92
  export async function askTutor(challengeDesc, studentPrompt, roomCode, challengeId) {
93
- if (!model) throw new Error("AI not initialized");
94
 
95
  const spamStatus = quickSpamCheck(studentPrompt, challengeDesc);
96
  if (spamStatus === true) return "Duck says: Quack? (Too short or empty!)";
97
  if (spamStatus === 'parrot') return "Duck says: You're just repeating the question! Try telling me WHAT you want to change.";
98
 
99
  // 1. Fetch Context (Few-Shot)
100
- // We want 'Starred' examples first, then random successful ones.
101
  let peers = await getPeerPrompts(roomCode, challengeId);
102
-
103
- // Filter for instructor stars (assuming 'isStarred' or 'likes' > threshold)
104
- // Since 'isStarred' isn't in DB yet, we use 'likes' as proxy for quality or randomness.
105
- // Ideally, we add a 'starred' field in Instructor Dashboard later.
106
- // For now, let's prioritize high likes.
107
  peers.sort((a, b) => b.likes - a.likes);
108
 
109
  // Take top 3 as examples
110
  const examples = peers.slice(0, 3).map(p => `- Example: "${p.prompt}"`).join('\n');
111
 
112
- const prompt = `
113
  ${TUTOR_INSTRUCTION}
114
 
115
  [Context - Challenge Description]
@@ -125,8 +155,10 @@ ${examples || "No examples available yet."}
125
  `;
126
 
127
  try {
128
- const result = await model.generateContent(prompt);
129
- return result.response.text();
 
 
130
  } catch (e) {
131
  console.error("AI Request Failed", e);
132
  return "Duck is sleeping... zzz (API Error)";
@@ -139,13 +171,13 @@ ${examples || "No examples available yet."}
139
  * @param {string} challengeDesc
140
  */
141
  export async function evaluatePrompts(submissions, challengeDesc) {
142
- if (!model) throw new Error("AI not initialized");
143
  if (!submissions || submissions.length === 0) return {};
144
 
145
  // Prepare batch text
146
  const entries = submissions.map((s, i) => `ID_${s.userId}: "${s.prompt.replace(/\n/g, ' ')}"`).join('\n');
147
 
148
- const prompt = `
149
  ${ANALYST_INSTRUCTION}
150
 
151
  [Challenge Description]
@@ -158,11 +190,9 @@ ${entries}
158
  `;
159
 
160
  try {
161
- const result = await model.generateContent({
162
- contents: [{ role: "user", parts: [{ text: prompt }] }],
163
- generationConfig: { responseMimeType: "application/json" }
164
- });
165
- return JSON.parse(result.response.text());
166
  } catch (e) {
167
  console.error("AI Evaluation Failed", e);
168
  return {};
 
1
 
2
+ // import { GoogleGenerativeAI } from "https://esm.run/@google/generative-ai"; // Removing SDK to avoid version issues
3
  import { getPeerPrompts } from "./classroom.js";
4
  import { db } from "./firebase.js";
5
  import { doc, getDoc } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js";
6
 
 
 
7
  let apiKey = null;
8
+ const MODEL_NAME = "gemini-1.5-flash"; // Use standard model name
9
 
10
  // System Instructions for the Socratic Tutor
11
  const TUTOR_INSTRUCTION = `
 
49
  export async function initGemini(key) {
50
  if (!key) return false;
51
  apiKey = key;
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * Direct REST API Call Helper to avoid SDK version issues
57
+ */
58
+ async function callGeminiAPI(messages, jsonMode = false) {
59
+ if (!apiKey) throw new Error("API Key not set");
60
+
61
+ const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL_NAME}:generateContent?key=${apiKey}`;
62
+
63
+ const payload = {
64
+ contents: messages,
65
+ generationConfig: {
66
+ temperature: 0.7,
67
+ }
68
+ };
69
+
70
+ if (jsonMode) {
71
+ payload.generationConfig.responseMimeType = "application/json";
72
+ }
73
+
74
+ const response = await fetch(endpoint, {
75
+ method: "POST",
76
+ headers: {
77
+ "Content-Type": "application/json"
78
+ },
79
+ body: JSON.stringify(payload)
80
+ });
81
+
82
+ if (!response.ok) {
83
+ const errText = await response.text();
84
+ throw new Error(`Gemini API Error ${response.status}: ${errText}`);
85
+ }
86
+
87
+ const data = await response.json();
88
+
89
+ // Safety check for empty response
90
+ if (data.promptFeedback && data.promptFeedback.blockReason) {
91
+ throw new Error(`Blocked: ${data.promptFeedback.blockReason}`);
92
  }
93
+
94
+ return data.candidates && data.candidates.length > 0
95
+ ? data.candidates[0].content.parts[0].text
96
+ : "";
97
  }
98
 
99
  /**
 
126
  * @param {string} challengeId
127
  */
128
  export async function askTutor(challengeDesc, studentPrompt, roomCode, challengeId) {
129
+ if (!apiKey) throw new Error("AI not initialized");
130
 
131
  const spamStatus = quickSpamCheck(studentPrompt, challengeDesc);
132
  if (spamStatus === true) return "Duck says: Quack? (Too short or empty!)";
133
  if (spamStatus === 'parrot') return "Duck says: You're just repeating the question! Try telling me WHAT you want to change.";
134
 
135
  // 1. Fetch Context (Few-Shot)
 
136
  let peers = await getPeerPrompts(roomCode, challengeId);
 
 
 
 
 
137
  peers.sort((a, b) => b.likes - a.likes);
138
 
139
  // Take top 3 as examples
140
  const examples = peers.slice(0, 3).map(p => `- Example: "${p.prompt}"`).join('\n');
141
 
142
+ const fullPrompt = `
143
  ${TUTOR_INSTRUCTION}
144
 
145
  [Context - Challenge Description]
 
155
  `;
156
 
157
  try {
158
+ // Construct messages for Chat format (or simple text)
159
+ const messages = [{ role: "user", parts: [{ text: fullPrompt }] }];
160
+ const text = await callGeminiAPI(messages);
161
+ return text;
162
  } catch (e) {
163
  console.error("AI Request Failed", e);
164
  return "Duck is sleeping... zzz (API Error)";
 
171
  * @param {string} challengeDesc
172
  */
173
  export async function evaluatePrompts(submissions, challengeDesc) {
174
+ if (!apiKey) throw new Error("AI not initialized");
175
  if (!submissions || submissions.length === 0) return {};
176
 
177
  // Prepare batch text
178
  const entries = submissions.map((s, i) => `ID_${s.userId}: "${s.prompt.replace(/\n/g, ' ')}"`).join('\n');
179
 
180
+ const fullPrompt = `
181
  ${ANALYST_INSTRUCTION}
182
 
183
  [Challenge Description]
 
190
  `;
191
 
192
  try {
193
+ const messages = [{ role: "user", parts: [{ text: fullPrompt }] }];
194
+ const text = await callGeminiAPI(messages, true); // JSON Mode = true
195
+ return JSON.parse(text);
 
 
196
  } catch (e) {
197
  console.error("AI Evaluation Failed", e);
198
  return {};