eslint-plugin-firebase-ai-logic 1.1.0 → 1.3.0
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/README.md +134 -110
- package/dist/index.js +1 -1
- package/dist/rules/check-temperature-defaults.d.ts +18 -0
- package/dist/rules/check-temperature-defaults.d.ts.map +1 -1
- package/dist/rules/check-temperature-defaults.js +40 -5
- package/dist/rules/check-temperature-defaults.js.map +1 -1
- package/dist/rules/require-app-check-production.d.ts +17 -0
- package/dist/rules/require-app-check-production.d.ts.map +1 -1
- package/dist/rules/require-app-check-production.js +58 -9
- package/dist/rules/require-app-check-production.js.map +1 -1
- package/dist/rules/require-backend.d.ts +17 -0
- package/dist/rules/require-backend.d.ts.map +1 -1
- package/dist/rules/require-backend.js +35 -42
- package/dist/rules/require-backend.js.map +1 -1
- package/dist/rules/require-error-handling.d.ts +21 -0
- package/dist/rules/require-error-handling.d.ts.map +1 -1
- package/dist/rules/require-error-handling.js +63 -3
- package/dist/rules/require-error-handling.js.map +1 -1
- package/dist/rules/validate-schema-structure.d.ts +16 -0
- package/dist/rules/validate-schema-structure.d.ts.map +1 -1
- package/dist/rules/validate-schema-structure.js +33 -20
- package/dist/rules/validate-schema-structure.js.map +1 -1
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +12 -5
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ESLint Plugin: Firebase AI Logic
|
|
2
2
|
|
|
3
|
-
Plugin ESLint oficial para **Firebase AI Logic** com
|
|
3
|
+
Plugin ESLint oficial para **Firebase AI Logic** com 39 regras que detectam anti-padrões, imports obsoletos, configs inválidas e best practices.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install --save-dev eslint-plugin-firebase-ai-logic
|
|
@@ -19,7 +19,7 @@ npm install --save-dev eslint-plugin-firebase-ai-logic
|
|
|
19
19
|
Edite `eslint.config.js`:
|
|
20
20
|
|
|
21
21
|
```javascript
|
|
22
|
-
import firebaseAiLogicPlugin from
|
|
22
|
+
import firebaseAiLogicPlugin from "eslint-plugin-firebase-ai-logic";
|
|
23
23
|
|
|
24
24
|
export default [
|
|
25
25
|
firebaseAiLogicPlugin.configs.recommended,
|
|
@@ -33,8 +33,8 @@ Edite `.eslintrc.js` ou `.eslintrc.json`:
|
|
|
33
33
|
|
|
34
34
|
```javascript
|
|
35
35
|
module.exports = {
|
|
36
|
-
plugins: [
|
|
37
|
-
extends: [
|
|
36
|
+
plugins: ["firebase-ai-logic"],
|
|
37
|
+
extends: ["plugin:firebase-ai-logic/recommended"],
|
|
38
38
|
};
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -54,13 +54,13 @@ Pronto! O plugin agora vai detectar problemas no seu código Firebase AI Logic.
|
|
|
54
54
|
|
|
55
55
|
```typescript
|
|
56
56
|
// ❌ ERRADO - Usando import antigo
|
|
57
|
-
import { getVertexAI } from
|
|
57
|
+
import { getVertexAI } from "firebase/vertexai-preview";
|
|
58
58
|
|
|
59
59
|
// ❌ ERRADO - Importando direto da Google Cloud
|
|
60
|
-
import { VertexAI } from
|
|
60
|
+
import { VertexAI } from "@google-cloud/vertexai";
|
|
61
61
|
|
|
62
62
|
// ✅ CERTO - Use firebase/ai
|
|
63
|
-
import { getAI, GoogleAIBackend } from
|
|
63
|
+
import { getAI, GoogleAIBackend } from "firebase/ai";
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
### ❌ Modelo Obsoleto
|
|
@@ -68,12 +68,12 @@ import { getAI, GoogleAIBackend } from 'firebase/ai';
|
|
|
68
68
|
```typescript
|
|
69
69
|
// ❌ ERRADO - gemini-2.5-pro é deprecated
|
|
70
70
|
const model = getGenerativeModel(ai, {
|
|
71
|
-
model:
|
|
71
|
+
model: "gemini-2.5-pro",
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
// ✅ CERTO - Use gemini-3-flash-preview
|
|
75
75
|
const model = getGenerativeModel(ai, {
|
|
76
|
-
model:
|
|
76
|
+
model: "gemini-3-flash-preview",
|
|
77
77
|
});
|
|
78
78
|
```
|
|
79
79
|
|
|
@@ -82,13 +82,13 @@ const model = getGenerativeModel(ai, {
|
|
|
82
82
|
```typescript
|
|
83
83
|
// ❌ ERRADO - Não funciona junto
|
|
84
84
|
const result = await model.generateContentStream(prompt, {
|
|
85
|
-
responseMimeType:
|
|
85
|
+
responseMimeType: "application/json",
|
|
86
86
|
responseSchema: mySchema,
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
// ✅ CERTO - Use generateContent (sem stream)
|
|
90
90
|
const result = await model.generateContent(prompt, {
|
|
91
|
-
responseMimeType:
|
|
91
|
+
responseMimeType: "application/json",
|
|
92
92
|
responseSchema: mySchema,
|
|
93
93
|
});
|
|
94
94
|
```
|
|
@@ -101,7 +101,7 @@ const ai = getAI(app);
|
|
|
101
101
|
|
|
102
102
|
// ✅ CERTO - Sempre especifique o backend
|
|
103
103
|
const ai = getAI(app, {
|
|
104
|
-
backend: new GoogleAIBackend()
|
|
104
|
+
backend: new GoogleAIBackend(),
|
|
105
105
|
});
|
|
106
106
|
```
|
|
107
107
|
|
|
@@ -119,7 +119,7 @@ const calls = result.response.functionCalls();
|
|
|
119
119
|
for (const call of calls) {
|
|
120
120
|
const response = await executeFunction(call.name, call.args);
|
|
121
121
|
await chat.sendMessage({
|
|
122
|
-
role:
|
|
122
|
+
role: "user",
|
|
123
123
|
parts: [{ functionResponse: response }],
|
|
124
124
|
});
|
|
125
125
|
}
|
|
@@ -127,63 +127,74 @@ for (const call of calls) {
|
|
|
127
127
|
|
|
128
128
|
---
|
|
129
129
|
|
|
130
|
-
## 📚 Todas as
|
|
130
|
+
## 📚 Todas as 39 Regras
|
|
131
131
|
|
|
132
132
|
### Imports & Models (5 regras)
|
|
133
133
|
|
|
134
|
-
| Regra
|
|
135
|
-
|
|
134
|
+
| Regra | Descrição |
|
|
135
|
+
| --------------------------------- | ------------------------------------------------------- |
|
|
136
136
|
| `no-deprecated-firebase-vertexai` | Não use `firebase/vertexai-preview` → use `firebase/ai` |
|
|
137
|
-
| `no-vertexai-only-import`
|
|
138
|
-
| `no-vertex-ai-direct-import`
|
|
139
|
-
| `no-google-genai-import`
|
|
140
|
-
| `no-deprecated-models`
|
|
137
|
+
| `no-vertexai-only-import` | Não use `firebase/vertexai` → use `firebase/ai` |
|
|
138
|
+
| `no-vertex-ai-direct-import` | Não use `@google-cloud/vertexai` → use `firebase/ai` |
|
|
139
|
+
| `no-google-genai-import` | Não use `@google/generative-ai` → use `firebase/ai` |
|
|
140
|
+
| `no-deprecated-models` | Detecta modelos obsoletos (gemini-2.5-pro, etc) |
|
|
141
141
|
|
|
142
142
|
### Schema & Validation (4 regras)
|
|
143
143
|
|
|
144
|
-
| Regra
|
|
145
|
-
|
|
146
|
-
| `no-streaming-with-schema`
|
|
147
|
-
| `no-unsupported-schema-features` | union types, constraints não são suportados
|
|
148
|
-
| `no-schema-in-prompt`
|
|
149
|
-
| `require-json-validation`
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
|
156
|
-
|
|
|
157
|
-
| `
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
|
162
|
-
|
|
163
|
-
| `
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
|
172
|
-
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
|
181
|
-
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
|
|
186
|
-
|
|
144
|
+
| Regra | Descrição |
|
|
145
|
+
| -------------------------------- | --------------------------------------------------------- |
|
|
146
|
+
| `no-streaming-with-schema` | JSON schema NÃO funciona com streaming |
|
|
147
|
+
| `no-unsupported-schema-features` | union types, constraints não são suportados |
|
|
148
|
+
| `no-schema-in-prompt` | Remova instruções JSON do prompt se usar `responseSchema` |
|
|
149
|
+
| `require-json-validation` | Valide JSON parseado com Zod após receber |
|
|
150
|
+
| `validate-schema-structure` | Detecta erros estruturais no objeto de schema |
|
|
151
|
+
| `validate-response-mime-type` | Garante que `responseMimeType` é compatível com o schema |
|
|
152
|
+
|
|
153
|
+
### Functions & Code Execution (7 regras)
|
|
154
|
+
|
|
155
|
+
| Regra | Descrição |
|
|
156
|
+
| ------------------------------------ | ---------------------------------------------------------------------------- |
|
|
157
|
+
| `require-function-description` | Função precisa de descrição detalhada |
|
|
158
|
+
| `require-function-response-handling` | Function calling requer loop completo |
|
|
159
|
+
| `no-unsupported-function-params` | Alguns atributos não são suportados |
|
|
160
|
+
| `validate-code-execution-config` | Valida configuração de Code Execution |
|
|
161
|
+
| `require-code-execution-handling` | Code execution requer tratamento de `executableCode` / `codeExecutionResult` |
|
|
162
|
+
| `no-file-uri-with-code-execution` | Bloqueia `fileUri` em ferramentas com Code Execution |
|
|
163
|
+
| `no-code-execution-creative-tasks` | Sugere desativar Code Execution para tarefas puramente criativas |
|
|
164
|
+
|
|
165
|
+
### Performance, Cost & Limits (6 regras)
|
|
166
|
+
|
|
167
|
+
| Regra | Descrição |
|
|
168
|
+
| ---------------------------------- | ---------------------------------------- |
|
|
169
|
+
| `prefer-batch-requests` | Agrupe requisições em `Promise.all()` |
|
|
170
|
+
| `prefer-count-tokens` | Use `countTokens()` para prompts grandes |
|
|
171
|
+
| `prefer-streaming-long-responses` | Streaming para respostas > 1000 chars |
|
|
172
|
+
| `prefer-concise-property-names` | Nomes curtos economizam tokens |
|
|
173
|
+
| `prefer-cloud-storage-large-files` | Use Cloud Storage pra arquivos > 10MB |
|
|
174
|
+
| `no-unlimited-chat-history` | Limpe histórico do chat regularmente |
|
|
175
|
+
|
|
176
|
+
### Security & Compliance (5 regras)
|
|
177
|
+
|
|
178
|
+
| Regra | Descrição |
|
|
179
|
+
| ----------------------------------------- | ------------------------------------------------------------ |
|
|
180
|
+
| `no-sensitive-system-instruction` | Não coloque dados sensíveis no system prompt |
|
|
181
|
+
| `require-app-check-production` | Use App Check em produção |
|
|
182
|
+
| `require-grounding-compliance` | Garante que Grounding segue regras de exibição e links |
|
|
183
|
+
| `require-google-ai-backend-for-grounding` | Google Search Grounding requer GoogleAIBackend |
|
|
184
|
+
| `validate-multimodal-config` | Valida segurança de arquivos multimídia (limites de tamanho) |
|
|
185
|
+
|
|
186
|
+
### Gemini 3 & Best Practices (8 regras)
|
|
187
|
+
|
|
188
|
+
| Regra | Descrição |
|
|
189
|
+
| ---------------------------- | --------------------------------------------------------- |
|
|
190
|
+
| `require-thought-signature` | Gemini 3: preserve `thoughtSignature` para coerência |
|
|
191
|
+
| `check-temperature-defaults` | Alertas sobre temperaturas extremas no Gemini 3 |
|
|
192
|
+
| `check-media-resolution` | Garante resolução compatível com as capacidades do modelo |
|
|
193
|
+
| `no-thinking-simple-tasks` | Thinking mode é overhead pra tarefas simples |
|
|
194
|
+
| `no-verbose-prompts` | Prompts verbosos = mais tokens e custo |
|
|
195
|
+
| `require-backend` | Backend (GoogleAI ou VertexAI) é obrigatório |
|
|
196
|
+
| `require-ai-before-model` | Crie AI antes de usar `getGenerativeModel` |
|
|
197
|
+
| `require-error-handling` | Envolva chamadas em try/catch |
|
|
187
198
|
|
|
188
199
|
---
|
|
189
200
|
|
|
@@ -193,17 +204,17 @@ for (const call of calls) {
|
|
|
193
204
|
|
|
194
205
|
```javascript
|
|
195
206
|
// ESLint 9+ (flat config)
|
|
196
|
-
import firebaseAiLogicPlugin from
|
|
207
|
+
import firebaseAiLogicPlugin from "eslint-plugin-firebase-ai-logic";
|
|
197
208
|
|
|
198
209
|
export default [
|
|
199
210
|
{
|
|
200
|
-
plugins: {
|
|
211
|
+
plugins: { "firebase-ai-logic": firebaseAiLogicPlugin },
|
|
201
212
|
rules: {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
213
|
+
"firebase-ai-logic/no-deprecated-models": "error",
|
|
214
|
+
"firebase-ai-logic/require-backend": "error",
|
|
215
|
+
"firebase-ai-logic/require-json-validation": "warn",
|
|
205
216
|
// desabilitar regra
|
|
206
|
-
|
|
217
|
+
"firebase-ai-logic/no-verbose-prompts": "off",
|
|
207
218
|
},
|
|
208
219
|
},
|
|
209
220
|
];
|
|
@@ -212,12 +223,12 @@ export default [
|
|
|
212
223
|
```javascript
|
|
213
224
|
// ESLint 8.x (legacy)
|
|
214
225
|
module.exports = {
|
|
215
|
-
plugins: [
|
|
226
|
+
plugins: ["firebase-ai-logic"],
|
|
216
227
|
rules: {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
228
|
+
"firebase-ai-logic/no-deprecated-models": "error",
|
|
229
|
+
"firebase-ai-logic/require-backend": "error",
|
|
230
|
+
"firebase-ai-logic/require-json-validation": "warn",
|
|
231
|
+
"firebase-ai-logic/no-verbose-prompts": "off",
|
|
221
232
|
},
|
|
222
233
|
};
|
|
223
234
|
```
|
|
@@ -225,6 +236,7 @@ module.exports = {
|
|
|
225
236
|
### Regras por Severity
|
|
226
237
|
|
|
227
238
|
**Error** (deve corrigir):
|
|
239
|
+
|
|
228
240
|
- Imports deprecated
|
|
229
241
|
- Modelos obsoletos
|
|
230
242
|
- Backend obrigatório
|
|
@@ -232,12 +244,14 @@ module.exports = {
|
|
|
232
244
|
- Schema com streaming
|
|
233
245
|
|
|
234
246
|
**Warning** (considere corrigir):
|
|
247
|
+
|
|
235
248
|
- Falta de validation JSON
|
|
236
249
|
- Sem error handling
|
|
237
250
|
- Historico de chat ilimitado
|
|
238
251
|
- Prompts verbose
|
|
239
252
|
|
|
240
253
|
**Suggestion** (dicas opcionais):
|
|
254
|
+
|
|
241
255
|
- Nomes de propriedades longos
|
|
242
256
|
- Sem countTokens
|
|
243
257
|
- Sem streaming em respostas longas
|
|
@@ -249,15 +263,15 @@ module.exports = {
|
|
|
249
263
|
### Setup Completo
|
|
250
264
|
|
|
251
265
|
```typescript
|
|
252
|
-
import { initializeApp } from
|
|
253
|
-
import { getAI, getGenerativeModel, GoogleAIBackend } from
|
|
254
|
-
import { initializeAppCheck, ReCaptchaV3Provider } from
|
|
255
|
-
import { z } from
|
|
266
|
+
import { initializeApp } from "firebase/app";
|
|
267
|
+
import { getAI, getGenerativeModel, GoogleAIBackend } from "firebase/ai";
|
|
268
|
+
import { initializeAppCheck, ReCaptchaV3Provider } from "firebase/app-check";
|
|
269
|
+
import { z } from "zod";
|
|
256
270
|
|
|
257
271
|
// ✅ App Check habilitado
|
|
258
272
|
const app = initializeApp(firebaseConfig);
|
|
259
273
|
initializeAppCheck(app, {
|
|
260
|
-
provider: new ReCaptchaV3Provider(
|
|
274
|
+
provider: new ReCaptchaV3Provider("YOUR_SITE_KEY"),
|
|
261
275
|
});
|
|
262
276
|
|
|
263
277
|
// ✅ AI com backend explícito
|
|
@@ -265,13 +279,13 @@ const ai = getAI(app, { backend: new GoogleAIBackend() });
|
|
|
265
279
|
|
|
266
280
|
// ✅ Model com config correto
|
|
267
281
|
const model = getGenerativeModel(ai, {
|
|
268
|
-
model:
|
|
269
|
-
systemInstruction:
|
|
282
|
+
model: "gemini-3-flash-preview", // ✅ Modelo atual
|
|
283
|
+
systemInstruction: "Você é um assistente amigável",
|
|
270
284
|
});
|
|
271
285
|
|
|
272
286
|
// ✅ Validação com Zod
|
|
273
287
|
const ResponseValidator = z.object({
|
|
274
|
-
sentiment: z.enum([
|
|
288
|
+
sentiment: z.enum(["positive", "negative", "neutral"]),
|
|
275
289
|
confidence: z.number().min(0).max(1),
|
|
276
290
|
});
|
|
277
291
|
|
|
@@ -281,12 +295,12 @@ async function analyzeReview(review: string) {
|
|
|
281
295
|
// ✅ countTokens para prompt grande
|
|
282
296
|
const tokens = await model.countTokens(review);
|
|
283
297
|
if (tokens.totalTokens > 5000) {
|
|
284
|
-
console.warn(
|
|
298
|
+
console.warn("Prompt grande:", tokens.totalTokens);
|
|
285
299
|
}
|
|
286
300
|
|
|
287
301
|
// ✅ generateContent (NÃO stream) para JSON
|
|
288
302
|
const result = await model.generateContent(review, {
|
|
289
|
-
responseMimeType:
|
|
303
|
+
responseMimeType: "application/json",
|
|
290
304
|
responseSchema: ReviewSchema,
|
|
291
305
|
});
|
|
292
306
|
|
|
@@ -294,10 +308,10 @@ async function analyzeReview(review: string) {
|
|
|
294
308
|
const data = JSON.parse(result.response.text());
|
|
295
309
|
return ResponseValidator.parse(data);
|
|
296
310
|
} catch (error) {
|
|
297
|
-
if (error instanceof Error &&
|
|
311
|
+
if (error instanceof Error && "status" in error) {
|
|
298
312
|
if ((error as any).status === 429) {
|
|
299
313
|
// Rate limit - implementar exponential backoff
|
|
300
|
-
console.error(
|
|
314
|
+
console.error("Rate limited, aguarde antes de tentar novamente");
|
|
301
315
|
}
|
|
302
316
|
}
|
|
303
317
|
throw error;
|
|
@@ -310,17 +324,19 @@ async function analyzeReview(review: string) {
|
|
|
310
324
|
```typescript
|
|
311
325
|
// ✅ Setup com tools
|
|
312
326
|
const model = getGenerativeModel(ai, {
|
|
313
|
-
model:
|
|
314
|
-
tools: [
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
327
|
+
model: "gemini-3-flash-preview",
|
|
328
|
+
tools: [
|
|
329
|
+
{
|
|
330
|
+
name: "search_web",
|
|
331
|
+
description: "Busca na web por informações recentes", // ✅ Descrição clara
|
|
332
|
+
parameters: {
|
|
333
|
+
type: "object",
|
|
334
|
+
properties: {
|
|
335
|
+
query: { type: "string" },
|
|
336
|
+
},
|
|
321
337
|
},
|
|
322
338
|
},
|
|
323
|
-
|
|
339
|
+
],
|
|
324
340
|
});
|
|
325
341
|
|
|
326
342
|
async function chatWithFunctionCalling(userMessage: string) {
|
|
@@ -341,20 +357,22 @@ async function chatWithFunctionCalling(userMessage: string) {
|
|
|
341
357
|
|
|
342
358
|
// ✅ IMPORTANTE: Enviar resposta de volta
|
|
343
359
|
response = await chat.sendMessage({
|
|
344
|
-
role:
|
|
345
|
-
parts: [
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
360
|
+
role: "user",
|
|
361
|
+
parts: [
|
|
362
|
+
{
|
|
363
|
+
functionResponse: {
|
|
364
|
+
name: call.name,
|
|
365
|
+
response: result,
|
|
366
|
+
},
|
|
349
367
|
},
|
|
350
|
-
|
|
368
|
+
],
|
|
351
369
|
});
|
|
352
370
|
}
|
|
353
371
|
}
|
|
354
372
|
|
|
355
373
|
return response.response.text();
|
|
356
374
|
} catch (error) {
|
|
357
|
-
console.error(
|
|
375
|
+
console.error("Erro em function calling:", error);
|
|
358
376
|
throw error;
|
|
359
377
|
}
|
|
360
378
|
}
|
|
@@ -367,14 +385,14 @@ async function chatWithFunctionCalling(userMessage: string) {
|
|
|
367
385
|
const MAX_HISTORY = 10;
|
|
368
386
|
|
|
369
387
|
async function maintainChatHistory(
|
|
370
|
-
messages: Array<{ role: string; text: string }
|
|
388
|
+
messages: Array<{ role: string; text: string }>,
|
|
371
389
|
) {
|
|
372
390
|
// ✅ Manter apenas últimas N mensagens
|
|
373
391
|
const recentMessages = messages.slice(-MAX_HISTORY);
|
|
374
392
|
|
|
375
393
|
const chat = model.startChat({
|
|
376
394
|
history: recentMessages.map((msg) => ({
|
|
377
|
-
role: msg.role as
|
|
395
|
+
role: msg.role as "user" | "model",
|
|
378
396
|
parts: [{ text: msg.text }],
|
|
379
397
|
})),
|
|
380
398
|
});
|
|
@@ -390,11 +408,13 @@ async function maintainChatHistory(
|
|
|
390
408
|
### "Plugin não está sendo carregado"
|
|
391
409
|
|
|
392
410
|
**Erro:**
|
|
411
|
+
|
|
393
412
|
```
|
|
394
413
|
Cannot find module 'eslint-plugin-firebase-ai-logic'
|
|
395
414
|
```
|
|
396
415
|
|
|
397
416
|
**Solução:**
|
|
417
|
+
|
|
398
418
|
```bash
|
|
399
419
|
# Verifique se está instalado
|
|
400
420
|
npm list eslint-plugin-firebase-ai-logic
|
|
@@ -409,11 +429,13 @@ import firebaseAiLogicPlugin from 'eslint-plugin-firebase-ai-logic';
|
|
|
409
429
|
### "Regra não reconhecida"
|
|
410
430
|
|
|
411
431
|
**Erro:**
|
|
432
|
+
|
|
412
433
|
```
|
|
413
434
|
"firebase-ai-logic/no-deprecated-models" was not found
|
|
414
435
|
```
|
|
415
436
|
|
|
416
437
|
**Solução:**
|
|
438
|
+
|
|
417
439
|
- Verifique o nome exato da regra
|
|
418
440
|
- Use com prefixo: `firebase-ai-logic/`
|
|
419
441
|
- Certifique-se que a regra está ativa na config
|
|
@@ -421,19 +443,19 @@ import firebaseAiLogicPlugin from 'eslint-plugin-firebase-ai-logic';
|
|
|
421
443
|
### "Config recommended não existe"
|
|
422
444
|
|
|
423
445
|
**Solução para ESLint 9:**
|
|
446
|
+
|
|
424
447
|
```javascript
|
|
425
448
|
// ✅ CERTO
|
|
426
|
-
import firebaseAiLogicPlugin from
|
|
427
|
-
export default [
|
|
428
|
-
firebaseAiLogicPlugin.configs.recommended,
|
|
429
|
-
];
|
|
449
|
+
import firebaseAiLogicPlugin from "eslint-plugin-firebase-ai-logic";
|
|
450
|
+
export default [firebaseAiLogicPlugin.configs.recommended];
|
|
430
451
|
```
|
|
431
452
|
|
|
432
453
|
**Solução para ESLint 8:**
|
|
454
|
+
|
|
433
455
|
```javascript
|
|
434
456
|
// ✅ CERTO
|
|
435
457
|
module.exports = {
|
|
436
|
-
extends: [
|
|
458
|
+
extends: ["plugin:firebase-ai-logic/recommended"],
|
|
437
459
|
};
|
|
438
460
|
```
|
|
439
461
|
|
|
@@ -446,6 +468,7 @@ Cada regra tem documentação detalhada no GitHub:
|
|
|
446
468
|
https://github.com/Just-mpm/eslint-plugin-firebase-ai-logic
|
|
447
469
|
|
|
448
470
|
Veja o diretório `docs/rules/` para:
|
|
471
|
+
|
|
449
472
|
- Exemplos de código bom e ruim
|
|
450
473
|
- Razão por trás da regra
|
|
451
474
|
- Como corrigir automaticamente
|
|
@@ -459,6 +482,7 @@ Abra uma issue no GitHub:
|
|
|
459
482
|
https://github.com/Just-mpm/eslint-plugin-firebase-ai-logic/issues
|
|
460
483
|
|
|
461
484
|
Inclua:
|
|
485
|
+
|
|
462
486
|
- Versão do plugin
|
|
463
487
|
- Versão do ESLint
|
|
464
488
|
- Configuração ESLint
|
|
@@ -511,5 +535,5 @@ npm run test:watch
|
|
|
511
535
|
|
|
512
536
|
Feito com ❤️ para developers Firebase AI Logic
|
|
513
537
|
|
|
514
|
-
**Versão:** 1.
|
|
538
|
+
**Versão:** 1.1.0
|
|
515
539
|
**NPM:** https://www.npmjs.com/package/eslint-plugin-firebase-ai-logic
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* check-temperature-defaults
|
|
4
|
+
*
|
|
5
|
+
* This rule suggests using the default temperature (1.0) for Gemini models,
|
|
6
|
+
* but with important exceptions:
|
|
7
|
+
*
|
|
8
|
+
* EXCEPTIONS (low temperature is valid and recommended):
|
|
9
|
+
* 1. Structured output (responseMimeType: 'application/json')
|
|
10
|
+
* - Low temperature (0.1-0.3) prevents hallucinations in extraction tasks
|
|
11
|
+
* 2. Classification tasks
|
|
12
|
+
* - Deterministic outputs benefit from low temperature
|
|
13
|
+
* 3. Data extraction
|
|
14
|
+
* - Precise extraction needs low temperature
|
|
15
|
+
*
|
|
16
|
+
* The rule only warns when:
|
|
17
|
+
* - Temperature is NOT 1.0
|
|
18
|
+
* - AND responseMimeType is NOT 'application/json' (structured output)
|
|
19
|
+
*/
|
|
2
20
|
declare const rule: Rule.RuleModule;
|
|
3
21
|
export default rule;
|
|
4
22
|
//# sourceMappingURL=check-temperature-defaults.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-temperature-defaults.d.ts","sourceRoot":"","sources":["../../src/rules/check-temperature-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"check-temperature-defaults.d.ts","sourceRoot":"","sources":["../../src/rules/check-temperature-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAQnC;;;;;;;;;;;;;;;;;GAiBG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAgFhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const ast_helpers_js_1 = require("../utils/ast-helpers.js");
|
|
4
|
+
/**
|
|
5
|
+
* check-temperature-defaults
|
|
6
|
+
*
|
|
7
|
+
* This rule suggests using the default temperature (1.0) for Gemini models,
|
|
8
|
+
* but with important exceptions:
|
|
9
|
+
*
|
|
10
|
+
* EXCEPTIONS (low temperature is valid and recommended):
|
|
11
|
+
* 1. Structured output (responseMimeType: 'application/json')
|
|
12
|
+
* - Low temperature (0.1-0.3) prevents hallucinations in extraction tasks
|
|
13
|
+
* 2. Classification tasks
|
|
14
|
+
* - Deterministic outputs benefit from low temperature
|
|
15
|
+
* 3. Data extraction
|
|
16
|
+
* - Precise extraction needs low temperature
|
|
17
|
+
*
|
|
18
|
+
* The rule only warns when:
|
|
19
|
+
* - Temperature is NOT 1.0
|
|
20
|
+
* - AND responseMimeType is NOT 'application/json' (structured output)
|
|
21
|
+
*/
|
|
4
22
|
const rule = {
|
|
5
23
|
meta: {
|
|
6
|
-
type: '
|
|
24
|
+
type: 'suggestion',
|
|
7
25
|
docs: {
|
|
8
|
-
description: '
|
|
26
|
+
description: 'Suggest default temperature (1.0) for Gemini models, except for structured output tasks',
|
|
9
27
|
recommended: true,
|
|
10
28
|
url: 'https://github.com/Just-mpm/eslint-plugin-firebase-ai-logic#check-temperature-defaults',
|
|
11
29
|
},
|
|
12
30
|
schema: [],
|
|
13
31
|
messages: {
|
|
14
|
-
nonDefaultTemperature: '
|
|
32
|
+
nonDefaultTemperature: 'Consider using the default temperature (1.0) for Gemini models. Lower values may cause looping or degraded reasoning. Exception: low temperature (0.1-0.3) is appropriate for structured output (JSON extraction) tasks.',
|
|
15
33
|
},
|
|
16
34
|
},
|
|
17
35
|
create(context) {
|
|
@@ -34,13 +52,30 @@ const rule = {
|
|
|
34
52
|
: configArg;
|
|
35
53
|
if (!(0, ast_helpers_js_1.isObjectExpression)(configToCheck))
|
|
36
54
|
return;
|
|
55
|
+
// Check for responseMimeType - if it's 'application/json', low temperature is valid
|
|
56
|
+
const responseMimeTypeProp = (0, ast_helpers_js_1.findProperty)(configToCheck, 'responseMimeType');
|
|
57
|
+
if (responseMimeTypeProp) {
|
|
58
|
+
const mimeType = (0, ast_helpers_js_1.getStringValue)(responseMimeTypeProp.value);
|
|
59
|
+
if (mimeType === 'application/json') {
|
|
60
|
+
// Structured output - low temperature is appropriate, don't warn
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Check for responseSchema - if present, it's structured output
|
|
65
|
+
const responseSchemaProp = (0, ast_helpers_js_1.findProperty)(configToCheck, 'responseSchema');
|
|
66
|
+
if (responseSchemaProp) {
|
|
67
|
+
// Has response schema - structured output, don't warn
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
37
70
|
// Check for temperature property
|
|
38
71
|
const temperatureProp = (0, ast_helpers_js_1.findProperty)(configToCheck, 'temperature');
|
|
39
72
|
if (temperatureProp) {
|
|
40
|
-
// If value is literal and not 1.0,
|
|
73
|
+
// If value is literal and not 1.0, suggest (not require) using default
|
|
41
74
|
if (temperatureProp.value.type === 'Literal' &&
|
|
42
75
|
typeof temperatureProp.value.value === 'number') {
|
|
43
|
-
|
|
76
|
+
const temp = temperatureProp.value.value;
|
|
77
|
+
// Only warn for non-default temperature when NOT doing structured output
|
|
78
|
+
if (temp !== 1.0) {
|
|
44
79
|
context.report({
|
|
45
80
|
node: temperatureProp,
|
|
46
81
|
messageId: 'nonDefaultTemperature',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-temperature-defaults.js","sourceRoot":"","sources":["../../src/rules/check-temperature-defaults.ts"],"names":[],"mappings":";;AACA,
|
|
1
|
+
{"version":3,"file":"check-temperature-defaults.js","sourceRoot":"","sources":["../../src/rules/check-temperature-defaults.ts"],"names":[],"mappings":";;AACA,4DAKiC;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,IAAI,GAAoB;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EACT,yFAAyF;YAC3F,WAAW,EAAE,IAAI;YACjB,GAAG,EAAE,wFAAwF;SAC9F;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,qBAAqB,EACnB,0NAA0N;SAC7N;KACF;IAED,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,cAAc,CAAC,IAAI;gBACjB,MAAM,UAAU,GAAG,IAAA,8BAAa,EAAC,IAAI,CAAC,CAAC;gBAEvC,oDAAoD;gBACpD,IACE,UAAU,KAAK,oBAAoB;oBACnC,UAAU,KAAK,iBAAiB,EAChC,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,+CAA+C;gBAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBACpC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAA,mCAAkB,EAAC,SAAS,CAAC;oBAAE,OAAO;gBAEzD,6BAA6B;gBAC7B,IAAI,gBAAgB,GAAG,IAAA,6BAAY,EAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;gBACnE,IAAI,aAAa,GAAG,gBAAgB;oBAClC,CAAC,CAAC,gBAAgB,CAAC,KAAK;oBACxB,CAAC,CAAC,SAAS,CAAC;gBAEd,IAAI,CAAC,IAAA,mCAAkB,EAAC,aAAa,CAAC;oBAAE,OAAO;gBAE/C,oFAAoF;gBACpF,MAAM,oBAAoB,GAAG,IAAA,6BAAY,EAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;gBAC7E,IAAI,oBAAoB,EAAE,CAAC;oBACzB,MAAM,QAAQ,GAAG,IAAA,+BAAc,EAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;oBAC5D,IAAI,QAAQ,KAAK,kBAAkB,EAAE,CAAC;wBACpC,iEAAiE;wBACjE,OAAO;oBACT,CAAC;gBACH,CAAC;gBAED,gEAAgE;gBAChE,MAAM,kBAAkB,GAAG,IAAA,6BAAY,EAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC;gBACzE,IAAI,kBAAkB,EAAE,CAAC;oBACvB,sDAAsD;oBACtD,OAAO;gBACT,CAAC;gBAED,iCAAiC;gBACjC,MAAM,eAAe,GAAG,IAAA,6BAAY,EAAC,aAAa,EAAE,aAAa,CAAC,CAAC;gBAEnE,IAAI,eAAe,EAAE,CAAC;oBACpB,uEAAuE;oBACvE,IACE,eAAe,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;wBACxC,OAAO,eAAe,CAAC,KAAK,CAAC,KAAK,KAAK,QAAQ,EAC/C,CAAC;wBACD,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC;wBACzC,yEAAyE;wBACzE,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;4BACjB,OAAO,CAAC,MAAM,CAAC;gCACb,IAAI,EAAE,eAAe;gCACrB,SAAS,EAAE,uBAAuB;6BACnC,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,kBAAe,IAAI,CAAC"}
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* require-app-check-production
|
|
4
|
+
*
|
|
5
|
+
* This rule suggests using App Check to protect AI endpoints from abuse.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT LIMITATIONS:
|
|
8
|
+
* - This rule only checks within the SAME FILE
|
|
9
|
+
* - Many projects use lazy loading where App Check is initialized in a separate file
|
|
10
|
+
* - Projects using hooks like useAppCheck() or ensureAppCheckReady() are valid
|
|
11
|
+
*
|
|
12
|
+
* The rule checks for:
|
|
13
|
+
* 1. Direct import of 'firebase/app-check'
|
|
14
|
+
* 2. Calls to initializeAppCheck()
|
|
15
|
+
* 3. Common patterns: useAppCheck, ensureAppCheckReady, AppCheckProvider
|
|
16
|
+
*
|
|
17
|
+
* If none are found in the same file as getAI(), it suggests (not requires) App Check.
|
|
18
|
+
*/
|
|
2
19
|
declare const rule: Rule.RuleModule;
|
|
3
20
|
export default rule;
|
|
4
21
|
//# sourceMappingURL=require-app-check-production.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"require-app-check-production.d.ts","sourceRoot":"","sources":["../../src/rules/require-app-check-production.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"require-app-check-production.d.ts","sourceRoot":"","sources":["../../src/rules/require-app-check-production.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAsGhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|