agents-config 1.0.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/AGENTS.md +490 -0
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/adapters/claude.template.md +77 -0
- package/adapters/codex.template.md +72 -0
- package/adapters/copilot.template.md +68 -0
- package/adapters/cursor.template.md +69 -0
- package/adapters/gemini.template.md +73 -0
- package/adapters/windsurf.template.md +81 -0
- package/bin/agents-init.js +699 -0
- package/bin/postinstall.js +28 -0
- package/instructions/development-standards.instructions.md +47 -0
- package/instructions/github-issue.instructions.md +324 -0
- package/instructions/github-release-notes.instructions.md +888 -0
- package/instructions/mui.instructions.md +50 -0
- package/instructions/storybook.instructions.md +55 -0
- package/instructions/web-interface-guidelines.instructions.md +331 -0
- package/package.json +56 -0
- package/prompts/create-pr.prompt.md +78 -0
- package/prompts/scaffold-component.prompt.md +57 -0
- package/rules/accessibility.md +36 -0
- package/rules/component-architecture.md +34 -0
- package/rules/gemini.md +547 -0
- package/rules/mui.md +491 -0
- package/rules/react-19-compiler.md +26 -0
- package/rules/spec-driven-development.md +36 -0
- package/rules/supabase.md +40 -0
- package/rules/tailwind-v4.md +29 -0
- package/rules/three-js-react.md +76 -0
- package/rules/web-performance.md +29 -0
- package/schemas/agents-project.schema.json +78 -0
- package/skills/accessibility-audit/SKILL.md +39 -0
- package/skills/integrate-gemini/SKILL.md +124 -0
- package/skills/scaffold-component/SKILL.md +77 -0
- package/skills/vercel-react-best-practices/AGENTS.md +2719 -0
- package/skills/vercel-react-best-practices/SKILL.md +125 -0
- package/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/skills/workflows/sdd-workflow.md +49 -0
- package/skills/workflows/setup-orchestration.md +18 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Standardized directory and file structure for React components in this workspace.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
**OBJECTIVE:**
|
|
6
|
+
Maintaining a consistent, scalable, and discoverable component architecture.
|
|
7
|
+
|
|
8
|
+
**REASON:**
|
|
9
|
+
As the project grows, a standardized folder structure prevents "file sprawl" and makes it easier to locate styles, tests, and assets related to a specific component.
|
|
10
|
+
|
|
11
|
+
**DESCRIPTION:**
|
|
12
|
+
The "Folder-per-Component" pattern used throughout the `src/components` directory.
|
|
13
|
+
|
|
14
|
+
**INSTRUCTIONS:**
|
|
15
|
+
|
|
16
|
+
### Directory Structure
|
|
17
|
+
- **Component Folders**: Every major component should live in its own folder under `src/components/`.
|
|
18
|
+
- **Primary File**: The main component file must be named `ComponentName.tsx` (PascalCase).
|
|
19
|
+
- **Associated Files**: Styles, local assets, and sub-components should reside within the same component folder.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
```
|
|
23
|
+
src/components/Hero/
|
|
24
|
+
├── Hero.tsx
|
|
25
|
+
├── Hero.css (if not using Tailwind)
|
|
26
|
+
└── HeroBackground.tsx (local child component)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Exports
|
|
30
|
+
- **Named Exports**: Prefer named exports for components to ensure better IDE support and tree-shaking.
|
|
31
|
+
- **Clear Boundaries**: Avoid importing local child components (e.g., `HeroBackground`) from outside their parent's folder. If a component is reused elsewhere, move it to the root of `src/components/`.
|
|
32
|
+
|
|
33
|
+
### Prop Types
|
|
34
|
+
- **Interfaces over Types**: Use `interface` for component props to allow for better extensibility and cleaner error messages.
|
package/rules/gemini.md
ADDED
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
# Google Gemini AI Integration Rules
|
|
2
|
+
|
|
3
|
+
**Purpose**: Architectural constraints, security requirements, and UX patterns for integrating Google Gemini API into React applications.
|
|
4
|
+
|
|
5
|
+
**Related Skill**: [integrate_gemini](../skills/integrate_gemini/SKILL.md)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## AI Integration Philosophy
|
|
10
|
+
|
|
11
|
+
### When to Use AI Features
|
|
12
|
+
|
|
13
|
+
**Use Gemini when**:
|
|
14
|
+
- Generating creative content (text, summaries, translations)
|
|
15
|
+
- Providing conversational interfaces
|
|
16
|
+
- Analyzing user-provided content
|
|
17
|
+
- Offering intelligent suggestions or recommendations
|
|
18
|
+
- Processing natural language queries
|
|
19
|
+
|
|
20
|
+
**Don't Use Gemini when**:
|
|
21
|
+
- Deterministic logic is required (use regular code)
|
|
22
|
+
- Real-time responses are critical (< 100ms)
|
|
23
|
+
- Offline functionality is needed
|
|
24
|
+
- Simple rule-based logic suffices
|
|
25
|
+
- User expects instant, predictable results
|
|
26
|
+
|
|
27
|
+
### Human-in-the-Loop Principle
|
|
28
|
+
|
|
29
|
+
**Rule**: AI should augment, not replace, human decision-making.
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
// ✅ Good: AI suggests, user decides
|
|
33
|
+
<SuggestionList
|
|
34
|
+
suggestions={aiSuggestions}
|
|
35
|
+
onAccept={handleAccept}
|
|
36
|
+
onReject={handleReject}
|
|
37
|
+
onEdit={handleEdit}
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
// ❌ Bad: AI acts without user confirmation
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (aiResponse) {
|
|
43
|
+
saveToDatabase(aiResponse); // No user review!
|
|
44
|
+
}
|
|
45
|
+
}, [aiResponse]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Graceful Degradation
|
|
49
|
+
|
|
50
|
+
**Rule**: Application must function without AI when it fails.
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// ✅ Good: Fallback when AI unavailable
|
|
54
|
+
const content = aiGenerated || defaultContent;
|
|
55
|
+
|
|
56
|
+
// ❌ Bad: Broken UI when AI fails
|
|
57
|
+
{aiGenerated && <Content>{aiGenerated}</Content>}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Security & Safety Rules
|
|
63
|
+
|
|
64
|
+
### API Key Security
|
|
65
|
+
|
|
66
|
+
**MUST**: Never expose API keys in client-side code.
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
// ✅ Good: Environment variable
|
|
70
|
+
const apiKey = import.meta.env.VITE_GEMINI_API_KEY;
|
|
71
|
+
|
|
72
|
+
// ❌ CRITICAL: Hardcoded key
|
|
73
|
+
const apiKey = "AIzaSyD..."; // NEVER DO THIS
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**MUST**: Use server-side proxy for production.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// ✅ Good: Server-side API route
|
|
80
|
+
const response = await fetch('/api/gemini', {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
body: JSON.stringify({ prompt }),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ❌ Bad: Direct client-side API call in production
|
|
86
|
+
const genAI = new GoogleGenerativeAI(apiKey);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Prompt Injection Prevention
|
|
90
|
+
|
|
91
|
+
**Rule**: Sanitize and validate all user inputs before sending to Gemini.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
// ✅ Good: Input validation
|
|
95
|
+
const sanitizedPrompt = prompt
|
|
96
|
+
.trim()
|
|
97
|
+
.slice(0, MAX_PROMPT_LENGTH)
|
|
98
|
+
.replace(/[<>]/g, ''); // Remove potential injection chars
|
|
99
|
+
|
|
100
|
+
// ❌ Bad: Raw user input
|
|
101
|
+
const response = await model.generateContent(userInput);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Content Safety
|
|
105
|
+
|
|
106
|
+
**Rule**: Implement content filtering for user-facing AI responses.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
// ✅ Good: Safety settings
|
|
110
|
+
const model = genAI.getGenerativeModel({
|
|
111
|
+
model: "gemini-pro",
|
|
112
|
+
safetySettings: [
|
|
113
|
+
{
|
|
114
|
+
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
|
|
115
|
+
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
|
119
|
+
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Rule**: Handle safety blocks gracefully.
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
// ✅ Good: Safety block handling
|
|
129
|
+
if (result.response.promptFeedback?.blockReason) {
|
|
130
|
+
setError('Content was blocked for safety reasons. Please rephrase your request.');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## UX Requirements
|
|
138
|
+
|
|
139
|
+
### Streaming Response Pattern
|
|
140
|
+
|
|
141
|
+
**MUST**: Use streaming for all text generation over 50 words.
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// ✅ Good: Streaming for better UX
|
|
145
|
+
const result = await model.generateContentStream(prompt);
|
|
146
|
+
for await (const chunk of result.stream) {
|
|
147
|
+
setText(prev => prev + chunk.text());
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ❌ Bad: Blocking until complete response
|
|
151
|
+
const result = await model.generateContent(prompt);
|
|
152
|
+
setText(result.response.text());
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Loading States
|
|
156
|
+
|
|
157
|
+
**MUST**: Show loading indicators during AI generation.
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
// ✅ Good: Clear loading state
|
|
161
|
+
{isLoading && (
|
|
162
|
+
<div role="status" aria-live="polite">
|
|
163
|
+
<Spinner />
|
|
164
|
+
<span>Generating response...</span>
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
// ❌ Bad: No feedback during generation
|
|
169
|
+
{response && <div>{response}</div>}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Streaming UI States
|
|
173
|
+
|
|
174
|
+
**MUST**: Use `aria-live` for streaming content.
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
// ✅ Good: Accessible streaming
|
|
178
|
+
<div aria-live="polite" aria-atomic="false">
|
|
179
|
+
{streamingText}
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
// ❌ Bad: Screen reader can't track updates
|
|
183
|
+
<div>{streamingText}</div>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Error Handling UX
|
|
187
|
+
|
|
188
|
+
**MUST**: Provide actionable error messages with retry options.
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
// ✅ Good: User-friendly error handling
|
|
192
|
+
{error && (
|
|
193
|
+
<Alert severity="error">
|
|
194
|
+
<AlertTitle>Unable to generate response</AlertTitle>
|
|
195
|
+
{error.message}
|
|
196
|
+
<Button onClick={handleRetry}>Try Again</Button>
|
|
197
|
+
</Alert>
|
|
198
|
+
)}
|
|
199
|
+
|
|
200
|
+
// ❌ Bad: Technical error dump
|
|
201
|
+
{error && <div>Error: {error.toString()}</div>}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### AI Attribution
|
|
205
|
+
|
|
206
|
+
**MUST**: Clearly indicate AI-generated content.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
// ✅ Good: Clear attribution
|
|
210
|
+
<div>
|
|
211
|
+
<p>{aiContent}</p>
|
|
212
|
+
<small>Generated by AI • Please verify accuracy</small>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
// ❌ Bad: No indication content is AI-generated
|
|
216
|
+
<div>{aiContent}</div>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Performance Rules
|
|
222
|
+
|
|
223
|
+
### Response Caching
|
|
224
|
+
|
|
225
|
+
**Rule**: Cache responses for identical prompts.
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
// ✅ Good: Cache frequent queries
|
|
229
|
+
const cacheKey = hashPrompt(prompt);
|
|
230
|
+
const cached = cache.get(cacheKey);
|
|
231
|
+
if (cached) return cached;
|
|
232
|
+
|
|
233
|
+
const response = await generateContent(prompt);
|
|
234
|
+
cache.set(cacheKey, response, { ttl: 3600 });
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Rate Limiting
|
|
238
|
+
|
|
239
|
+
**MUST**: Implement client-side rate limiting to prevent quota exhaustion.
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
// ✅ Good: Rate limit user requests
|
|
243
|
+
const rateLimiter = new RateLimiter({
|
|
244
|
+
maxRequests: 10,
|
|
245
|
+
windowMs: 60000, // 10 requests per minute
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
if (!rateLimiter.check(userId)) {
|
|
249
|
+
throw new Error('Rate limit exceeded. Please try again later.');
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Timeout Handling
|
|
254
|
+
|
|
255
|
+
**MUST**: Set reasonable timeouts for AI requests.
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
// ✅ Good: Timeout protection
|
|
259
|
+
const controller = new AbortController();
|
|
260
|
+
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const response = await fetch('/api/gemini', {
|
|
264
|
+
signal: controller.signal,
|
|
265
|
+
});
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (error.name === 'AbortError') {
|
|
268
|
+
setError('Request timed out. Please try again.');
|
|
269
|
+
}
|
|
270
|
+
} finally {
|
|
271
|
+
clearTimeout(timeoutId);
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Token Optimization
|
|
276
|
+
|
|
277
|
+
**Rule**: Minimize token usage for cost and performance.
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
// ✅ Good: Concise system instructions
|
|
281
|
+
const systemInstruction = "Summarize in 2-3 sentences.";
|
|
282
|
+
|
|
283
|
+
// ❌ Bad: Verbose, wasteful prompting
|
|
284
|
+
const systemInstruction = `
|
|
285
|
+
Please provide a comprehensive summary of the following text.
|
|
286
|
+
Make sure to include all key points and important details.
|
|
287
|
+
The summary should be well-structured and easy to read.
|
|
288
|
+
Please keep it concise but informative.
|
|
289
|
+
`;
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Ethical Guidelines
|
|
295
|
+
|
|
296
|
+
### Transparency
|
|
297
|
+
|
|
298
|
+
**MUST**: Users must know they're interacting with AI.
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
// ✅ Good: Clear AI disclosure
|
|
302
|
+
<Chatbot>
|
|
303
|
+
<Header>AI Assistant</Header>
|
|
304
|
+
<Disclaimer>
|
|
305
|
+
This is an AI chatbot. Responses may contain errors.
|
|
306
|
+
</Disclaimer>
|
|
307
|
+
</Chatbot>
|
|
308
|
+
|
|
309
|
+
// ❌ Bad: Misleading as human
|
|
310
|
+
<Chatbot>
|
|
311
|
+
<Header>Customer Support</Header>
|
|
312
|
+
</Chatbot>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Bias Awareness
|
|
316
|
+
|
|
317
|
+
**Rule**: Acknowledge potential AI biases in documentation.
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
// ✅ Good: Bias disclaimer
|
|
321
|
+
<TermsOfService>
|
|
322
|
+
AI-generated content may reflect biases present in training data.
|
|
323
|
+
Use critical judgment when evaluating suggestions.
|
|
324
|
+
</TermsOfService>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Data Privacy
|
|
328
|
+
|
|
329
|
+
**MUST**: Inform users about data sent to Gemini API.
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
// ✅ Good: Privacy notice
|
|
333
|
+
<PrivacyNotice>
|
|
334
|
+
Your input will be sent to Google's Gemini API for processing.
|
|
335
|
+
<Link to="/privacy">Learn more about data handling</Link>
|
|
336
|
+
</PrivacyNotice>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**MUST**: Respect user consent for data sharing.
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
// ✅ Good: Opt-in consent
|
|
343
|
+
const [aiConsent, setAiConsent] = useState(false);
|
|
344
|
+
|
|
345
|
+
if (!aiConsent) {
|
|
346
|
+
return <ConsentDialog onAccept={() => setAiConsent(true)} />;
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Content Responsibility
|
|
351
|
+
|
|
352
|
+
**Rule**: Verify critical AI-generated content before using.
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
// ✅ Good: Verification step for critical content
|
|
356
|
+
<GeneratedContent>
|
|
357
|
+
{content}
|
|
358
|
+
<Warning>
|
|
359
|
+
This content was AI-generated and should be reviewed for accuracy.
|
|
360
|
+
</Warning>
|
|
361
|
+
<Button onClick={handleVerify}>Mark as Verified</Button>
|
|
362
|
+
</GeneratedContent>
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Anti-Patterns
|
|
368
|
+
|
|
369
|
+
### ❌ Blocking UI During Generation
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
// ❌ Bad: Freezes entire UI
|
|
373
|
+
const response = await model.generateContent(longPrompt);
|
|
374
|
+
setContent(response);
|
|
375
|
+
|
|
376
|
+
// ✅ Good: Non-blocking with streaming
|
|
377
|
+
const stream = await model.generateContentStream(longPrompt);
|
|
378
|
+
for await (const chunk of stream) {
|
|
379
|
+
setContent(prev => prev + chunk.text());
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### ❌ No Loading Indicators
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
// ❌ Bad: Silent loading
|
|
387
|
+
const handleGenerate = async () => {
|
|
388
|
+
const result = await generateAI(prompt);
|
|
389
|
+
setResult(result);
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// ✅ Good: Clear feedback
|
|
393
|
+
const handleGenerate = async () => {
|
|
394
|
+
setLoading(true);
|
|
395
|
+
try {
|
|
396
|
+
const result = await generateAI(prompt);
|
|
397
|
+
setResult(result);
|
|
398
|
+
} finally {
|
|
399
|
+
setLoading(false);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### ❌ Hardcoded Prompts
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
// ❌ Bad: Inflexible hardcoded prompts
|
|
408
|
+
const prompt = "Write a blog post about React";
|
|
409
|
+
|
|
410
|
+
// ✅ Good: Configurable prompt templates
|
|
411
|
+
const prompt = promptTemplate
|
|
412
|
+
.replace('{{topic}}', topic)
|
|
413
|
+
.replace('{{tone}}', tone)
|
|
414
|
+
.replace('{{length}}', length);
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### ❌ No Error Recovery
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
// ❌ Bad: Crash on error
|
|
421
|
+
const result = await model.generateContent(prompt);
|
|
422
|
+
setText(result.text());
|
|
423
|
+
|
|
424
|
+
// ✅ Good: Graceful error handling
|
|
425
|
+
try {
|
|
426
|
+
const result = await model.generateContent(prompt);
|
|
427
|
+
setText(result.text());
|
|
428
|
+
} catch (error) {
|
|
429
|
+
setError(error);
|
|
430
|
+
setText(fallbackContent);
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### ❌ Missing AI Attribution
|
|
435
|
+
|
|
436
|
+
```tsx
|
|
437
|
+
// ❌ Bad: Presented as human-written
|
|
438
|
+
<ArticleContent>{aiGeneratedText}</ArticleContent>
|
|
439
|
+
|
|
440
|
+
// ✅ Good: Clear AI attribution
|
|
441
|
+
<ArticleContent>
|
|
442
|
+
{aiGeneratedText}
|
|
443
|
+
<Attribution>Content generated with AI assistance</Attribution>
|
|
444
|
+
</ArticleContent>
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### ❌ Infinite Retry Loops
|
|
448
|
+
|
|
449
|
+
```tsx
|
|
450
|
+
// ❌ Bad: Unlimited retries
|
|
451
|
+
const generateWithRetry = async () => {
|
|
452
|
+
try {
|
|
453
|
+
return await generate();
|
|
454
|
+
} catch {
|
|
455
|
+
return generateWithRetry(); // Infinite loop!
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// ✅ Good: Limited retry attempts
|
|
460
|
+
const generateWithRetry = async (maxRetries = 3) => {
|
|
461
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
462
|
+
try {
|
|
463
|
+
return await generate();
|
|
464
|
+
} catch (error) {
|
|
465
|
+
if (i === maxRetries - 1) throw error;
|
|
466
|
+
await sleep(1000 * Math.pow(2, i)); // Exponential backoff
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## Testing Considerations
|
|
475
|
+
|
|
476
|
+
### Mock AI Responses
|
|
477
|
+
|
|
478
|
+
**Rule**: Mock Gemini API in tests for reliability.
|
|
479
|
+
|
|
480
|
+
```tsx
|
|
481
|
+
// ✅ Good: Mocked AI for tests
|
|
482
|
+
jest.mock('@google/generative-ai', () => ({
|
|
483
|
+
GoogleGenerativeAI: jest.fn().mockImplementation(() => ({
|
|
484
|
+
getGenerativeModel: () => ({
|
|
485
|
+
generateContent: async () => ({
|
|
486
|
+
response: { text: () => 'Mocked AI response' },
|
|
487
|
+
}),
|
|
488
|
+
}),
|
|
489
|
+
})),
|
|
490
|
+
}));
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Test Error Scenarios
|
|
494
|
+
|
|
495
|
+
**Rule**: Test all AI failure modes.
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
// ✅ Good: Comprehensive error testing
|
|
499
|
+
describe('AI Generation', () => {
|
|
500
|
+
it('handles network errors', async () => {
|
|
501
|
+
mockAPI.mockRejectedValue(new NetworkError());
|
|
502
|
+
await expect(generate()).rejects.toThrow();
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it('handles rate limiting', async () => {
|
|
506
|
+
mockAPI.mockRejectedValue(new RateLimitError());
|
|
507
|
+
// Assert error UI shown
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('handles content blocking', async () => {
|
|
511
|
+
mockAPI.mockResolvedValue({ blockReason: 'SAFETY' });
|
|
512
|
+
// Assert safety message shown
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## Related Resources
|
|
520
|
+
|
|
521
|
+
- [Google Gemini API Documentation](https://ai.google.dev/docs)
|
|
522
|
+
- [Generative AI Safety Guidelines](https://ai.google.dev/gemini-api/docs/safety-settings)
|
|
523
|
+
- [Best Practices for Prompting](https://ai.google.dev/gemini-api/docs/prompting-strategies)
|
|
524
|
+
- [Rate Limits and Quotas](https://ai.google.dev/gemini-api/docs/quota)
|
|
525
|
+
- [Responsible AI Practices](https://ai.google.dev/responsible)
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## Integration Checklist
|
|
530
|
+
|
|
531
|
+
Before deploying Gemini integration:
|
|
532
|
+
|
|
533
|
+
- [ ] API keys stored securely (environment variables, never hardcoded)
|
|
534
|
+
- [ ] Safety settings configured
|
|
535
|
+
- [ ] Rate limiting implemented
|
|
536
|
+
- [ ] Timeout handling in place
|
|
537
|
+
- [ ] Loading states for all AI operations
|
|
538
|
+
- [ ] Error handling with user-friendly messages
|
|
539
|
+
- [ ] Retry logic with exponential backoff
|
|
540
|
+
- [ ] AI attribution visible on all generated content
|
|
541
|
+
- [ ] Privacy notice provided to users
|
|
542
|
+
- [ ] Content verification workflow for critical use cases
|
|
543
|
+
- [ ] Streaming implemented for long-form content
|
|
544
|
+
- [ ] Accessible ARIA labels for dynamic content
|
|
545
|
+
- [ ] Response caching for repeated queries
|
|
546
|
+
- [ ] Tests covering error scenarios
|
|
547
|
+
- [ ] Monitoring/logging for AI requests
|