Spaces:
Running
Running
refactor: reduce technical debt by removing unused methods and tool definitions
Browse files- Remove unused questionTools from aiService constructor
- Remove unused getEnhancedFallback and getContextualHint methods
- Remove unused getContextualization method (duplicate)
- Remove unused helper methods from conversationManager
- Clean up unused word processing and variation methods
- Maintain only essential, actively used code paths
- src/aiService.js +0 -153
- src/conversationManager.js +0 -57
src/aiService.js
CHANGED
|
@@ -3,54 +3,6 @@ class OpenRouterService {
|
|
| 3 |
this.apiUrl = 'https://openrouter.ai/api/v1/chat/completions';
|
| 4 |
this.apiKey = this.getApiKey();
|
| 5 |
this.model = 'google/gemma-3-27b-it:free';
|
| 6 |
-
|
| 7 |
-
// Focused tool definitions for specific, non-overlapping question types
|
| 8 |
-
this.questionTools = {
|
| 9 |
-
part_of_speech: {
|
| 10 |
-
name: 'identify_part_of_speech',
|
| 11 |
-
description: 'Identify the grammatical category directly and clearly',
|
| 12 |
-
parameters: {
|
| 13 |
-
type: 'object',
|
| 14 |
-
properties: {
|
| 15 |
-
hint: { type: 'string', description: 'Direct answer: "This is a [noun/verb/adjective/adverb]" then add a simple, concrete clue about what type (e.g., "a thing", "an action", "describes something")' }
|
| 16 |
-
},
|
| 17 |
-
required: ['hint']
|
| 18 |
-
}
|
| 19 |
-
},
|
| 20 |
-
sentence_role: {
|
| 21 |
-
name: 'explain_sentence_role',
|
| 22 |
-
description: 'Explain the structural function using context clues',
|
| 23 |
-
parameters: {
|
| 24 |
-
type: 'object',
|
| 25 |
-
properties: {
|
| 26 |
-
hint: { type: 'string', description: 'Point to specific words around the blank. Example: "Look at \'the whole ___ consisting of\' - what could contain something?" Focus on the immediate context.' }
|
| 27 |
-
},
|
| 28 |
-
required: ['hint']
|
| 29 |
-
}
|
| 30 |
-
},
|
| 31 |
-
word_category: {
|
| 32 |
-
name: 'categorize_word',
|
| 33 |
-
description: 'State clearly if abstract or concrete',
|
| 34 |
-
parameters: {
|
| 35 |
-
type: 'object',
|
| 36 |
-
properties: {
|
| 37 |
-
hint: { type: 'string', description: 'Start simple: "This is abstract/concrete." Then give a relatable example or size clue: "Think about something very big/small" or "Like feelings/objects"' }
|
| 38 |
-
},
|
| 39 |
-
required: ['hint']
|
| 40 |
-
}
|
| 41 |
-
},
|
| 42 |
-
synonym: {
|
| 43 |
-
name: 'provide_synonym',
|
| 44 |
-
description: 'Give clear synonym or similar word',
|
| 45 |
-
parameters: {
|
| 46 |
-
type: 'object',
|
| 47 |
-
properties: {
|
| 48 |
-
hint: { type: 'string', description: 'Direct synonyms or word families: "Try a word similar to [related word]" or "Think of another word for [meaning]"' }
|
| 49 |
-
},
|
| 50 |
-
required: ['hint']
|
| 51 |
-
}
|
| 52 |
-
}
|
| 53 |
-
};
|
| 54 |
}
|
| 55 |
|
| 56 |
getApiKey() {
|
|
@@ -114,57 +66,6 @@ class OpenRouterService {
|
|
| 114 |
}
|
| 115 |
}
|
| 116 |
|
| 117 |
-
getEnhancedFallback(questionType, word, sentence, bookTitle) {
|
| 118 |
-
const fallbacks = {
|
| 119 |
-
part_of_speech: `Consider what "${word}" is doing in the sentence. Is it a person, place, thing (noun), an action (verb), or describing something (adjective)?`,
|
| 120 |
-
sentence_role: `Look at how "${word}" connects to other words around it. What is its job in making the sentence complete?`,
|
| 121 |
-
word_category: `Think about whether "${word}" is something you can touch or see (concrete) or an idea/feeling (abstract).`,
|
| 122 |
-
synonym: `What other word could replace "${word}" and keep the same meaning in this sentence?`
|
| 123 |
-
};
|
| 124 |
-
|
| 125 |
-
return fallbacks[questionType] || `Think about what "${word}" means in this classic literature context.`;
|
| 126 |
-
}
|
| 127 |
-
|
| 128 |
-
async getContextualHint(passage, wordToReplace, context) {
|
| 129 |
-
if (!this.apiKey) {
|
| 130 |
-
return 'API key required for contextual hints';
|
| 131 |
-
}
|
| 132 |
-
|
| 133 |
-
try {
|
| 134 |
-
const response = await fetch(this.apiUrl, {
|
| 135 |
-
method: 'POST',
|
| 136 |
-
headers: {
|
| 137 |
-
'Content-Type': 'application/json',
|
| 138 |
-
'Authorization': `Bearer ${this.apiKey}`,
|
| 139 |
-
'HTTP-Referer': window.location.origin,
|
| 140 |
-
'X-Title': 'Cloze Reader'
|
| 141 |
-
},
|
| 142 |
-
body: JSON.stringify({
|
| 143 |
-
model: this.model,
|
| 144 |
-
messages: [{
|
| 145 |
-
role: 'user',
|
| 146 |
-
content: `In this passage: "${passage}"
|
| 147 |
-
|
| 148 |
-
The word "${wordToReplace}" has been replaced with a blank. Give me a helpful hint about what word fits here, considering the context: "${context}".
|
| 149 |
-
|
| 150 |
-
Provide a brief, educational hint that helps understand the word without giving it away directly.`
|
| 151 |
-
}],
|
| 152 |
-
max_tokens: 150,
|
| 153 |
-
temperature: 0.7
|
| 154 |
-
})
|
| 155 |
-
});
|
| 156 |
-
|
| 157 |
-
if (!response.ok) {
|
| 158 |
-
throw new Error(`API request failed: ${response.status}`);
|
| 159 |
-
}
|
| 160 |
-
|
| 161 |
-
const data = await response.json();
|
| 162 |
-
return data.choices[0].message.content.trim();
|
| 163 |
-
} catch (error) {
|
| 164 |
-
console.error('Error getting contextual hint:', error);
|
| 165 |
-
return 'Unable to generate hint at this time';
|
| 166 |
-
}
|
| 167 |
-
}
|
| 168 |
|
| 169 |
async selectSignificantWords(passage, count) {
|
| 170 |
console.log('selectSignificantWords called with count:', count);
|
|
@@ -290,60 +191,6 @@ Passage: "${passage}"`
|
|
| 290 |
}
|
| 291 |
}
|
| 292 |
|
| 293 |
-
async getContextualization(title, author, passage) {
|
| 294 |
-
console.log('getContextualization called for:', title, 'by', author);
|
| 295 |
-
|
| 296 |
-
// Check for API key at runtime
|
| 297 |
-
const currentKey = this.getApiKey();
|
| 298 |
-
if (currentKey && !this.apiKey) {
|
| 299 |
-
this.apiKey = currentKey;
|
| 300 |
-
}
|
| 301 |
-
|
| 302 |
-
console.log('API key available for contextualization:', !!this.apiKey);
|
| 303 |
-
|
| 304 |
-
if (!this.apiKey) {
|
| 305 |
-
console.log('No API key, returning fallback contextualization');
|
| 306 |
-
return `📚 Practice with classic literature from ${author}'s "${title}"`;
|
| 307 |
-
}
|
| 308 |
-
|
| 309 |
-
try {
|
| 310 |
-
const response = await fetch(this.apiUrl, {
|
| 311 |
-
method: 'POST',
|
| 312 |
-
headers: {
|
| 313 |
-
'Content-Type': 'application/json',
|
| 314 |
-
'Authorization': `Bearer ${this.apiKey}`,
|
| 315 |
-
'HTTP-Referer': window.location.origin,
|
| 316 |
-
'X-Title': 'Cloze Reader'
|
| 317 |
-
},
|
| 318 |
-
body: JSON.stringify({
|
| 319 |
-
model: this.model,
|
| 320 |
-
messages: [{
|
| 321 |
-
role: 'system',
|
| 322 |
-
content: 'You are a literary expert providing brief educational context about classic literature. Always respond with exactly 2 sentences, no more. Avoid exaggerative adverbs. Be factual and restrained.'
|
| 323 |
-
}, {
|
| 324 |
-
role: 'user',
|
| 325 |
-
content: `Provide educational context for this passage from "${title}" by ${author}: "${passage}"`
|
| 326 |
-
}],
|
| 327 |
-
max_tokens: 100,
|
| 328 |
-
temperature: 0.3
|
| 329 |
-
})
|
| 330 |
-
});
|
| 331 |
-
|
| 332 |
-
if (!response.ok) {
|
| 333 |
-
const errorText = await response.text();
|
| 334 |
-
console.error('Contextualization API error:', response.status, errorText);
|
| 335 |
-
throw new Error(`API request failed: ${response.status}`);
|
| 336 |
-
}
|
| 337 |
-
|
| 338 |
-
const data = await response.json();
|
| 339 |
-
const content = data.choices[0].message.content.trim();
|
| 340 |
-
console.log('Contextualization received:', content);
|
| 341 |
-
return content;
|
| 342 |
-
} catch (error) {
|
| 343 |
-
console.error('Error getting contextualization:', error);
|
| 344 |
-
return `📚 Practice with classic literature from ${author}'s "${title}"`;
|
| 345 |
-
}
|
| 346 |
-
}
|
| 347 |
}
|
| 348 |
|
| 349 |
export { OpenRouterService as AIService };
|
|
|
|
| 3 |
this.apiUrl = 'https://openrouter.ai/api/v1/chat/completions';
|
| 4 |
this.apiKey = this.getApiKey();
|
| 5 |
this.model = 'google/gemma-3-27b-it:free';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
}
|
| 7 |
|
| 8 |
getApiKey() {
|
|
|
|
| 66 |
}
|
| 67 |
}
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
async selectSignificantWords(passage, count) {
|
| 71 |
console.log('selectSignificantWords called with count:', count);
|
|
|
|
| 191 |
}
|
| 192 |
}
|
| 193 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
}
|
| 195 |
|
| 196 |
export { OpenRouterService as AIService };
|
src/conversationManager.js
CHANGED
|
@@ -129,63 +129,6 @@ class ChatService {
|
|
| 129 |
};
|
| 130 |
}
|
| 131 |
|
| 132 |
-
// Helper method to get words before the target word
|
| 133 |
-
getWordsBefore(sentence, targetWord, count = 3) {
|
| 134 |
-
const words = sentence.split(/\s+/);
|
| 135 |
-
const targetIndex = words.findIndex(word =>
|
| 136 |
-
word.toLowerCase().replace(/[^\w]/g, '') === targetWord.toLowerCase()
|
| 137 |
-
);
|
| 138 |
-
|
| 139 |
-
if (targetIndex === -1) return "";
|
| 140 |
-
|
| 141 |
-
const startIndex = Math.max(0, targetIndex - count);
|
| 142 |
-
return words.slice(startIndex, targetIndex).join(' ');
|
| 143 |
-
}
|
| 144 |
-
|
| 145 |
-
// Helper method to get words after the target word
|
| 146 |
-
getWordsAfter(sentence, targetWord, count = 3) {
|
| 147 |
-
const words = sentence.split(/\s+/);
|
| 148 |
-
const targetIndex = words.findIndex(word =>
|
| 149 |
-
word.toLowerCase().replace(/[^\w]/g, '') === targetWord.toLowerCase()
|
| 150 |
-
);
|
| 151 |
-
|
| 152 |
-
if (targetIndex === -1) return "";
|
| 153 |
-
|
| 154 |
-
const endIndex = Math.min(words.length, targetIndex + count + 1);
|
| 155 |
-
return words.slice(targetIndex + 1, endIndex).join(' ');
|
| 156 |
-
}
|
| 157 |
-
|
| 158 |
-
// Process AI response to ensure quality and safety
|
| 159 |
-
processAIResponse(rawResponse, targetWord) {
|
| 160 |
-
let processed = rawResponse.trim();
|
| 161 |
-
|
| 162 |
-
// Remove any accidental word reveals
|
| 163 |
-
const variations = this.generateWordVariations(targetWord);
|
| 164 |
-
variations.forEach(variation => {
|
| 165 |
-
const regex = new RegExp(`\\b${variation}\\b`, 'gi');
|
| 166 |
-
processed = processed.replace(regex, '[the word]');
|
| 167 |
-
});
|
| 168 |
-
|
| 169 |
-
return processed;
|
| 170 |
-
}
|
| 171 |
-
|
| 172 |
-
// Generate word variations to avoid accidental reveals
|
| 173 |
-
generateWordVariations(word) {
|
| 174 |
-
const variations = [word.toLowerCase()];
|
| 175 |
-
|
| 176 |
-
// Add common variations
|
| 177 |
-
if (word.endsWith('ing')) {
|
| 178 |
-
variations.push(word.slice(0, -3));
|
| 179 |
-
}
|
| 180 |
-
if (word.endsWith('ed')) {
|
| 181 |
-
variations.push(word.slice(0, -2));
|
| 182 |
-
}
|
| 183 |
-
if (word.endsWith('s')) {
|
| 184 |
-
variations.push(word.slice(0, -1));
|
| 185 |
-
}
|
| 186 |
-
|
| 187 |
-
return variations;
|
| 188 |
-
}
|
| 189 |
|
| 190 |
// Clear conversations and reset tracking
|
| 191 |
clearConversations() {
|
|
|
|
| 129 |
};
|
| 130 |
}
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
// Clear conversations and reset tracking
|
| 134 |
clearConversations() {
|