@zemerik/gemini-assist 1.1.1-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +35 -0
- package/.github/ISSUE_TEMPLATE/question.md +29 -0
- package/.github/dependabot.yml +49 -0
- package/.github/labels.yml +129 -0
- package/.github/pull_request_template.md +46 -0
- package/.github/stale.yml +25 -0
- package/.github/workflows/ci.yml +192 -0
- package/.github/workflows/release.yml +71 -0
- package/.rustignore +5 -0
- package/Cargo.toml +19 -0
- package/LICENSE +0 -0
- package/README.md +220 -0
- package/README_RUST.md +134 -0
- package/RELEASE_NOTES.md +124 -0
- package/bin/gemini-assist.js +136 -0
- package/build.rs +3 -0
- package/gitignore +1 -0
- package/index.d.ts +13 -0
- package/index.js +317 -0
- package/napi.config.json +33 -0
- package/package.json +63 -0
- package/scripts/postinstall.js +11 -0
- package/src/gemini.js +127 -0
- package/src/gemini_client.rs +159 -0
- package/src/lib.rs +58 -0
- package/src/rust/gemini_client.rs +157 -0
- package/src/rust/lib.rs +57 -0
- package/src/utils.js +62 -0
package/index.js
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/* prettier-ignore */
|
|
4
|
+
|
|
5
|
+
/* auto-generated by NAPI-RS */
|
|
6
|
+
|
|
7
|
+
const { existsSync, readFileSync } = require('fs')
|
|
8
|
+
const { join } = require('path')
|
|
9
|
+
|
|
10
|
+
const { platform, arch } = process
|
|
11
|
+
|
|
12
|
+
let nativeBinding = null
|
|
13
|
+
let localFileExisted = false
|
|
14
|
+
let loadError = null
|
|
15
|
+
|
|
16
|
+
function isMusl() {
|
|
17
|
+
// For Node 10
|
|
18
|
+
if (!process.report || typeof process.report.getReport !== 'function') {
|
|
19
|
+
try {
|
|
20
|
+
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
|
21
|
+
return readFileSync(lddPath, 'utf8').includes('musl')
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
const { glibcVersionRuntime } = process.report.getReport().header
|
|
27
|
+
return !glibcVersionRuntime
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
switch (platform) {
|
|
32
|
+
case 'android':
|
|
33
|
+
switch (arch) {
|
|
34
|
+
case 'arm64':
|
|
35
|
+
localFileExisted = existsSync(join(__dirname, 'gemini-assist-native.android-arm64.node'))
|
|
36
|
+
try {
|
|
37
|
+
if (localFileExisted) {
|
|
38
|
+
nativeBinding = require('./gemini-assist-native.android-arm64.node')
|
|
39
|
+
} else {
|
|
40
|
+
nativeBinding = require('@zemerik/gemini-assist-android-arm64')
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
loadError = e
|
|
44
|
+
}
|
|
45
|
+
break
|
|
46
|
+
case 'arm':
|
|
47
|
+
localFileExisted = existsSync(join(__dirname, 'gemini-assist-native.android-arm-eabi.node'))
|
|
48
|
+
try {
|
|
49
|
+
if (localFileExisted) {
|
|
50
|
+
nativeBinding = require('./gemini-assist-native.android-arm-eabi.node')
|
|
51
|
+
} else {
|
|
52
|
+
nativeBinding = require('@zemerik/gemini-assist-android-arm-eabi')
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
loadError = e
|
|
56
|
+
}
|
|
57
|
+
break
|
|
58
|
+
default:
|
|
59
|
+
throw new Error(`Unsupported architecture on Android ${arch}`)
|
|
60
|
+
}
|
|
61
|
+
break
|
|
62
|
+
case 'win32':
|
|
63
|
+
switch (arch) {
|
|
64
|
+
case 'x64':
|
|
65
|
+
localFileExisted = existsSync(
|
|
66
|
+
join(__dirname, 'gemini-assist-native.win32-x64-msvc.node')
|
|
67
|
+
)
|
|
68
|
+
try {
|
|
69
|
+
if (localFileExisted) {
|
|
70
|
+
nativeBinding = require('./gemini-assist-native.win32-x64-msvc.node')
|
|
71
|
+
} else {
|
|
72
|
+
nativeBinding = require('@zemerik/gemini-assist-win32-x64-msvc')
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
loadError = e
|
|
76
|
+
}
|
|
77
|
+
break
|
|
78
|
+
case 'ia32':
|
|
79
|
+
localFileExisted = existsSync(
|
|
80
|
+
join(__dirname, 'gemini-assist-native.win32-ia32-msvc.node')
|
|
81
|
+
)
|
|
82
|
+
try {
|
|
83
|
+
if (localFileExisted) {
|
|
84
|
+
nativeBinding = require('./gemini-assist-native.win32-ia32-msvc.node')
|
|
85
|
+
} else {
|
|
86
|
+
nativeBinding = require('@zemerik/gemini-assist-win32-ia32-msvc')
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
loadError = e
|
|
90
|
+
}
|
|
91
|
+
break
|
|
92
|
+
case 'arm64':
|
|
93
|
+
localFileExisted = existsSync(
|
|
94
|
+
join(__dirname, 'gemini-assist-native.win32-arm64-msvc.node')
|
|
95
|
+
)
|
|
96
|
+
try {
|
|
97
|
+
if (localFileExisted) {
|
|
98
|
+
nativeBinding = require('./gemini-assist-native.win32-arm64-msvc.node')
|
|
99
|
+
} else {
|
|
100
|
+
nativeBinding = require('@zemerik/gemini-assist-win32-arm64-msvc')
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
loadError = e
|
|
104
|
+
}
|
|
105
|
+
break
|
|
106
|
+
default:
|
|
107
|
+
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
|
108
|
+
}
|
|
109
|
+
break
|
|
110
|
+
case 'darwin':
|
|
111
|
+
localFileExisted = existsSync(join(__dirname, 'gemini-assist-native.darwin-universal.node'))
|
|
112
|
+
try {
|
|
113
|
+
if (localFileExisted) {
|
|
114
|
+
nativeBinding = require('./gemini-assist-native.darwin-universal.node')
|
|
115
|
+
} else {
|
|
116
|
+
nativeBinding = require('@zemerik/gemini-assist-darwin-universal')
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
} catch {}
|
|
120
|
+
switch (arch) {
|
|
121
|
+
case 'x64':
|
|
122
|
+
localFileExisted = existsSync(join(__dirname, 'gemini-assist-native.darwin-x64.node'))
|
|
123
|
+
try {
|
|
124
|
+
if (localFileExisted) {
|
|
125
|
+
nativeBinding = require('./gemini-assist-native.darwin-x64.node')
|
|
126
|
+
} else {
|
|
127
|
+
nativeBinding = require('@zemerik/gemini-assist-darwin-x64')
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
loadError = e
|
|
131
|
+
}
|
|
132
|
+
break
|
|
133
|
+
case 'arm64':
|
|
134
|
+
localFileExisted = existsSync(
|
|
135
|
+
join(__dirname, 'gemini-assist-native.darwin-arm64.node')
|
|
136
|
+
)
|
|
137
|
+
try {
|
|
138
|
+
if (localFileExisted) {
|
|
139
|
+
nativeBinding = require('./gemini-assist-native.darwin-arm64.node')
|
|
140
|
+
} else {
|
|
141
|
+
nativeBinding = require('@zemerik/gemini-assist-darwin-arm64')
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
loadError = e
|
|
145
|
+
}
|
|
146
|
+
break
|
|
147
|
+
default:
|
|
148
|
+
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
|
149
|
+
}
|
|
150
|
+
break
|
|
151
|
+
case 'freebsd':
|
|
152
|
+
if (arch !== 'x64') {
|
|
153
|
+
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
|
154
|
+
}
|
|
155
|
+
localFileExisted = existsSync(join(__dirname, 'gemini-assist-native.freebsd-x64.node'))
|
|
156
|
+
try {
|
|
157
|
+
if (localFileExisted) {
|
|
158
|
+
nativeBinding = require('./gemini-assist-native.freebsd-x64.node')
|
|
159
|
+
} else {
|
|
160
|
+
nativeBinding = require('@zemerik/gemini-assist-freebsd-x64')
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
loadError = e
|
|
164
|
+
}
|
|
165
|
+
break
|
|
166
|
+
case 'linux':
|
|
167
|
+
switch (arch) {
|
|
168
|
+
case 'x64':
|
|
169
|
+
if (isMusl()) {
|
|
170
|
+
localFileExisted = existsSync(
|
|
171
|
+
join(__dirname, 'gemini-assist-native.linux-x64-musl.node')
|
|
172
|
+
)
|
|
173
|
+
try {
|
|
174
|
+
if (localFileExisted) {
|
|
175
|
+
nativeBinding = require('./gemini-assist-native.linux-x64-musl.node')
|
|
176
|
+
} else {
|
|
177
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-x64-musl')
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
loadError = e
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
localFileExisted = existsSync(
|
|
184
|
+
join(__dirname, 'gemini-assist-native.linux-x64-gnu.node')
|
|
185
|
+
)
|
|
186
|
+
try {
|
|
187
|
+
if (localFileExisted) {
|
|
188
|
+
nativeBinding = require('./gemini-assist-native.linux-x64-gnu.node')
|
|
189
|
+
} else {
|
|
190
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-x64-gnu')
|
|
191
|
+
}
|
|
192
|
+
} catch (e) {
|
|
193
|
+
loadError = e
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
break
|
|
197
|
+
case 'arm64':
|
|
198
|
+
if (isMusl()) {
|
|
199
|
+
localFileExisted = existsSync(
|
|
200
|
+
join(__dirname, 'gemini-assist-native.linux-arm64-musl.node')
|
|
201
|
+
)
|
|
202
|
+
try {
|
|
203
|
+
if (localFileExisted) {
|
|
204
|
+
nativeBinding = require('./gemini-assist-native.linux-arm64-musl.node')
|
|
205
|
+
} else {
|
|
206
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-arm64-musl')
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
loadError = e
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
localFileExisted = existsSync(
|
|
213
|
+
join(__dirname, 'gemini-assist-native.linux-arm64-gnu.node')
|
|
214
|
+
)
|
|
215
|
+
try {
|
|
216
|
+
if (localFileExisted) {
|
|
217
|
+
nativeBinding = require('./gemini-assist-native.linux-arm64-gnu.node')
|
|
218
|
+
} else {
|
|
219
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-arm64-gnu')
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
loadError = e
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
break
|
|
226
|
+
case 'arm':
|
|
227
|
+
if (isMusl()) {
|
|
228
|
+
localFileExisted = existsSync(
|
|
229
|
+
join(__dirname, 'gemini-assist-native.linux-arm-musleabihf.node')
|
|
230
|
+
)
|
|
231
|
+
try {
|
|
232
|
+
if (localFileExisted) {
|
|
233
|
+
nativeBinding = require('./gemini-assist-native.linux-arm-musleabihf.node')
|
|
234
|
+
} else {
|
|
235
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-arm-musleabihf')
|
|
236
|
+
}
|
|
237
|
+
} catch (e) {
|
|
238
|
+
loadError = e
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
localFileExisted = existsSync(
|
|
242
|
+
join(__dirname, 'gemini-assist-native.linux-arm-gnueabihf.node')
|
|
243
|
+
)
|
|
244
|
+
try {
|
|
245
|
+
if (localFileExisted) {
|
|
246
|
+
nativeBinding = require('./gemini-assist-native.linux-arm-gnueabihf.node')
|
|
247
|
+
} else {
|
|
248
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-arm-gnueabihf')
|
|
249
|
+
}
|
|
250
|
+
} catch (e) {
|
|
251
|
+
loadError = e
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
break
|
|
255
|
+
case 'riscv64':
|
|
256
|
+
if (isMusl()) {
|
|
257
|
+
localFileExisted = existsSync(
|
|
258
|
+
join(__dirname, 'gemini-assist-native.linux-riscv64-musl.node')
|
|
259
|
+
)
|
|
260
|
+
try {
|
|
261
|
+
if (localFileExisted) {
|
|
262
|
+
nativeBinding = require('./gemini-assist-native.linux-riscv64-musl.node')
|
|
263
|
+
} else {
|
|
264
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-riscv64-musl')
|
|
265
|
+
}
|
|
266
|
+
} catch (e) {
|
|
267
|
+
loadError = e
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
localFileExisted = existsSync(
|
|
271
|
+
join(__dirname, 'gemini-assist-native.linux-riscv64-gnu.node')
|
|
272
|
+
)
|
|
273
|
+
try {
|
|
274
|
+
if (localFileExisted) {
|
|
275
|
+
nativeBinding = require('./gemini-assist-native.linux-riscv64-gnu.node')
|
|
276
|
+
} else {
|
|
277
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-riscv64-gnu')
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {
|
|
280
|
+
loadError = e
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
break
|
|
284
|
+
case 's390x':
|
|
285
|
+
localFileExisted = existsSync(
|
|
286
|
+
join(__dirname, 'gemini-assist-native.linux-s390x-gnu.node')
|
|
287
|
+
)
|
|
288
|
+
try {
|
|
289
|
+
if (localFileExisted) {
|
|
290
|
+
nativeBinding = require('./gemini-assist-native.linux-s390x-gnu.node')
|
|
291
|
+
} else {
|
|
292
|
+
nativeBinding = require('@zemerik/gemini-assist-linux-s390x-gnu')
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
loadError = e
|
|
296
|
+
}
|
|
297
|
+
break
|
|
298
|
+
default:
|
|
299
|
+
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
|
300
|
+
}
|
|
301
|
+
break
|
|
302
|
+
default:
|
|
303
|
+
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!nativeBinding) {
|
|
307
|
+
if (loadError) {
|
|
308
|
+
throw loadError
|
|
309
|
+
}
|
|
310
|
+
throw new Error(`Failed to load native binding`)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const { RustGeminiClient, validateApiKeyRust, rustVersion } = nativeBinding
|
|
314
|
+
|
|
315
|
+
module.exports.RustGeminiClient = RustGeminiClient
|
|
316
|
+
module.exports.validateApiKeyRust = validateApiKeyRust
|
|
317
|
+
module.exports.rustVersion = rustVersion
|
package/napi.config.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gemini-assist-native",
|
|
3
|
+
"main": "./index.node",
|
|
4
|
+
"binary": {
|
|
5
|
+
"napi-version": 8,
|
|
6
|
+
"packageName": "gemini-assist-native",
|
|
7
|
+
"path": "./",
|
|
8
|
+
"host": "https://github.com/yourusername/gemini-assist/releases/download/",
|
|
9
|
+
"remotePath": "{version}",
|
|
10
|
+
"arch": {
|
|
11
|
+
"x86_64": {
|
|
12
|
+
"libc": {
|
|
13
|
+
"musl": {
|
|
14
|
+
"filePrefix": "gemini-assist-native",
|
|
15
|
+
"fileSuffix": ".musl-x64.node"
|
|
16
|
+
},
|
|
17
|
+
"glibc": {
|
|
18
|
+
"filePrefix": "gemini-assist-native",
|
|
19
|
+
"fileSuffix": ".glibc-x64.node"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"default": {
|
|
23
|
+
"filePrefix": "gemini-assist-native",
|
|
24
|
+
"fileSuffix": "-x64.node"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"arm64": {
|
|
28
|
+
"filePrefix": "gemini-assist-native",
|
|
29
|
+
"fileSuffix": "-arm64.node"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zemerik/gemini-assist",
|
|
3
|
+
"version": "1.1.1-beta",
|
|
4
|
+
"description": "AI Assistant CLI tool powered by Google Gemini API (50% JS, 50% Rust)",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gemini-assist": "bin/gemini-assist.js",
|
|
8
|
+
"gassist": "bin/gemini-assist.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
|
+
"start": "node bin/gemini-assist.js",
|
|
13
|
+
"build": "napi build --platform --release",
|
|
14
|
+
"build:debug": "napi build --platform",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"install": "node scripts/postinstall.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"gemini",
|
|
20
|
+
"ai",
|
|
21
|
+
"assistant",
|
|
22
|
+
"cli",
|
|
23
|
+
"google",
|
|
24
|
+
"chatbot",
|
|
25
|
+
"rust",
|
|
26
|
+
"napi"
|
|
27
|
+
],
|
|
28
|
+
"author": "Hemang Yadav (Zemerik)",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@google/generative-ai": "^0.21.0",
|
|
32
|
+
"commander": "^11.1.0",
|
|
33
|
+
"dotenv": "^16.4.5",
|
|
34
|
+
"chalk": "^4.1.2",
|
|
35
|
+
"readline": "^1.3.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@napi-rs/cli": "^2.16.0"
|
|
39
|
+
},
|
|
40
|
+
"napi": {
|
|
41
|
+
"name": "gemini-assist-native",
|
|
42
|
+
"triples": {
|
|
43
|
+
"defaults": true,
|
|
44
|
+
"additional": [
|
|
45
|
+
"x86_64-apple-darwin",
|
|
46
|
+
"aarch64-apple-darwin",
|
|
47
|
+
"x86_64-pc-windows-msvc",
|
|
48
|
+
"aarch64-pc-windows-msvc",
|
|
49
|
+
"x86_64-unknown-linux-gnu",
|
|
50
|
+
"aarch64-unknown-linux-gnu",
|
|
51
|
+
"x86_64-unknown-linux-musl",
|
|
52
|
+
"aarch64-unknown-linux-musl"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=14.0.0"
|
|
58
|
+
},
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": ""
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const { existsSync } = require('fs');
|
|
2
|
+
const { join } = require('path');
|
|
3
|
+
|
|
4
|
+
// Check if Rust build is available, if not, fallback to JS
|
|
5
|
+
const nativePath = join(__dirname, '..', 'index.node');
|
|
6
|
+
|
|
7
|
+
if (!existsSync(nativePath)) {
|
|
8
|
+
console.log('⚠ Rust native bindings not found. Building...');
|
|
9
|
+
console.log(' Run "npm run build" to compile Rust bindings for better performance.');
|
|
10
|
+
console.log(' The CLI will work with JavaScript fallback in the meantime.');
|
|
11
|
+
}
|
package/src/gemini.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Hybrid JS/Rust Implementation (50% JS, 50% Rust)
|
|
2
|
+
// Core Gemini API logic is handled by Rust (src/gemini_client.rs)
|
|
3
|
+
// This JS wrapper provides Node.js compatibility and fallback
|
|
4
|
+
|
|
5
|
+
let RustGeminiClient;
|
|
6
|
+
|
|
7
|
+
// Try to load Rust native bindings, fallback to JS implementation if not available
|
|
8
|
+
try {
|
|
9
|
+
const native = require('../index.node');
|
|
10
|
+
RustGeminiClient = native.RustGeminiClient;
|
|
11
|
+
} catch (error) {
|
|
12
|
+
// Fallback to JS implementation if Rust bindings not available
|
|
13
|
+
const { GoogleGenerativeAI } = require('@google/generative-ai');
|
|
14
|
+
// Only show fallback message once, not on every require
|
|
15
|
+
if (!global.__gemini_fallback_shown) {
|
|
16
|
+
console.log('⚠ Rust bindings not available, using JavaScript fallback');
|
|
17
|
+
global.__gemini_fallback_shown = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class JSGeminiClient {
|
|
21
|
+
constructor(apiKey, options = {}) {
|
|
22
|
+
this.apiKey = apiKey;
|
|
23
|
+
// Default to undefined to let SDK use its default, or use provided model
|
|
24
|
+
this.modelName = options.model;
|
|
25
|
+
this.temperature = options.temperature || 0.7;
|
|
26
|
+
this.genAI = new GoogleGenerativeAI(apiKey);
|
|
27
|
+
|
|
28
|
+
// Build model config - SDK requires a model name
|
|
29
|
+
// Default to gemini-2.5-flash
|
|
30
|
+
let modelToUse = this.modelName || 'gemini-2.5-flash';
|
|
31
|
+
|
|
32
|
+
const modelConfig = {
|
|
33
|
+
model: modelToUse,
|
|
34
|
+
generationConfig: {
|
|
35
|
+
temperature: this.temperature,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
this.model = this.genAI.getGenerativeModel(modelConfig);
|
|
40
|
+
|
|
41
|
+
// Store actual model name being used
|
|
42
|
+
this.modelName = modelToUse;
|
|
43
|
+
|
|
44
|
+
this.chatHistory = [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async chat(prompt) {
|
|
48
|
+
try {
|
|
49
|
+
this.chatHistory.push({
|
|
50
|
+
role: 'user',
|
|
51
|
+
parts: [{ text: prompt }]
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const result = await this.model.generateContent(prompt);
|
|
55
|
+
const response = await result.response;
|
|
56
|
+
const text = response.text();
|
|
57
|
+
|
|
58
|
+
this.chatHistory.push({
|
|
59
|
+
role: 'model',
|
|
60
|
+
parts: [{ text: text }]
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return text;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error.message.includes('API_KEY')) {
|
|
66
|
+
throw new Error('Invalid API key. Please check your GEMINI_API_KEY.');
|
|
67
|
+
} else if (error.message.includes('quota') || error.message.includes('rate limit')) {
|
|
68
|
+
throw new Error('API quota exceeded or rate limit reached. Please try again later.');
|
|
69
|
+
} else if (error.message.includes('safety')) {
|
|
70
|
+
throw new Error('Content was blocked by safety filters.');
|
|
71
|
+
} else if (error.message.includes('404') || error.message.includes('not found')) {
|
|
72
|
+
// Provide helpful suggestions for model issues
|
|
73
|
+
let suggestions = [
|
|
74
|
+
'Try: --model gemini-1.5-flash (without version suffix)',
|
|
75
|
+
'Try: --model gemini-1.5-pro',
|
|
76
|
+
'Try: --model gemini-pro (legacy)',
|
|
77
|
+
'Check your API key has access to this model',
|
|
78
|
+
'View available models: https://ai.google.dev/models/gemini'
|
|
79
|
+
].join('\n ');
|
|
80
|
+
throw new Error(`Model "${this.modelName}" not found or not available.\n\nSuggestions:\n ${suggestions}\n\nFull error: ${error.message}`);
|
|
81
|
+
} else {
|
|
82
|
+
throw new Error(`Gemini API error: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
clearHistory() {
|
|
88
|
+
this.chatHistory = [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async getHistoryCount() {
|
|
92
|
+
return this.chatHistory.length;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
RustGeminiClient = JSGeminiClient;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Wrapper class that uses Rust implementation
|
|
100
|
+
class GeminiClient {
|
|
101
|
+
constructor(apiKey, options = {}) {
|
|
102
|
+
// Default to gemini-2.5-flash
|
|
103
|
+
this.modelName = options.model || 'gemini-2.5-flash';
|
|
104
|
+
this.temperature = options.temperature || 0.7;
|
|
105
|
+
|
|
106
|
+
// Use Rust client (or JS fallback)
|
|
107
|
+
this.client = new RustGeminiClient(
|
|
108
|
+
apiKey,
|
|
109
|
+
this.modelName,
|
|
110
|
+
this.temperature
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async chat(prompt) {
|
|
115
|
+
return await this.client.chat(prompt);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async clearHistory() {
|
|
119
|
+
return await this.client.clearHistory();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getHistoryCount() {
|
|
123
|
+
return await this.client.getHistoryCount();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = { GeminiClient };
|