1
1
import log from 'electron-log' ;
2
- import { GoogleGenerativeAI , HarmBlockThreshold , HarmCategory } from '@google/generative-ai' ;
2
+ import {
3
+ GoogleGenerativeAI ,
4
+ HarmBlockThreshold ,
5
+ HarmCategory ,
6
+ } from '@google/generative-ai' ;
3
7
import { SettingsService } from './SettingsService' ;
4
8
import { SettingsEnum } from '../models/SettingsEnum' ;
5
9
import { GenerationService } from './GenerationService' ;
@@ -8,24 +12,11 @@ import { OpenAICompatibleRequest } from '../models/OpenAICompatibleRequest';
8
12
import { AIModel } from '../models/AIModel' ;
9
13
import { fetchWithRetries } from '../util/fetchWithRetries' ;
10
14
import { getRandomItem } from '../util/getRandomItem' ;
11
-
12
- export class GeminiKeysNotSetError extends Error {
13
- constructor ( message : string ) {
14
- super ( message ) ;
15
- this . name = 'GeminiKeysNotSetError' ;
16
- }
17
- }
18
-
19
- export class GeminiAPIError extends Error {
20
- constructor ( public status : number , message : string ) {
21
- super ( message ) ;
22
- this . name = 'GeminiAPIError' ;
23
- }
24
- }
15
+ import { GeminiKeysNotSetError } from '../exceptions/GeminiKeyNotSetError' ;
16
+ import { GeminiAPIError } from '../exceptions/GeminiAPIError' ;
25
17
26
18
export class GeminiService implements GenerationService {
27
19
private readonly settingsService : SettingsService ;
28
- private genAI ?: GoogleGenerativeAI ;
29
20
30
21
constructor ( settingsService : SettingsService ) {
31
22
this . settingsService = settingsService ;
@@ -40,11 +31,18 @@ export class GeminiService implements GenerationService {
40
31
}
41
32
42
33
getGeminiKeys ( ) : string [ ] {
43
- const keysString = this . settingsService . get ( SettingsEnum . GEMINI_KEYS ) as string ;
34
+ const keysString = this . settingsService . get (
35
+ SettingsEnum . GEMINI_KEYS
36
+ ) as string ;
44
37
if ( ! keysString || keysString . trim ( ) === '' ) {
45
- throw new GeminiKeysNotSetError ( 'No Gemini API keys set. Please configure them in settings (e.g., key1,key2,key3).' ) ;
38
+ throw new GeminiKeysNotSetError (
39
+ 'No Gemini API keys set. Please configure them in settings (e.g., key1,key2,key3).'
40
+ ) ;
46
41
}
47
- return keysString . split ( ',' ) . map ( key => key . trim ( ) ) . filter ( key => key . length > 0 ) ;
42
+ return keysString
43
+ . split ( ',' )
44
+ . map ( ( key ) => key . trim ( ) )
45
+ . filter ( ( key ) => key . length > 0 ) ;
48
46
}
49
47
50
48
private getGenAIClient ( ) : GoogleGenerativeAI {
@@ -54,23 +52,39 @@ export class GeminiService implements GenerationService {
54
52
}
55
53
56
54
private readonly safetySettings = [
57
- { category : HarmCategory . HARM_CATEGORY_HARASSMENT , threshold : HarmBlockThreshold . BLOCK_NONE } ,
58
- { category : HarmCategory . HARM_CATEGORY_HATE_SPEECH , threshold : HarmBlockThreshold . BLOCK_NONE } ,
59
- { category : HarmCategory . HARM_CATEGORY_SEXUALLY_EXPLICIT , threshold : HarmBlockThreshold . BLOCK_NONE } ,
60
- { category : HarmCategory . HARM_CATEGORY_DANGEROUS_CONTENT , threshold : HarmBlockThreshold . BLOCK_NONE } ,
55
+ {
56
+ category : HarmCategory . HARM_CATEGORY_HARASSMENT ,
57
+ threshold : HarmBlockThreshold . BLOCK_NONE ,
58
+ } ,
59
+ {
60
+ category : HarmCategory . HARM_CATEGORY_HATE_SPEECH ,
61
+ threshold : HarmBlockThreshold . BLOCK_NONE ,
62
+ } ,
63
+ {
64
+ category : HarmCategory . HARM_CATEGORY_SEXUALLY_EXPLICIT ,
65
+ threshold : HarmBlockThreshold . BLOCK_NONE ,
66
+ } ,
67
+ {
68
+ category : HarmCategory . HARM_CATEGORY_DANGEROUS_CONTENT ,
69
+ threshold : HarmBlockThreshold . BLOCK_NONE ,
70
+ } ,
61
71
] ;
62
72
63
- async sentientSimsGenerate ( request : OpenAICompatibleRequest , retries : number = 3 ) : Promise < SimsGenerateResponse > {
73
+ async sentientSimsGenerate (
74
+ request : OpenAICompatibleRequest ,
75
+ retries : number = 3
76
+ ) : Promise < SimsGenerateResponse > {
64
77
const genAI = this . getGenAIClient ( ) ;
65
78
const model = genAI . getGenerativeModel ( {
66
79
model : this . getGeminiModel ( ) ,
67
80
safetySettings : this . safetySettings ,
68
- systemInstruction : request . messages . find ( msg => msg . role === 'system' ) ?. content ,
81
+ systemInstruction : request . messages . find ( ( msg ) => msg . role === 'system' )
82
+ ?. content ,
69
83
} ) ;
70
84
71
85
const contents = request . messages
72
- . filter ( msg => msg . role !== 'system' )
73
- . map ( msg => ( {
86
+ . filter ( ( msg ) => msg . role !== 'system' )
87
+ . map ( ( msg ) => ( {
74
88
role : msg . role === 'assistant' ? 'model' : 'user' ,
75
89
parts : [ { text : msg . content } ] ,
76
90
} ) ) ;
@@ -87,54 +101,76 @@ export class GeminiService implements GenerationService {
87
101
} ;
88
102
log . debug ( `Full Gemini Request: ${ JSON . stringify ( fullRequest , null , 2 ) } ` ) ;
89
103
90
- let text : string ;
91
- for ( let attempt = 1 ; attempt <= retries ; attempt ++ ) {
104
+ let text : string | undefined ;
105
+ for ( let attempt = 0 ; attempt < retries ; attempt += 1 ) {
92
106
try {
107
+ // eslint-disable-next-line no-await-in-loop
93
108
const result = await model . generateContent ( fullRequest ) ;
94
109
text = result . response . text ( ) ;
95
110
log . debug ( `Gemini Response: ${ text } ` ) ;
96
111
break ;
97
112
} catch ( error : any ) {
98
- const status = error . status || ( error . response ?. status ) ;
99
113
const message = error . message || 'Unknown error' ;
100
- log . error ( `Gemini Error on attempt ${ attempt } /${ retries } : ${ message } ` , error ) ;
114
+ log . error (
115
+ `Gemini Error on attempt ${ attempt } /${ retries } : ${ message } ` ,
116
+ error
117
+ ) ;
101
118
if ( attempt === retries ) throw error ;
102
119
}
103
120
}
104
121
105
122
if ( this . settingsService . get ( SettingsEnum . LOCALIZATION_ENABLED ) && text ) {
106
- const language = this . settingsService . get ( SettingsEnum . LOCALIZATION_LANGUAGE ) ;
123
+ const language = this . settingsService . get (
124
+ SettingsEnum . LOCALIZATION_LANGUAGE
125
+ ) ;
107
126
if ( language ) {
108
- const translationGenAI = this . getGenAIClient ( ) ; // Случайный ключ для перевода
127
+ const translationGenAI = this . getGenAIClient ( ) ;
109
128
const translationModel = translationGenAI . getGenerativeModel ( {
110
- model : this . getGeminiModel ( ) , // Та же модель, что и для генерации
129
+ model : this . getGeminiModel ( ) ,
111
130
safetySettings : this . safetySettings ,
112
131
} ) ;
113
132
114
133
const translationRequest = {
115
- contents : [ {
116
- role : 'user' ,
117
- parts : [ { text : `Do not include any additional text like "Here is your translation" or other explanations—just the response itself in ${ language } . Translate this text to ${ language } : ${ text } ` } ] ,
118
- } ] ,
134
+ contents : [
135
+ {
136
+ role : 'user' ,
137
+ parts : [
138
+ {
139
+ text : `Do not include any additional text like "Here is your translation" or other explanations—just the response itself in ${ language } . Translate this text to ${ language } : ${ text } ` ,
140
+ } ,
141
+ ] ,
142
+ } ,
143
+ ] ,
119
144
generationConfig : {
120
145
maxOutputTokens : request . maxResponseTokens ,
121
146
temperature : 0.8 ,
122
147
topP : 0.9 ,
123
148
} ,
124
149
safetySettings : this . safetySettings ,
125
150
} ;
126
- log . debug ( `Gemini Translation Request: ${ JSON . stringify ( translationRequest , null , 2 ) } ` ) ;
127
-
128
- for ( let attempt = 1 ; attempt <= retries ; attempt ++ ) {
151
+ log . debug (
152
+ `Gemini Translation Request: ${ JSON . stringify (
153
+ translationRequest ,
154
+ null ,
155
+ 2
156
+ ) } `
157
+ ) ;
158
+
159
+ for ( let attempt = 0 ; attempt < retries ; attempt += 1 ) {
129
160
try {
130
- const translationResult = await translationModel . generateContent ( translationRequest ) ;
161
+ // eslint-disable-next-line no-await-in-loop
162
+ const translationResult = await translationModel . generateContent (
163
+ translationRequest
164
+ ) ;
131
165
text = translationResult . response . text ( ) ;
132
166
log . debug ( `Gemini Translated Response: ${ text } ` ) ;
133
167
break ;
134
168
} catch ( error : any ) {
135
- const status = error . status || ( error . response ?. status ) ;
136
169
const message = error . message || 'Unknown error' ;
137
- log . error ( `Gemini Translation Error on attempt ${ attempt } /${ retries } : ${ message } ` , error ) ;
170
+ log . error (
171
+ `Gemini Translation Error on attempt ${ attempt } /${ retries } : ${ message } ` ,
172
+ error
173
+ ) ;
138
174
if ( attempt === retries ) throw error ;
139
175
}
140
176
}
@@ -183,7 +219,10 @@ export class GeminiService implements GenerationService {
183
219
184
220
if ( ! response . ok ) {
185
221
const errorText = await response . text ( ) ;
186
- throw new GeminiAPIError ( response . status , `Failed to fetch Gemini models: ${ response . status } - ${ errorText } ` ) ;
222
+ throw new GeminiAPIError (
223
+ response . status ,
224
+ `Failed to fetch Gemini models: ${ response . status } - ${ errorText } `
225
+ ) ;
187
226
}
188
227
189
228
const data = await response . json ( ) ;
@@ -200,7 +239,10 @@ export class GeminiService implements GenerationService {
200
239
}
201
240
return [
202
241
{ name : 'gemini-1.5-flash' , displayName : 'Gemini 1.5 Flash' } ,
203
- { name : 'gemini-2.0-flash-exp' , displayName : 'Gemini 2.0 Flash Experimental' } ,
242
+ {
243
+ name : 'gemini-2.0-flash-exp' ,
244
+ displayName : 'Gemini 2.0 Flash Experimental' ,
245
+ } ,
204
246
] ;
205
247
}
206
248
}
0 commit comments