milwright commited on
Commit
63a5bc1
·
1 Parent(s): 3c9c1d8

feat: add clean aiService.js without hardcoded keys

Browse files

- Clean implementation using only environment variables
- OPENROUTER_API_KEY and HF_API_KEY from HuggingFace secrets
- No hardcoded API keys in source code

Files changed (1) hide show
  1. src/aiService.js +104 -0
src/aiService.js ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class OpenRouterService {
2
+ constructor() {
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() {
9
+ if (typeof process !== 'undefined' && process.env && process.env.OPENROUTER_API_KEY) {
10
+ return process.env.OPENROUTER_API_KEY;
11
+ }
12
+ if (typeof window !== 'undefined' && window.OPENROUTER_API_KEY) {
13
+ return window.OPENROUTER_API_KEY;
14
+ }
15
+ return '';
16
+ }
17
+
18
+ setApiKey(key) {
19
+ this.apiKey = key;
20
+ }
21
+
22
+ async getContextualHint(passage, wordToReplace, context) {
23
+ if (!this.apiKey) {
24
+ return 'API key required for contextual hints';
25
+ }
26
+
27
+ try {
28
+ const response = await fetch(this.apiUrl, {
29
+ method: 'POST',
30
+ headers: {
31
+ 'Content-Type': 'application/json',
32
+ 'Authorization': `Bearer ${this.apiKey}`,
33
+ 'HTTP-Referer': window.location.origin,
34
+ 'X-Title': 'Cloze Reader'
35
+ },
36
+ body: JSON.stringify({
37
+ model: this.model,
38
+ messages: [{
39
+ role: 'user',
40
+ content: `In this passage: "${passage}"
41
+
42
+ The word "${wordToReplace}" has been replaced with a blank. Give me a helpful hint about what word fits here, considering the context: "${context}".
43
+
44
+ Provide a brief, educational hint that helps understand the word without giving it away directly.`
45
+ }],
46
+ max_tokens: 150,
47
+ temperature: 0.7
48
+ })
49
+ });
50
+
51
+ if (!response.ok) {
52
+ throw new Error(`API request failed: ${response.status}`);
53
+ }
54
+
55
+ const data = await response.json();
56
+ return data.choices[0].message.content.trim();
57
+ } catch (error) {
58
+ console.error('Error getting contextual hint:', error);
59
+ return 'Unable to generate hint at this time';
60
+ }
61
+ }
62
+
63
+ async getContextualization(title, author, passage) {
64
+ if (!this.apiKey) {
65
+ return `📚 Practice with classic literature from ${author}'s "${title}"`;
66
+ }
67
+
68
+ try {
69
+ const response = await fetch(this.apiUrl, {
70
+ method: 'POST',
71
+ headers: {
72
+ 'Content-Type': 'application/json',
73
+ 'Authorization': `Bearer ${this.apiKey}`,
74
+ 'HTTP-Referer': window.location.origin,
75
+ 'X-Title': 'Cloze Reader'
76
+ },
77
+ body: JSON.stringify({
78
+ model: this.model,
79
+ messages: [{
80
+ role: 'system',
81
+ 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.'
82
+ }, {
83
+ role: 'user',
84
+ content: `Provide educational context for this passage from "${title}" by ${author}: "${passage}"`
85
+ }],
86
+ max_tokens: 100,
87
+ temperature: 0.3
88
+ })
89
+ });
90
+
91
+ if (!response.ok) {
92
+ throw new Error(`API request failed: ${response.status}`);
93
+ }
94
+
95
+ const data = await response.json();
96
+ return data.choices[0].message.content.trim();
97
+ } catch (error) {
98
+ console.error('Error getting contextualization:', error);
99
+ return `📚 Practice with classic literature from ${author}'s "${title}"`;
100
+ }
101
+ }
102
+ }
103
+
104
+ export { OpenRouterService as AIService };