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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # ESLint Plugin: Firebase AI Logic
2
2
 
3
- Plugin ESLint oficial para **Firebase AI Logic** com 24 regras que detectam anti-padrões, imports obsoletos, configs inválidas e best practices.
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 'eslint-plugin-firebase-ai-logic';
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: ['firebase-ai-logic'],
37
- extends: ['plugin:firebase-ai-logic/recommended'],
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 'firebase/vertexai-preview';
57
+ import { getVertexAI } from "firebase/vertexai-preview";
58
58
 
59
59
  // ❌ ERRADO - Importando direto da Google Cloud
60
- import { VertexAI } from '@google-cloud/vertexai';
60
+ import { VertexAI } from "@google-cloud/vertexai";
61
61
 
62
62
  // ✅ CERTO - Use firebase/ai
63
- import { getAI, GoogleAIBackend } from 'firebase/ai';
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: 'gemini-2.5-pro'
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: 'gemini-3-flash-preview'
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: 'application/json',
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: 'application/json',
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: 'user',
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 24 Regras
130
+ ## 📚 Todas as 39 Regras
131
131
 
132
132
  ### Imports & Models (5 regras)
133
133
 
134
- | Regra | Descrição |
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` | 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) |
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 | 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
-
151
- ### Functions (3 regras)
152
-
153
- | Regra | Descrição |
154
- |-------|-----------|
155
- | `require-function-description` | Função precisa de descrição detalhada |
156
- | `require-function-response-handling` | Function calling requer loop completo |
157
- | `no-unsupported-function-params` | Alguns atributos não são suportados |
158
-
159
- ### Performance & Optimization (5 regras)
160
-
161
- | Regra | Descrição |
162
- |-------|-----------|
163
- | `prefer-batch-requests` | Agrupe requisições em Promise.all() |
164
- | `prefer-count-tokens` | Use countTokens() para prompts grandes |
165
- | `prefer-streaming-long-responses` | Streaming para respostas > 1000 chars |
166
- | `prefer-concise-property-names` | Nomes curtos economizam tokens |
167
- | `prefer-cloud-storage-large-files` | Use Cloud Storage pra arquivos > 10MB |
168
-
169
- ### Configuration (4 regras)
170
-
171
- | Regra | Descrição |
172
- |-------|-----------|
173
- | `require-backend` | Backend (GoogleAI ou VertexAI) é obrigatório |
174
- | `require-ai-before-model` | Crie AI antes de usar getGenerativeModel |
175
- | `require-app-check-production` | Use App Check em produção |
176
- | `require-error-handling` | Envolva chamadas em try/catch |
177
-
178
- ### Best Practices (3 regras)
179
-
180
- | Regra | Descrição |
181
- |-------|-----------|
182
- | `no-thinking-simple-tasks` | Thinking mode é overhead pra tarefas simples |
183
- | `no-unlimited-chat-history` | Limpe histórico do chat regularmente |
184
- | `no-verbose-prompts` | Prompts verbosos = mais tokens e custo |
185
- | `require-thought-signature` | Gemini 3: preserve thoughtSignature |
186
- | `no-sensitive-system-instruction` | Não coloque dados sensíveis no system prompt |
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 'eslint-plugin-firebase-ai-logic';
207
+ import firebaseAiLogicPlugin from "eslint-plugin-firebase-ai-logic";
197
208
 
198
209
  export default [
199
210
  {
200
- plugins: { 'firebase-ai-logic': firebaseAiLogicPlugin },
211
+ plugins: { "firebase-ai-logic": firebaseAiLogicPlugin },
201
212
  rules: {
202
- 'firebase-ai-logic/no-deprecated-models': 'error',
203
- 'firebase-ai-logic/require-backend': 'error',
204
- 'firebase-ai-logic/require-json-validation': 'warn',
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
- 'firebase-ai-logic/no-verbose-prompts': 'off',
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: ['firebase-ai-logic'],
226
+ plugins: ["firebase-ai-logic"],
216
227
  rules: {
217
- 'firebase-ai-logic/no-deprecated-models': 'error',
218
- 'firebase-ai-logic/require-backend': 'error',
219
- 'firebase-ai-logic/require-json-validation': 'warn',
220
- 'firebase-ai-logic/no-verbose-prompts': 'off',
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 'firebase/app';
253
- import { getAI, getGenerativeModel, GoogleAIBackend } from 'firebase/ai';
254
- import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';
255
- import { z } from 'zod';
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('YOUR_SITE_KEY'),
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: 'gemini-3-flash-preview', // ✅ Modelo atual
269
- systemInstruction: 'Você é um assistente amigável',
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(['positive', 'negative', 'neutral']),
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('Prompt grande:', tokens.totalTokens);
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: 'application/json',
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 && 'status' in 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('Rate limited, aguarde antes de tentar novamente');
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: 'gemini-3-flash-preview',
314
- tools: [{
315
- name: 'search_web',
316
- description: 'Busca na web por informações recentes', // ✅ Descrição clara
317
- parameters: {
318
- type: 'object',
319
- properties: {
320
- query: { type: 'string' },
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: 'user',
345
- parts: [{
346
- functionResponse: {
347
- name: call.name,
348
- response: result,
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('Erro em function calling:', 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 'user' | 'model',
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 'eslint-plugin-firebase-ai-logic';
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: ['plugin:firebase-ai-logic/recommended'],
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.0.0
538
+ **Versão:** 1.1.0
515
539
  **NPM:** https://www.npmjs.com/package/eslint-plugin-firebase-ai-logic
package/dist/index.js CHANGED
@@ -239,7 +239,7 @@ const configs = {
239
239
  const plugin = {
240
240
  meta: {
241
241
  name: 'eslint-plugin-firebase-ai-logic',
242
- version: '1.0.0',
242
+ version: '1.3.0',
243
243
  },
244
244
  rules,
245
245
  configs,
@@ -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;AAOnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UA6DhB,CAAC;AAEF,eAAe,IAAI,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: 'problem',
24
+ type: 'suggestion',
7
25
  docs: {
8
- description: 'Enforce default temperature (1.0) for Gemini 3 models to prevent unexpected behavior',
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: 'Gemini 3 models are optimized for the default temperature (1.0). Changing this value may lead to unexpected behavior, looping, or degraded reasoning performance.',
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, report
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
- if (temperatureProp.value.value !== 1.0) {
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,4DAIiC;AAEjC,MAAM,IAAI,GAAoB;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,sFAAsF;YACxF,WAAW,EAAE,IAAI;YACjB,GAAG,EAAE,wFAAwF;SAC9F;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,qBAAqB,EACnB,mKAAmK;SACtK;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,iCAAiC;gBACjC,MAAM,eAAe,GAAG,IAAA,6BAAY,EAAC,aAAa,EAAE,aAAa,CAAC,CAAC;gBAEnE,IAAI,eAAe,EAAE,CAAC;oBACpB,0CAA0C;oBAC1C,IACE,eAAe,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;wBACxC,OAAO,eAAe,CAAC,KAAK,CAAC,KAAK,KAAK,QAAQ,EAC/C,CAAC;wBACD,IAAI,eAAe,CAAC,KAAK,CAAC,KAAK,KAAK,GAAG,EAAE,CAAC;4BACxC,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
+ {"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,UAmEhB,CAAC;AAEF,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"}