@atlashub/smartstack-cli 1.4.0 → 1.5.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.
Files changed (66) hide show
  1. package/.documentation/agents.html +8 -4
  2. package/.documentation/apex.html +8 -4
  3. package/.documentation/business-analyse.html +833 -406
  4. package/.documentation/commands.html +8 -4
  5. package/.documentation/css/styles.css +153 -15
  6. package/.documentation/efcore.html +8 -4
  7. package/.documentation/gitflow.html +795 -230
  8. package/.documentation/hooks.html +8 -4
  9. package/.documentation/index.html +13 -9
  10. package/.documentation/installation.html +23 -19
  11. package/.documentation/ralph-loop.html +530 -0
  12. package/.documentation/test-web.html +8 -4
  13. package/README.md +52 -10
  14. package/dist/index.js +813 -283
  15. package/dist/index.js.map +1 -1
  16. package/package.json +1 -1
  17. package/templates/agents/efcore/conflicts.md +44 -17
  18. package/templates/agents/efcore/db-status.md +27 -6
  19. package/templates/agents/efcore/scan.md +43 -13
  20. package/templates/commands/ai-prompt.md +315 -315
  21. package/templates/commands/application/create.md +362 -362
  22. package/templates/commands/controller/create.md +216 -216
  23. package/templates/commands/controller.md +59 -0
  24. package/templates/commands/create/agent.md +138 -0
  25. package/templates/commands/create/command.md +166 -0
  26. package/templates/commands/create/hook.md +234 -0
  27. package/templates/commands/create/plugin.md +329 -0
  28. package/templates/commands/create/project.md +507 -0
  29. package/templates/commands/create/skill.md +199 -0
  30. package/templates/commands/create.md +220 -0
  31. package/templates/commands/documentation/module.md +202 -202
  32. package/templates/commands/efcore/_env-check.md +153 -153
  33. package/templates/commands/efcore/conflicts.md +109 -192
  34. package/templates/commands/efcore/db-status.md +101 -89
  35. package/templates/commands/efcore/migration.md +23 -11
  36. package/templates/commands/efcore/scan.md +115 -119
  37. package/templates/commands/efcore.md +54 -6
  38. package/templates/commands/feature-full.md +267 -267
  39. package/templates/commands/gitflow/11-finish.md +145 -11
  40. package/templates/commands/gitflow/13-sync.md +216 -216
  41. package/templates/commands/gitflow/14-rebase.md +251 -251
  42. package/templates/commands/gitflow/2-status.md +120 -10
  43. package/templates/commands/gitflow/3-commit.md +150 -0
  44. package/templates/commands/gitflow/7-pull-request.md +134 -5
  45. package/templates/commands/gitflow/9-merge.md +142 -1
  46. package/templates/commands/implement.md +663 -663
  47. package/templates/commands/init.md +562 -0
  48. package/templates/commands/mcp-integration.md +330 -0
  49. package/templates/commands/notification.md +129 -129
  50. package/templates/commands/validate.md +233 -0
  51. package/templates/commands/workflow.md +193 -193
  52. package/templates/skills/ai-prompt/SKILL.md +778 -778
  53. package/templates/skills/application/SKILL.md +563 -563
  54. package/templates/skills/application/templates-backend.md +450 -450
  55. package/templates/skills/application/templates-frontend.md +531 -531
  56. package/templates/skills/application/templates-i18n.md +520 -520
  57. package/templates/skills/application/templates-seed.md +647 -647
  58. package/templates/skills/controller/SKILL.md +240 -240
  59. package/templates/skills/controller/postman-templates.md +614 -614
  60. package/templates/skills/controller/templates.md +1468 -1468
  61. package/templates/skills/documentation/SKILL.md +133 -133
  62. package/templates/skills/documentation/templates.md +476 -476
  63. package/templates/skills/feature-full/SKILL.md +838 -838
  64. package/templates/skills/notification/SKILL.md +555 -555
  65. package/templates/skills/ui-components/SKILL.md +870 -870
  66. package/templates/skills/workflow/SKILL.md +582 -582
@@ -1,778 +1,778 @@
1
- ---
2
- name: ai-prompt
3
- description: |
4
- Integre les capacites IA dans les features SmartStack.
5
- Utiliser ce skill quand:
6
- - L'utilisateur veut integrer l'IA dans une fonctionnalite
7
- - L'utilisateur mentionne "prompt", "GPT", "Claude", "IA", "AI"
8
- - Creation d'un assistant ou chatbot
9
- - Generation de contenu automatique
10
- - Validation de reponses IA avec schema
11
- Types: Prompt, OutputSchema, Provider, Model
12
- ---
13
-
14
- # Skill AI Prompt SmartStack
15
-
16
- > **Architecture:** Prompt + Blocks → Provider + Model → Completion → Schema Validation
17
- > Multi-provider: OpenAI, Anthropic (Claude), Azure OpenAI, Google Gemini
18
-
19
- ## QUAND CE SKILL S'ACTIVE
20
-
21
- Claude invoque automatiquement ce skill quand il detecte :
22
-
23
- | Declencheur | Exemple |
24
- |-------------|---------|
25
- | Demande explicite | "Integre GPT pour generer des descriptions" |
26
- | Chatbot/Assistant | "Cree un assistant pour le support" |
27
- | Generation contenu | "Genere automatiquement des reponses" |
28
- | Analyse texte | "Analyse le sentiment des commentaires" |
29
- | Mots-cles | "prompt", "GPT", "Claude", "IA", "completion", "LLM" |
30
-
31
- ---
32
-
33
- ## ARCHITECTURE IA
34
-
35
- ```
36
- ┌─────────────────────────────────────────────────────────────────────────────┐
37
- │ AI COMPLETION FLOW │
38
- ├─────────────────────────────────────────────────────────────────────────────┤
39
- │ │
40
- │ [FEATURE] ─────────────────────────────────────────────────────────────┐ │
41
- │ │ │ │
42
- │ ▼ │ │
43
- │ ┌─────────────────────────────┐ │ │
44
- │ │ IAiCompletionService │ │ │
45
- │ │ ExecutePromptAsync() │ │ │
46
- │ └─────────────┬───────────────┘ │ │
47
- │ │ │ │
48
- │ ┌────────┴────────┐ │ │
49
- │ ▼ ▼ │ │
50
- │ ┌─────────────┐ ┌─────────────────┐ │ │
51
- │ │ Prompt │ │ Provider │ │ │
52
- │ │ + Blocks │ │ Instance │ │ │
53
- │ └──────┬──────┘ └────────┬────────┘ │ │
54
- │ │ │ │ │
55
- │ ▼ ▼ │ │
56
- │ ┌─────────────────────────────────────┐ │ │
57
- │ │ RENDERED PROMPT │ │ │
58
- │ │ System: {{instructions}} │ │ │
59
- │ │ User: {{userInput}} │ │ │
60
- │ └─────────────────┬───────────────────┘ │ │
61
- │ │ │ │
62
- │ ▼ │ │
63
- │ ┌─────────────────────────────────────┐ │ │
64
- │ │ EXTERNAL API │ │ │
65
- │ │ OpenAI / Claude / Azure / Gemini │ │ │
66
- │ └─────────────────┬───────────────────┘ │ │
67
- │ │ │ │
68
- │ ▼ │ │
69
- │ ┌─────────────────────────────────────┐ │ │
70
- │ │ AI RESPONSE │ │ │
71
- │ │ { "content": "...", ... } │ │ │
72
- │ └─────────────────┬───────────────────┘ │ │
73
- │ │ │ │
74
- │ ┌────────────┴────────────┐ │ │
75
- │ ▼ ▼ │ │
76
- │ ┌─────────────┐ ┌─────────────────┐ │ │
77
- │ │ RAW │ │ OutputSchema │ │ │
78
- │ │ Response │ │ Validation │ │ │
79
- │ └─────────────┘ └────────┬────────┘ │ │
80
- │ │ │ │
81
- │ ▼ │ │
82
- │ ┌─────────────────┐ │ │
83
- │ │ Typed Result<T> │ │ │
84
- │ │ + Validation │ │ │
85
- │ └─────────────────┘ │ │
86
- │ │ │
87
- └────────────────────────────────────────────────────────────────────────────┘
88
- ```
89
-
90
- ---
91
-
92
- ## ENTITES PRINCIPALES
93
-
94
- ### AiProvider
95
-
96
- ```csharp
97
- // Providers disponibles (seed data)
98
- - OpenAI (gpt-4o, gpt-4-turbo, gpt-3.5-turbo)
99
- - Anthropic (claude-3-opus, claude-3-sonnet, claude-3-haiku)
100
- - Azure OpenAI (deploiements custom)
101
- - Google (gemini-pro, gemini-1.5-pro)
102
- ```
103
-
104
- ### AiModel
105
-
106
- ```csharp
107
- // Proprietes cles
108
- public string Code { get; } // "gpt-4o"
109
- public string ShortCode { get; } // "GPT4O"
110
- public ModelCategory Category { get; } // TextGeneration, Embedding, etc.
111
- public int ContextWindow { get; } // 128000
112
- public int MaxOutputTokens { get; } // 16384
113
- public decimal InputCostPerMillion { get; }
114
- public decimal OutputCostPerMillion { get; }
115
-
116
- // Capabilities
117
- public bool SupportsVision { get; }
118
- public bool SupportsFunctionCalling { get; }
119
- public bool SupportsStreaming { get; }
120
- ```
121
-
122
- ### AiProviderInstance
123
-
124
- ```csharp
125
- // Instance configuree pour un use-case
126
- public string Code { get; } // "support-assistant"
127
- public string Name { get; } // "Assistant Support"
128
- public string SystemContext { get; } // Prompt systeme personnalise
129
- public string DefaultModel { get; } // "gpt-4o"
130
- public decimal MonthlyBudgetLimit { get; }
131
- public decimal CurrentMonthUsage { get; }
132
-
133
- // Securite
134
- public string EncryptedApiKey { get; } // Cle chiffree
135
- ```
136
-
137
- ### Prompt
138
-
139
- ```csharp
140
- // Structure d'un prompt
141
- public string Code { get; } // "ticket-analyzer"
142
- public string Name { get; } // "Ticket Analyzer"
143
- public string Version { get; } // "1.0.0"
144
- public PromptStatus Status { get; } // Draft, Active, Deprecated
145
- public bool IsTemplate { get; } // Template reutilisable
146
- public Guid? OutputSchemaId { get; } // Schema de validation
147
-
148
- // Blocks = Sections du prompt
149
- public ICollection<PromptBlock> Blocks { get; }
150
- ```
151
-
152
- ### PromptBlock
153
-
154
- ```csharp
155
- // Types de blocks
156
- public enum PromptBlockType
157
- {
158
- System, // Instructions systeme
159
- User, // Message utilisateur
160
- Assistant, // Reponse exemple
161
- Tool, // Definition d'outil
162
- Context, // Contexte additionnel
163
- Examples // Exemples few-shot
164
- }
165
-
166
- // Proprietes
167
- public string Label { get; } // "Instructions"
168
- public string Content { get; } // "Tu es un assistant..."
169
- public int DisplayOrder { get; }
170
- public bool IsRequired { get; }
171
- public string Condition { get; } // Condition d'inclusion
172
- ```
173
-
174
- ### OutputSchema
175
-
176
- ```csharp
177
- // Schema JSON pour validation
178
- public string Code { get; } // "ticket-analysis"
179
- public string Name { get; } // "Ticket Analysis Schema"
180
- public string JsonSchema { get; } // JSON Schema valide
181
- public string DotNetType { get; } // "SmartStack.Application.AI.TicketAnalysisResult"
182
- ```
183
-
184
- ---
185
-
186
- ## WORKFLOW INTEGRATION IA
187
-
188
- ### ETAPE 1: Definir le Use Case
189
-
190
- | Question | Impact |
191
- |----------|--------|
192
- | Quel type de contenu generer ? | Choix du modele |
193
- | Reponse structuree requise ? | OutputSchema |
194
- | Multi-langue ? | Variables langue |
195
- | Budget limite ? | ProviderInstance avec limit |
196
- | Temps reel requis ? | Streaming |
197
-
198
- ### ETAPE 2: Creer le Prompt
199
-
200
- ```csharp
201
- // Via IPromptService
202
- var promptId = await _promptService.CreatePromptAsync(new CreatePromptRequest
203
- {
204
- Code = "ticket-analyzer",
205
- Name = "Ticket Analyzer",
206
- Description = "Analyse les tickets support pour categorisation",
207
- IsTemplate = false,
208
- Blocks = new[]
209
- {
210
- new CreateBlockRequest
211
- {
212
- BlockType = PromptBlockType.System,
213
- Label = "Instructions",
214
- Content = @"Tu es un expert en support technique.
215
- Analyse le ticket fourni et retourne:
216
- - Une categorie (technical, billing, general, urgent)
217
- - Un score de priorite (1-5)
218
- - Un resume en une phrase
219
- - Des tags pertinents
220
-
221
- Reponds UNIQUEMENT en JSON valide.",
222
- DisplayOrder = 1,
223
- IsRequired = true
224
- },
225
- new CreateBlockRequest
226
- {
227
- BlockType = PromptBlockType.User,
228
- Label = "Ticket",
229
- Content = "Ticket: {{ticketTitle}}\nDescription: {{ticketDescription}}",
230
- DisplayOrder = 2,
231
- IsRequired = true
232
- }
233
- }
234
- });
235
- ```
236
-
237
- ### ETAPE 3: Definir le Schema de Validation
238
-
239
- ```csharp
240
- // JSON Schema pour validation automatique
241
- var schemaId = await _schemaService.CreateAsync(new CreateSchemaRequest
242
- {
243
- Code = "ticket-analysis-result",
244
- Name = "Ticket Analysis Result",
245
- JsonSchema = @"{
246
- ""$schema"": ""http://json-schema.org/draft-07/schema#"",
247
- ""type"": ""object"",
248
- ""required"": [""category"", ""priority"", ""summary"", ""tags""],
249
- ""properties"": {
250
- ""category"": {
251
- ""type"": ""string"",
252
- ""enum"": [""technical"", ""billing"", ""general"", ""urgent""]
253
- },
254
- ""priority"": {
255
- ""type"": ""integer"",
256
- ""minimum"": 1,
257
- ""maximum"": 5
258
- },
259
- ""summary"": {
260
- ""type"": ""string"",
261
- ""maxLength"": 200
262
- },
263
- ""tags"": {
264
- ""type"": ""array"",
265
- ""items"": { ""type"": ""string"" },
266
- ""maxItems"": 5
267
- }
268
- }
269
- }",
270
- DotNetType = "SmartStack.Application.AI.TicketAnalysisResult"
271
- });
272
-
273
- // Lier au prompt
274
- await _promptService.UpdatePromptAsync(promptId, new UpdatePromptRequest
275
- {
276
- OutputSchemaId = schemaId
277
- });
278
- ```
279
-
280
- ### ETAPE 4: Executer le Prompt
281
-
282
- ```csharp
283
- // Execution simple (retourne string)
284
- var result = await _aiCompletionService.ExecutePromptAsync(
285
- promptId,
286
- new Dictionary<string, object>
287
- {
288
- ["ticketTitle"] = ticket.Title,
289
- ["ticketDescription"] = ticket.Description
290
- });
291
-
292
- if (result.Success)
293
- {
294
- var content = result.Content; // JSON string
295
- _logger.LogInformation(
296
- "AI completed in {Ms}ms, {Input}/{Output} tokens",
297
- result.ExecutionTimeMs,
298
- result.InputTokens,
299
- result.OutputTokens);
300
- }
301
-
302
- // Execution avec validation typee
303
- var typedResult = await _aiCompletionService
304
- .ExecutePromptWithValidationAsync<TicketAnalysisResult>(
305
- promptId,
306
- new Dictionary<string, object>
307
- {
308
- ["ticketTitle"] = ticket.Title,
309
- ["ticketDescription"] = ticket.Description
310
- });
311
-
312
- if (typedResult.Success && typedResult.IsValid)
313
- {
314
- var analysis = typedResult.Data; // TicketAnalysisResult type
315
- ticket.SetCategory(analysis.Category);
316
- ticket.SetPriority(analysis.Priority);
317
- ticket.AddTags(analysis.Tags);
318
- }
319
- else if (!typedResult.IsValid)
320
- {
321
- _logger.LogWarning(
322
- "AI response validation failed: {Errors}",
323
- string.Join(", ", typedResult.ValidationErrors));
324
- }
325
- ```
326
-
327
- ---
328
-
329
- ## SERVICES IA
330
-
331
- ### IAiCompletionService
332
-
333
- ```csharp
334
- public interface IAiCompletionService
335
- {
336
- // Execution basique
337
- Task<AiCompletionResult> CompleteAsync(
338
- AiCompletionRequest request,
339
- CancellationToken ct = default);
340
-
341
- // Execution par prompt ID
342
- Task<AiCompletionResult> ExecutePromptAsync(
343
- Guid promptId,
344
- Dictionary<string, object>? variables = null,
345
- Guid? providerId = null,
346
- Guid? modelId = null,
347
- CancellationToken ct = default);
348
-
349
- // Execution avec validation schema
350
- Task<AiValidatedCompletionResult<T>> ExecutePromptWithValidationAsync<T>(
351
- Guid promptId,
352
- Dictionary<string, object>? variables = null,
353
- Guid? providerId = null,
354
- Guid? modelId = null,
355
- CancellationToken ct = default);
356
-
357
- // Par code de prompt
358
- Task<AiValidatedCompletionResult<T>> ExecutePromptByCodeWithValidationAsync<T>(
359
- string promptCode,
360
- Dictionary<string, object>? variables = null,
361
- Guid? providerId = null,
362
- Guid? modelId = null,
363
- CancellationToken ct = default);
364
-
365
- // Provider systeme par defaut
366
- Task<Guid?> GetSystemProviderIdAsync(CancellationToken ct = default);
367
- }
368
- ```
369
-
370
- ### IPromptService
371
-
372
- ```csharp
373
- public interface IPromptService
374
- {
375
- // CRUD Prompts
376
- Task<List<PromptDto>> GetAllPromptsAsync(PromptStatus? status, bool? isTemplate);
377
- Task<PromptDto> GetPromptByIdAsync(Guid promptId);
378
- Task<PromptDto> GetPromptByCodeAsync(string code, string? version = null);
379
- Task<PromptDto> GetActivePromptByCodeAsync(string code);
380
-
381
- // Rendu
382
- Task<string> RenderPromptAsync(string code, Dictionary<string, object>? variables = null);
383
-
384
- // Lifecycle
385
- Task<Guid> CreatePromptAsync(CreatePromptRequest request);
386
- Task UpdatePromptAsync(Guid promptId, UpdatePromptRequest request);
387
- Task<Guid> DuplicatePromptAsync(Guid promptId, string newVersion);
388
- Task ActivatePromptAsync(Guid promptId);
389
- Task DeactivatePromptAsync(Guid promptId);
390
- Task DeprecatePromptAsync(Guid promptId);
391
- Task DeletePromptAsync(Guid promptId);
392
-
393
- // Blocks
394
- Task AddBlockToPromptAsync(Guid promptId, CreateBlockRequest request);
395
- Task UpdateBlockAsync(Guid blockId, UpdateBlockRequest request);
396
- Task RemoveBlockFromPromptAsync(Guid promptId, Guid blockId);
397
- Task ReorderBlocksAsync(Guid promptId, Guid[] orderedBlockIds);
398
- }
399
- ```
400
-
401
- ---
402
-
403
- ## TEMPLATES INTEGRATION
404
-
405
- ### Template Service avec IA
406
-
407
- ```csharp
408
- public class TicketAnalysisService : ITicketAnalysisService
409
- {
410
- private readonly IAiCompletionService _aiService;
411
- private readonly ITicketRepository _ticketRepository;
412
- private readonly ILogger<TicketAnalysisService> _logger;
413
-
414
- public TicketAnalysisService(
415
- IAiCompletionService aiService,
416
- ITicketRepository ticketRepository,
417
- ILogger<TicketAnalysisService> logger)
418
- {
419
- _aiService = aiService;
420
- _ticketRepository = ticketRepository;
421
- _logger = logger;
422
- }
423
-
424
- public async Task<TicketAnalysisResult?> AnalyzeTicketAsync(
425
- Guid ticketId,
426
- CancellationToken ct)
427
- {
428
- var ticket = await _ticketRepository.GetByIdAsync(ticketId, ct);
429
- if (ticket == null) return null;
430
-
431
- try
432
- {
433
- var result = await _aiService
434
- .ExecutePromptByCodeWithValidationAsync<TicketAnalysisResult>(
435
- "ticket-analyzer",
436
- new Dictionary<string, object>
437
- {
438
- ["ticketTitle"] = ticket.Title,
439
- ["ticketDescription"] = ticket.Description,
440
- ["ticketCategory"] = ticket.Category?.Name ?? "Unknown"
441
- },
442
- cancellationToken: ct);
443
-
444
- if (result.Success && result.IsValid)
445
- {
446
- _logger.LogInformation(
447
- "Ticket {TicketId} analyzed: category={Category}, priority={Priority}",
448
- ticketId, result.Data.Category, result.Data.Priority);
449
-
450
- return result.Data;
451
- }
452
-
453
- if (!result.Success)
454
- {
455
- _logger.LogError(
456
- "AI analysis failed for ticket {TicketId}: {Error}",
457
- ticketId, result.Error);
458
- }
459
- else if (!result.IsValid)
460
- {
461
- _logger.LogWarning(
462
- "AI response invalid for ticket {TicketId}: {Errors}",
463
- ticketId, string.Join(", ", result.ValidationErrors));
464
- }
465
-
466
- return null;
467
- }
468
- catch (Exception ex)
469
- {
470
- _logger.LogError(ex, "Exception analyzing ticket {TicketId}", ticketId);
471
- return null;
472
- }
473
- }
474
- }
475
- ```
476
-
477
- ### Template DTO Result
478
-
479
- ```csharp
480
- // Application/AI/TicketAnalysisResult.cs
481
- public class TicketAnalysisResult
482
- {
483
- [JsonPropertyName("category")]
484
- public string Category { get; set; } = string.Empty;
485
-
486
- [JsonPropertyName("priority")]
487
- public int Priority { get; set; }
488
-
489
- [JsonPropertyName("summary")]
490
- public string Summary { get; set; } = string.Empty;
491
-
492
- [JsonPropertyName("tags")]
493
- public List<string> Tags { get; set; } = new();
494
-
495
- [JsonPropertyName("confidence")]
496
- public double? Confidence { get; set; }
497
- }
498
- ```
499
-
500
- ### Template Controller
501
-
502
- ```csharp
503
- [ApiController]
504
- [Route("api/[controller]")]
505
- public class TicketAnalysisController : ControllerBase
506
- {
507
- private readonly ITicketAnalysisService _analysisService;
508
-
509
- [HttpPost("{ticketId}/analyze")]
510
- [RequirePermission(Permissions.Support.Tickets.Update)]
511
- public async Task<ActionResult<TicketAnalysisResult>> AnalyzeTicket(
512
- Guid ticketId,
513
- CancellationToken ct)
514
- {
515
- var result = await _analysisService.AnalyzeTicketAsync(ticketId, ct);
516
-
517
- if (result == null)
518
- return BadRequest(new { message = "Analysis failed" });
519
-
520
- return Ok(result);
521
- }
522
- }
523
- ```
524
-
525
- ---
526
-
527
- ## FRONTEND INTEGRATION
528
-
529
- ### API IA
530
-
531
- ```typescript
532
- // services/api/aiApi.ts
533
- export const aiApi = {
534
- // Prompts
535
- getPrompts: (status?: PromptStatus, isTemplate?: boolean) =>
536
- apiClient.get<PromptDto[]>('/admin/ai/prompts', { params: { status, isTemplate } }),
537
-
538
- getPromptById: (id: string) =>
539
- apiClient.get<PromptDto>(`/admin/ai/prompts/${id}`),
540
-
541
- createPrompt: (data: CreatePromptRequest) =>
542
- apiClient.post<{ id: string }>('/admin/ai/prompts', data),
543
-
544
- // Execution
545
- executePrompt: (promptId: string, variables: Record<string, unknown>) =>
546
- apiClient.post<AiCompletionResult>(`/admin/ai/prompts/${promptId}/execute`, { variables }),
547
-
548
- previewPrompt: (promptId: string, variables: Record<string, unknown>) =>
549
- apiClient.post<{ rendered: string }>(`/admin/ai/prompts/${promptId}/preview`, { variables }),
550
-
551
- // Providers
552
- getProviders: () =>
553
- apiClient.get<AiProviderDto[]>('/admin/ai/providers'),
554
-
555
- getModels: (providerId?: string) =>
556
- apiClient.get<AiModelDto[]>('/admin/ai/models', { params: { providerId } }),
557
-
558
- // Schemas
559
- getSchemas: () =>
560
- apiClient.get<OutputSchemaDto[]>('/admin/ai/schemas'),
561
-
562
- createSchema: (data: CreateSchemaRequest) =>
563
- apiClient.post<{ id: string }>('/admin/ai/schemas', data),
564
- };
565
- ```
566
-
567
- ### Hook useAiCompletion
568
-
569
- ```typescript
570
- // hooks/useAiCompletion.ts
571
- import { useState } from 'react';
572
- import { aiApi } from '@/services/api/aiApi';
573
-
574
- export function useAiCompletion<T = unknown>(promptCode: string) {
575
- const [loading, setLoading] = useState(false);
576
- const [error, setError] = useState<string | null>(null);
577
- const [result, setResult] = useState<T | null>(null);
578
-
579
- const execute = async (variables: Record<string, unknown>) => {
580
- setLoading(true);
581
- setError(null);
582
-
583
- try {
584
- const response = await aiApi.executePrompt(promptCode, variables);
585
-
586
- if (response.success && response.isValid) {
587
- setResult(response.data as T);
588
- return response.data as T;
589
- } else {
590
- setError(response.error || 'Validation failed');
591
- return null;
592
- }
593
- } catch (err) {
594
- setError(err instanceof Error ? err.message : 'Unknown error');
595
- return null;
596
- } finally {
597
- setLoading(false);
598
- }
599
- };
600
-
601
- return { execute, loading, error, result };
602
- }
603
- ```
604
-
605
- ### Composant AI Assistant
606
-
607
- ```tsx
608
- // components/ai/AiAssistantButton.tsx
609
- import { Sparkles, Loader2 } from 'lucide-react';
610
- import { useAiCompletion } from '@/hooks/useAiCompletion';
611
-
612
- interface AiAssistantButtonProps {
613
- promptCode: string;
614
- variables: Record<string, unknown>;
615
- onResult: (result: unknown) => void;
616
- label?: string;
617
- }
618
-
619
- export function AiAssistantButton({
620
- promptCode,
621
- variables,
622
- onResult,
623
- label = 'Analyser avec IA'
624
- }: AiAssistantButtonProps) {
625
- const { execute, loading, error } = useAiCompletion(promptCode);
626
-
627
- const handleClick = async () => {
628
- const result = await execute(variables);
629
- if (result) {
630
- onResult(result);
631
- }
632
- };
633
-
634
- return (
635
- <div>
636
- <button
637
- onClick={handleClick}
638
- disabled={loading}
639
- className="inline-flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50"
640
- >
641
- {loading ? (
642
- <Loader2 className="w-4 h-4 animate-spin" />
643
- ) : (
644
- <Sparkles className="w-4 h-4" />
645
- )}
646
- {label}
647
- </button>
648
- {error && (
649
- <p className="mt-2 text-sm text-red-500">{error}</p>
650
- )}
651
- </div>
652
- );
653
- }
654
- ```
655
-
656
- ---
657
-
658
- ## PATTERNS AVANCES
659
-
660
- ### Pattern: Streaming Response
661
-
662
- ```typescript
663
- // Pour les reponses longues, utiliser le streaming
664
- async function streamCompletion(promptId: string, variables: Record<string, unknown>) {
665
- const response = await fetch(`/api/ai/prompts/${promptId}/stream`, {
666
- method: 'POST',
667
- headers: { 'Content-Type': 'application/json' },
668
- body: JSON.stringify({ variables })
669
- });
670
-
671
- const reader = response.body?.getReader();
672
- const decoder = new TextDecoder();
673
-
674
- while (true) {
675
- const { done, value } = await reader!.read();
676
- if (done) break;
677
-
678
- const chunk = decoder.decode(value);
679
- // Afficher progressivement
680
- appendToOutput(chunk);
681
- }
682
- }
683
- ```
684
-
685
- ### Pattern: Fallback Provider
686
-
687
- ```csharp
688
- // Si le provider principal echoue, fallback sur un autre
689
- public async Task<AiCompletionResult> CompleteWithFallbackAsync(
690
- Guid promptId,
691
- Dictionary<string, object> variables)
692
- {
693
- var providers = await _providerService.GetActiveProvidersAsync();
694
-
695
- foreach (var provider in providers.OrderBy(p => p.Priority))
696
- {
697
- try
698
- {
699
- var result = await _aiService.ExecutePromptAsync(
700
- promptId, variables, providerId: provider.Id);
701
-
702
- if (result.Success)
703
- return result;
704
- }
705
- catch (Exception ex)
706
- {
707
- _logger.LogWarning(ex,
708
- "Provider {Provider} failed, trying next",
709
- provider.Name);
710
- }
711
- }
712
-
713
- return new AiCompletionResult { Success = false, Error = "All providers failed" };
714
- }
715
- ```
716
-
717
- ---
718
-
719
- ## CHECKLIST INTEGRATION IA
720
-
721
- ```
722
- □ Use case defini (generation, analyse, classification)
723
- □ Prompt cree avec:
724
- □ Code unique
725
- □ Blocks structures (System, User, etc.)
726
- □ Variables documentees
727
- □ Version definie
728
- □ OutputSchema cree si reponse structuree
729
- □ Service cree avec injection IAiCompletionService
730
- □ Gestion erreurs implementee
731
- □ Logging des executions (tokens, temps, erreurs)
732
- □ Frontend:
733
- □ Hook ou composant pour l'execution
734
- □ Loading state
735
- □ Error handling
736
- □ Affichage resultat
737
- □ Tests:
738
- □ Prompt execute correctement
739
- □ Variables substituees
740
- □ Schema valide la reponse
741
- □ Fallback fonctionne
742
- □ Budget/Usage:
743
- □ ProviderInstance avec limite
744
- □ Tracking des tokens
745
- ```
746
-
747
- ---
748
-
749
- ## REGLES ABSOLUES
750
-
751
- 1. **TOUJOURS** utiliser IAiCompletionService (jamais appel API direct)
752
- 2. **TOUJOURS** definir un OutputSchema pour les reponses structurees
753
- 3. **TOUJOURS** gerer les erreurs (API down, quota depasse, validation)
754
- 4. **TOUJOURS** logger les executions avec tokens et duree
755
- 5. **TOUJOURS** specifier la version du prompt
756
- 6. **TOUJOURS** utiliser des variables pour le contenu dynamique
757
- 7. **JAMAIS** hardcoder les cles API
758
- 8. **JAMAIS** exposer les prompts systeme au frontend
759
- 9. **JAMAIS** ignorer les limites de tokens
760
- 10. **JAMAIS** faire confiance aux reponses sans validation
761
-
762
- ---
763
-
764
- ## FICHIERS CLES
765
-
766
- | Fichier | Role |
767
- |---------|------|
768
- | `Domain/AI/Prompts/Prompt.cs` | Entite prompt |
769
- | `Domain/AI/Prompts/PromptBlock.cs` | Blocks du prompt |
770
- | `Domain/AI/Schemas/OutputSchema.cs` | Schema validation |
771
- | `Domain/AI/AiProvider.cs` | Provider IA |
772
- | `Domain/AI/AiModel.cs` | Modele IA |
773
- | `Domain/AI/AiProviderInstance.cs` | Instance configuree |
774
- | `Application/Common/Interfaces/IAiCompletionService.cs` | Interface service |
775
- | `Application/Common/Interfaces/IPromptService.cs` | Interface prompts |
776
- | `Infrastructure/Services/AI/AiCompletionService.cs` | Implementation |
777
- | `Infrastructure/Services/AI/PromptService.cs` | Implementation prompts |
778
- | `web/src/services/api/aiApi.ts` | API frontend |
1
+ ---
2
+ name: ai-prompt
3
+ description: |
4
+ Integre les capacites IA dans les features SmartStack.
5
+ Utiliser ce skill quand:
6
+ - L'utilisateur veut integrer l'IA dans une fonctionnalite
7
+ - L'utilisateur mentionne "prompt", "GPT", "Claude", "IA", "AI"
8
+ - Creation d'un assistant ou chatbot
9
+ - Generation de contenu automatique
10
+ - Validation de reponses IA avec schema
11
+ Types: Prompt, OutputSchema, Provider, Model
12
+ ---
13
+
14
+ # Skill AI Prompt SmartStack
15
+
16
+ > **Architecture:** Prompt + Blocks → Provider + Model → Completion → Schema Validation
17
+ > Multi-provider: OpenAI, Anthropic (Claude), Azure OpenAI, Google Gemini
18
+
19
+ ## QUAND CE SKILL S'ACTIVE
20
+
21
+ Claude invoque automatiquement ce skill quand il detecte :
22
+
23
+ | Declencheur | Exemple |
24
+ |-------------|---------|
25
+ | Demande explicite | "Integre GPT pour generer des descriptions" |
26
+ | Chatbot/Assistant | "Cree un assistant pour le support" |
27
+ | Generation contenu | "Genere automatiquement des reponses" |
28
+ | Analyse texte | "Analyse le sentiment des commentaires" |
29
+ | Mots-cles | "prompt", "GPT", "Claude", "IA", "completion", "LLM" |
30
+
31
+ ---
32
+
33
+ ## ARCHITECTURE IA
34
+
35
+ ```
36
+ ┌─────────────────────────────────────────────────────────────────────────────┐
37
+ │ AI COMPLETION FLOW │
38
+ ├─────────────────────────────────────────────────────────────────────────────┤
39
+ │ │
40
+ │ [FEATURE] ─────────────────────────────────────────────────────────────┐ │
41
+ │ │ │ │
42
+ │ ▼ │ │
43
+ │ ┌─────────────────────────────┐ │ │
44
+ │ │ IAiCompletionService │ │ │
45
+ │ │ ExecutePromptAsync() │ │ │
46
+ │ └─────────────┬───────────────┘ │ │
47
+ │ │ │ │
48
+ │ ┌────────┴────────┐ │ │
49
+ │ ▼ ▼ │ │
50
+ │ ┌─────────────┐ ┌─────────────────┐ │ │
51
+ │ │ Prompt │ │ Provider │ │ │
52
+ │ │ + Blocks │ │ Instance │ │ │
53
+ │ └──────┬──────┘ └────────┬────────┘ │ │
54
+ │ │ │ │ │
55
+ │ ▼ ▼ │ │
56
+ │ ┌─────────────────────────────────────┐ │ │
57
+ │ │ RENDERED PROMPT │ │ │
58
+ │ │ System: {{instructions}} │ │ │
59
+ │ │ User: {{userInput}} │ │ │
60
+ │ └─────────────────┬───────────────────┘ │ │
61
+ │ │ │ │
62
+ │ ▼ │ │
63
+ │ ┌─────────────────────────────────────┐ │ │
64
+ │ │ EXTERNAL API │ │ │
65
+ │ │ OpenAI / Claude / Azure / Gemini │ │ │
66
+ │ └─────────────────┬───────────────────┘ │ │
67
+ │ │ │ │
68
+ │ ▼ │ │
69
+ │ ┌─────────────────────────────────────┐ │ │
70
+ │ │ AI RESPONSE │ │ │
71
+ │ │ { "content": "...", ... } │ │ │
72
+ │ └─────────────────┬───────────────────┘ │ │
73
+ │ │ │ │
74
+ │ ┌────────────┴────────────┐ │ │
75
+ │ ▼ ▼ │ │
76
+ │ ┌─────────────┐ ┌─────────────────┐ │ │
77
+ │ │ RAW │ │ OutputSchema │ │ │
78
+ │ │ Response │ │ Validation │ │ │
79
+ │ └─────────────┘ └────────┬────────┘ │ │
80
+ │ │ │ │
81
+ │ ▼ │ │
82
+ │ ┌─────────────────┐ │ │
83
+ │ │ Typed Result<T> │ │ │
84
+ │ │ + Validation │ │ │
85
+ │ └─────────────────┘ │ │
86
+ │ │ │
87
+ └────────────────────────────────────────────────────────────────────────────┘
88
+ ```
89
+
90
+ ---
91
+
92
+ ## ENTITES PRINCIPALES
93
+
94
+ ### AiProvider
95
+
96
+ ```csharp
97
+ // Providers disponibles (seed data)
98
+ - OpenAI (gpt-4o, gpt-4-turbo, gpt-3.5-turbo)
99
+ - Anthropic (claude-3-opus, claude-3-sonnet, claude-3-haiku)
100
+ - Azure OpenAI (deploiements custom)
101
+ - Google (gemini-pro, gemini-1.5-pro)
102
+ ```
103
+
104
+ ### AiModel
105
+
106
+ ```csharp
107
+ // Proprietes cles
108
+ public string Code { get; } // "gpt-4o"
109
+ public string ShortCode { get; } // "GPT4O"
110
+ public ModelCategory Category { get; } // TextGeneration, Embedding, etc.
111
+ public int ContextWindow { get; } // 128000
112
+ public int MaxOutputTokens { get; } // 16384
113
+ public decimal InputCostPerMillion { get; }
114
+ public decimal OutputCostPerMillion { get; }
115
+
116
+ // Capabilities
117
+ public bool SupportsVision { get; }
118
+ public bool SupportsFunctionCalling { get; }
119
+ public bool SupportsStreaming { get; }
120
+ ```
121
+
122
+ ### AiProviderInstance
123
+
124
+ ```csharp
125
+ // Instance configuree pour un use-case
126
+ public string Code { get; } // "support-assistant"
127
+ public string Name { get; } // "Assistant Support"
128
+ public string SystemContext { get; } // Prompt systeme personnalise
129
+ public string DefaultModel { get; } // "gpt-4o"
130
+ public decimal MonthlyBudgetLimit { get; }
131
+ public decimal CurrentMonthUsage { get; }
132
+
133
+ // Securite
134
+ public string EncryptedApiKey { get; } // Cle chiffree
135
+ ```
136
+
137
+ ### Prompt
138
+
139
+ ```csharp
140
+ // Structure d'un prompt
141
+ public string Code { get; } // "ticket-analyzer"
142
+ public string Name { get; } // "Ticket Analyzer"
143
+ public string Version { get; } // "1.0.0"
144
+ public PromptStatus Status { get; } // Draft, Active, Deprecated
145
+ public bool IsTemplate { get; } // Template reutilisable
146
+ public Guid? OutputSchemaId { get; } // Schema de validation
147
+
148
+ // Blocks = Sections du prompt
149
+ public ICollection<PromptBlock> Blocks { get; }
150
+ ```
151
+
152
+ ### PromptBlock
153
+
154
+ ```csharp
155
+ // Types de blocks
156
+ public enum PromptBlockType
157
+ {
158
+ System, // Instructions systeme
159
+ User, // Message utilisateur
160
+ Assistant, // Reponse exemple
161
+ Tool, // Definition d'outil
162
+ Context, // Contexte additionnel
163
+ Examples // Exemples few-shot
164
+ }
165
+
166
+ // Proprietes
167
+ public string Label { get; } // "Instructions"
168
+ public string Content { get; } // "Tu es un assistant..."
169
+ public int DisplayOrder { get; }
170
+ public bool IsRequired { get; }
171
+ public string Condition { get; } // Condition d'inclusion
172
+ ```
173
+
174
+ ### OutputSchema
175
+
176
+ ```csharp
177
+ // Schema JSON pour validation
178
+ public string Code { get; } // "ticket-analysis"
179
+ public string Name { get; } // "Ticket Analysis Schema"
180
+ public string JsonSchema { get; } // JSON Schema valide
181
+ public string DotNetType { get; } // "SmartStack.Application.AI.TicketAnalysisResult"
182
+ ```
183
+
184
+ ---
185
+
186
+ ## WORKFLOW INTEGRATION IA
187
+
188
+ ### ETAPE 1: Definir le Use Case
189
+
190
+ | Question | Impact |
191
+ |----------|--------|
192
+ | Quel type de contenu generer ? | Choix du modele |
193
+ | Reponse structuree requise ? | OutputSchema |
194
+ | Multi-langue ? | Variables langue |
195
+ | Budget limite ? | ProviderInstance avec limit |
196
+ | Temps reel requis ? | Streaming |
197
+
198
+ ### ETAPE 2: Creer le Prompt
199
+
200
+ ```csharp
201
+ // Via IPromptService
202
+ var promptId = await _promptService.CreatePromptAsync(new CreatePromptRequest
203
+ {
204
+ Code = "ticket-analyzer",
205
+ Name = "Ticket Analyzer",
206
+ Description = "Analyse les tickets support pour categorisation",
207
+ IsTemplate = false,
208
+ Blocks = new[]
209
+ {
210
+ new CreateBlockRequest
211
+ {
212
+ BlockType = PromptBlockType.System,
213
+ Label = "Instructions",
214
+ Content = @"Tu es un expert en support technique.
215
+ Analyse le ticket fourni et retourne:
216
+ - Une categorie (technical, billing, general, urgent)
217
+ - Un score de priorite (1-5)
218
+ - Un resume en une phrase
219
+ - Des tags pertinents
220
+
221
+ Reponds UNIQUEMENT en JSON valide.",
222
+ DisplayOrder = 1,
223
+ IsRequired = true
224
+ },
225
+ new CreateBlockRequest
226
+ {
227
+ BlockType = PromptBlockType.User,
228
+ Label = "Ticket",
229
+ Content = "Ticket: {{ticketTitle}}\nDescription: {{ticketDescription}}",
230
+ DisplayOrder = 2,
231
+ IsRequired = true
232
+ }
233
+ }
234
+ });
235
+ ```
236
+
237
+ ### ETAPE 3: Definir le Schema de Validation
238
+
239
+ ```csharp
240
+ // JSON Schema pour validation automatique
241
+ var schemaId = await _schemaService.CreateAsync(new CreateSchemaRequest
242
+ {
243
+ Code = "ticket-analysis-result",
244
+ Name = "Ticket Analysis Result",
245
+ JsonSchema = @"{
246
+ ""$schema"": ""http://json-schema.org/draft-07/schema#"",
247
+ ""type"": ""object"",
248
+ ""required"": [""category"", ""priority"", ""summary"", ""tags""],
249
+ ""properties"": {
250
+ ""category"": {
251
+ ""type"": ""string"",
252
+ ""enum"": [""technical"", ""billing"", ""general"", ""urgent""]
253
+ },
254
+ ""priority"": {
255
+ ""type"": ""integer"",
256
+ ""minimum"": 1,
257
+ ""maximum"": 5
258
+ },
259
+ ""summary"": {
260
+ ""type"": ""string"",
261
+ ""maxLength"": 200
262
+ },
263
+ ""tags"": {
264
+ ""type"": ""array"",
265
+ ""items"": { ""type"": ""string"" },
266
+ ""maxItems"": 5
267
+ }
268
+ }
269
+ }",
270
+ DotNetType = "SmartStack.Application.AI.TicketAnalysisResult"
271
+ });
272
+
273
+ // Lier au prompt
274
+ await _promptService.UpdatePromptAsync(promptId, new UpdatePromptRequest
275
+ {
276
+ OutputSchemaId = schemaId
277
+ });
278
+ ```
279
+
280
+ ### ETAPE 4: Executer le Prompt
281
+
282
+ ```csharp
283
+ // Execution simple (retourne string)
284
+ var result = await _aiCompletionService.ExecutePromptAsync(
285
+ promptId,
286
+ new Dictionary<string, object>
287
+ {
288
+ ["ticketTitle"] = ticket.Title,
289
+ ["ticketDescription"] = ticket.Description
290
+ });
291
+
292
+ if (result.Success)
293
+ {
294
+ var content = result.Content; // JSON string
295
+ _logger.LogInformation(
296
+ "AI completed in {Ms}ms, {Input}/{Output} tokens",
297
+ result.ExecutionTimeMs,
298
+ result.InputTokens,
299
+ result.OutputTokens);
300
+ }
301
+
302
+ // Execution avec validation typee
303
+ var typedResult = await _aiCompletionService
304
+ .ExecutePromptWithValidationAsync<TicketAnalysisResult>(
305
+ promptId,
306
+ new Dictionary<string, object>
307
+ {
308
+ ["ticketTitle"] = ticket.Title,
309
+ ["ticketDescription"] = ticket.Description
310
+ });
311
+
312
+ if (typedResult.Success && typedResult.IsValid)
313
+ {
314
+ var analysis = typedResult.Data; // TicketAnalysisResult type
315
+ ticket.SetCategory(analysis.Category);
316
+ ticket.SetPriority(analysis.Priority);
317
+ ticket.AddTags(analysis.Tags);
318
+ }
319
+ else if (!typedResult.IsValid)
320
+ {
321
+ _logger.LogWarning(
322
+ "AI response validation failed: {Errors}",
323
+ string.Join(", ", typedResult.ValidationErrors));
324
+ }
325
+ ```
326
+
327
+ ---
328
+
329
+ ## SERVICES IA
330
+
331
+ ### IAiCompletionService
332
+
333
+ ```csharp
334
+ public interface IAiCompletionService
335
+ {
336
+ // Execution basique
337
+ Task<AiCompletionResult> CompleteAsync(
338
+ AiCompletionRequest request,
339
+ CancellationToken ct = default);
340
+
341
+ // Execution par prompt ID
342
+ Task<AiCompletionResult> ExecutePromptAsync(
343
+ Guid promptId,
344
+ Dictionary<string, object>? variables = null,
345
+ Guid? providerId = null,
346
+ Guid? modelId = null,
347
+ CancellationToken ct = default);
348
+
349
+ // Execution avec validation schema
350
+ Task<AiValidatedCompletionResult<T>> ExecutePromptWithValidationAsync<T>(
351
+ Guid promptId,
352
+ Dictionary<string, object>? variables = null,
353
+ Guid? providerId = null,
354
+ Guid? modelId = null,
355
+ CancellationToken ct = default);
356
+
357
+ // Par code de prompt
358
+ Task<AiValidatedCompletionResult<T>> ExecutePromptByCodeWithValidationAsync<T>(
359
+ string promptCode,
360
+ Dictionary<string, object>? variables = null,
361
+ Guid? providerId = null,
362
+ Guid? modelId = null,
363
+ CancellationToken ct = default);
364
+
365
+ // Provider systeme par defaut
366
+ Task<Guid?> GetSystemProviderIdAsync(CancellationToken ct = default);
367
+ }
368
+ ```
369
+
370
+ ### IPromptService
371
+
372
+ ```csharp
373
+ public interface IPromptService
374
+ {
375
+ // CRUD Prompts
376
+ Task<List<PromptDto>> GetAllPromptsAsync(PromptStatus? status, bool? isTemplate);
377
+ Task<PromptDto> GetPromptByIdAsync(Guid promptId);
378
+ Task<PromptDto> GetPromptByCodeAsync(string code, string? version = null);
379
+ Task<PromptDto> GetActivePromptByCodeAsync(string code);
380
+
381
+ // Rendu
382
+ Task<string> RenderPromptAsync(string code, Dictionary<string, object>? variables = null);
383
+
384
+ // Lifecycle
385
+ Task<Guid> CreatePromptAsync(CreatePromptRequest request);
386
+ Task UpdatePromptAsync(Guid promptId, UpdatePromptRequest request);
387
+ Task<Guid> DuplicatePromptAsync(Guid promptId, string newVersion);
388
+ Task ActivatePromptAsync(Guid promptId);
389
+ Task DeactivatePromptAsync(Guid promptId);
390
+ Task DeprecatePromptAsync(Guid promptId);
391
+ Task DeletePromptAsync(Guid promptId);
392
+
393
+ // Blocks
394
+ Task AddBlockToPromptAsync(Guid promptId, CreateBlockRequest request);
395
+ Task UpdateBlockAsync(Guid blockId, UpdateBlockRequest request);
396
+ Task RemoveBlockFromPromptAsync(Guid promptId, Guid blockId);
397
+ Task ReorderBlocksAsync(Guid promptId, Guid[] orderedBlockIds);
398
+ }
399
+ ```
400
+
401
+ ---
402
+
403
+ ## TEMPLATES INTEGRATION
404
+
405
+ ### Template Service avec IA
406
+
407
+ ```csharp
408
+ public class TicketAnalysisService : ITicketAnalysisService
409
+ {
410
+ private readonly IAiCompletionService _aiService;
411
+ private readonly ITicketRepository _ticketRepository;
412
+ private readonly ILogger<TicketAnalysisService> _logger;
413
+
414
+ public TicketAnalysisService(
415
+ IAiCompletionService aiService,
416
+ ITicketRepository ticketRepository,
417
+ ILogger<TicketAnalysisService> logger)
418
+ {
419
+ _aiService = aiService;
420
+ _ticketRepository = ticketRepository;
421
+ _logger = logger;
422
+ }
423
+
424
+ public async Task<TicketAnalysisResult?> AnalyzeTicketAsync(
425
+ Guid ticketId,
426
+ CancellationToken ct)
427
+ {
428
+ var ticket = await _ticketRepository.GetByIdAsync(ticketId, ct);
429
+ if (ticket == null) return null;
430
+
431
+ try
432
+ {
433
+ var result = await _aiService
434
+ .ExecutePromptByCodeWithValidationAsync<TicketAnalysisResult>(
435
+ "ticket-analyzer",
436
+ new Dictionary<string, object>
437
+ {
438
+ ["ticketTitle"] = ticket.Title,
439
+ ["ticketDescription"] = ticket.Description,
440
+ ["ticketCategory"] = ticket.Category?.Name ?? "Unknown"
441
+ },
442
+ cancellationToken: ct);
443
+
444
+ if (result.Success && result.IsValid)
445
+ {
446
+ _logger.LogInformation(
447
+ "Ticket {TicketId} analyzed: category={Category}, priority={Priority}",
448
+ ticketId, result.Data.Category, result.Data.Priority);
449
+
450
+ return result.Data;
451
+ }
452
+
453
+ if (!result.Success)
454
+ {
455
+ _logger.LogError(
456
+ "AI analysis failed for ticket {TicketId}: {Error}",
457
+ ticketId, result.Error);
458
+ }
459
+ else if (!result.IsValid)
460
+ {
461
+ _logger.LogWarning(
462
+ "AI response invalid for ticket {TicketId}: {Errors}",
463
+ ticketId, string.Join(", ", result.ValidationErrors));
464
+ }
465
+
466
+ return null;
467
+ }
468
+ catch (Exception ex)
469
+ {
470
+ _logger.LogError(ex, "Exception analyzing ticket {TicketId}", ticketId);
471
+ return null;
472
+ }
473
+ }
474
+ }
475
+ ```
476
+
477
+ ### Template DTO Result
478
+
479
+ ```csharp
480
+ // Application/AI/TicketAnalysisResult.cs
481
+ public class TicketAnalysisResult
482
+ {
483
+ [JsonPropertyName("category")]
484
+ public string Category { get; set; } = string.Empty;
485
+
486
+ [JsonPropertyName("priority")]
487
+ public int Priority { get; set; }
488
+
489
+ [JsonPropertyName("summary")]
490
+ public string Summary { get; set; } = string.Empty;
491
+
492
+ [JsonPropertyName("tags")]
493
+ public List<string> Tags { get; set; } = new();
494
+
495
+ [JsonPropertyName("confidence")]
496
+ public double? Confidence { get; set; }
497
+ }
498
+ ```
499
+
500
+ ### Template Controller
501
+
502
+ ```csharp
503
+ [ApiController]
504
+ [Route("api/[controller]")]
505
+ public class TicketAnalysisController : ControllerBase
506
+ {
507
+ private readonly ITicketAnalysisService _analysisService;
508
+
509
+ [HttpPost("{ticketId}/analyze")]
510
+ [RequirePermission(Permissions.Support.Tickets.Update)]
511
+ public async Task<ActionResult<TicketAnalysisResult>> AnalyzeTicket(
512
+ Guid ticketId,
513
+ CancellationToken ct)
514
+ {
515
+ var result = await _analysisService.AnalyzeTicketAsync(ticketId, ct);
516
+
517
+ if (result == null)
518
+ return BadRequest(new { message = "Analysis failed" });
519
+
520
+ return Ok(result);
521
+ }
522
+ }
523
+ ```
524
+
525
+ ---
526
+
527
+ ## FRONTEND INTEGRATION
528
+
529
+ ### API IA
530
+
531
+ ```typescript
532
+ // services/api/aiApi.ts
533
+ export const aiApi = {
534
+ // Prompts
535
+ getPrompts: (status?: PromptStatus, isTemplate?: boolean) =>
536
+ apiClient.get<PromptDto[]>('/admin/ai/prompts', { params: { status, isTemplate } }),
537
+
538
+ getPromptById: (id: string) =>
539
+ apiClient.get<PromptDto>(`/admin/ai/prompts/${id}`),
540
+
541
+ createPrompt: (data: CreatePromptRequest) =>
542
+ apiClient.post<{ id: string }>('/admin/ai/prompts', data),
543
+
544
+ // Execution
545
+ executePrompt: (promptId: string, variables: Record<string, unknown>) =>
546
+ apiClient.post<AiCompletionResult>(`/admin/ai/prompts/${promptId}/execute`, { variables }),
547
+
548
+ previewPrompt: (promptId: string, variables: Record<string, unknown>) =>
549
+ apiClient.post<{ rendered: string }>(`/admin/ai/prompts/${promptId}/preview`, { variables }),
550
+
551
+ // Providers
552
+ getProviders: () =>
553
+ apiClient.get<AiProviderDto[]>('/admin/ai/providers'),
554
+
555
+ getModels: (providerId?: string) =>
556
+ apiClient.get<AiModelDto[]>('/admin/ai/models', { params: { providerId } }),
557
+
558
+ // Schemas
559
+ getSchemas: () =>
560
+ apiClient.get<OutputSchemaDto[]>('/admin/ai/schemas'),
561
+
562
+ createSchema: (data: CreateSchemaRequest) =>
563
+ apiClient.post<{ id: string }>('/admin/ai/schemas', data),
564
+ };
565
+ ```
566
+
567
+ ### Hook useAiCompletion
568
+
569
+ ```typescript
570
+ // hooks/useAiCompletion.ts
571
+ import { useState } from 'react';
572
+ import { aiApi } from '@/services/api/aiApi';
573
+
574
+ export function useAiCompletion<T = unknown>(promptCode: string) {
575
+ const [loading, setLoading] = useState(false);
576
+ const [error, setError] = useState<string | null>(null);
577
+ const [result, setResult] = useState<T | null>(null);
578
+
579
+ const execute = async (variables: Record<string, unknown>) => {
580
+ setLoading(true);
581
+ setError(null);
582
+
583
+ try {
584
+ const response = await aiApi.executePrompt(promptCode, variables);
585
+
586
+ if (response.success && response.isValid) {
587
+ setResult(response.data as T);
588
+ return response.data as T;
589
+ } else {
590
+ setError(response.error || 'Validation failed');
591
+ return null;
592
+ }
593
+ } catch (err) {
594
+ setError(err instanceof Error ? err.message : 'Unknown error');
595
+ return null;
596
+ } finally {
597
+ setLoading(false);
598
+ }
599
+ };
600
+
601
+ return { execute, loading, error, result };
602
+ }
603
+ ```
604
+
605
+ ### Composant AI Assistant
606
+
607
+ ```tsx
608
+ // components/ai/AiAssistantButton.tsx
609
+ import { Sparkles, Loader2 } from 'lucide-react';
610
+ import { useAiCompletion } from '@/hooks/useAiCompletion';
611
+
612
+ interface AiAssistantButtonProps {
613
+ promptCode: string;
614
+ variables: Record<string, unknown>;
615
+ onResult: (result: unknown) => void;
616
+ label?: string;
617
+ }
618
+
619
+ export function AiAssistantButton({
620
+ promptCode,
621
+ variables,
622
+ onResult,
623
+ label = 'Analyser avec IA'
624
+ }: AiAssistantButtonProps) {
625
+ const { execute, loading, error } = useAiCompletion(promptCode);
626
+
627
+ const handleClick = async () => {
628
+ const result = await execute(variables);
629
+ if (result) {
630
+ onResult(result);
631
+ }
632
+ };
633
+
634
+ return (
635
+ <div>
636
+ <button
637
+ onClick={handleClick}
638
+ disabled={loading}
639
+ className="inline-flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50"
640
+ >
641
+ {loading ? (
642
+ <Loader2 className="w-4 h-4 animate-spin" />
643
+ ) : (
644
+ <Sparkles className="w-4 h-4" />
645
+ )}
646
+ {label}
647
+ </button>
648
+ {error && (
649
+ <p className="mt-2 text-sm text-red-500">{error}</p>
650
+ )}
651
+ </div>
652
+ );
653
+ }
654
+ ```
655
+
656
+ ---
657
+
658
+ ## PATTERNS AVANCES
659
+
660
+ ### Pattern: Streaming Response
661
+
662
+ ```typescript
663
+ // Pour les reponses longues, utiliser le streaming
664
+ async function streamCompletion(promptId: string, variables: Record<string, unknown>) {
665
+ const response = await fetch(`/api/ai/prompts/${promptId}/stream`, {
666
+ method: 'POST',
667
+ headers: { 'Content-Type': 'application/json' },
668
+ body: JSON.stringify({ variables })
669
+ });
670
+
671
+ const reader = response.body?.getReader();
672
+ const decoder = new TextDecoder();
673
+
674
+ while (true) {
675
+ const { done, value } = await reader!.read();
676
+ if (done) break;
677
+
678
+ const chunk = decoder.decode(value);
679
+ // Afficher progressivement
680
+ appendToOutput(chunk);
681
+ }
682
+ }
683
+ ```
684
+
685
+ ### Pattern: Fallback Provider
686
+
687
+ ```csharp
688
+ // Si le provider principal echoue, fallback sur un autre
689
+ public async Task<AiCompletionResult> CompleteWithFallbackAsync(
690
+ Guid promptId,
691
+ Dictionary<string, object> variables)
692
+ {
693
+ var providers = await _providerService.GetActiveProvidersAsync();
694
+
695
+ foreach (var provider in providers.OrderBy(p => p.Priority))
696
+ {
697
+ try
698
+ {
699
+ var result = await _aiService.ExecutePromptAsync(
700
+ promptId, variables, providerId: provider.Id);
701
+
702
+ if (result.Success)
703
+ return result;
704
+ }
705
+ catch (Exception ex)
706
+ {
707
+ _logger.LogWarning(ex,
708
+ "Provider {Provider} failed, trying next",
709
+ provider.Name);
710
+ }
711
+ }
712
+
713
+ return new AiCompletionResult { Success = false, Error = "All providers failed" };
714
+ }
715
+ ```
716
+
717
+ ---
718
+
719
+ ## CHECKLIST INTEGRATION IA
720
+
721
+ ```
722
+ □ Use case defini (generation, analyse, classification)
723
+ □ Prompt cree avec:
724
+ □ Code unique
725
+ □ Blocks structures (System, User, etc.)
726
+ □ Variables documentees
727
+ □ Version definie
728
+ □ OutputSchema cree si reponse structuree
729
+ □ Service cree avec injection IAiCompletionService
730
+ □ Gestion erreurs implementee
731
+ □ Logging des executions (tokens, temps, erreurs)
732
+ □ Frontend:
733
+ □ Hook ou composant pour l'execution
734
+ □ Loading state
735
+ □ Error handling
736
+ □ Affichage resultat
737
+ □ Tests:
738
+ □ Prompt execute correctement
739
+ □ Variables substituees
740
+ □ Schema valide la reponse
741
+ □ Fallback fonctionne
742
+ □ Budget/Usage:
743
+ □ ProviderInstance avec limite
744
+ □ Tracking des tokens
745
+ ```
746
+
747
+ ---
748
+
749
+ ## REGLES ABSOLUES
750
+
751
+ 1. **TOUJOURS** utiliser IAiCompletionService (jamais appel API direct)
752
+ 2. **TOUJOURS** definir un OutputSchema pour les reponses structurees
753
+ 3. **TOUJOURS** gerer les erreurs (API down, quota depasse, validation)
754
+ 4. **TOUJOURS** logger les executions avec tokens et duree
755
+ 5. **TOUJOURS** specifier la version du prompt
756
+ 6. **TOUJOURS** utiliser des variables pour le contenu dynamique
757
+ 7. **JAMAIS** hardcoder les cles API
758
+ 8. **JAMAIS** exposer les prompts systeme au frontend
759
+ 9. **JAMAIS** ignorer les limites de tokens
760
+ 10. **JAMAIS** faire confiance aux reponses sans validation
761
+
762
+ ---
763
+
764
+ ## FICHIERS CLES
765
+
766
+ | Fichier | Role |
767
+ |---------|------|
768
+ | `Domain/AI/Prompts/Prompt.cs` | Entite prompt |
769
+ | `Domain/AI/Prompts/PromptBlock.cs` | Blocks du prompt |
770
+ | `Domain/AI/Schemas/OutputSchema.cs` | Schema validation |
771
+ | `Domain/AI/AiProvider.cs` | Provider IA |
772
+ | `Domain/AI/AiModel.cs` | Modele IA |
773
+ | `Domain/AI/AiProviderInstance.cs` | Instance configuree |
774
+ | `Application/Common/Interfaces/IAiCompletionService.cs` | Interface service |
775
+ | `Application/Common/Interfaces/IPromptService.cs` | Interface prompts |
776
+ | `Infrastructure/Services/AI/AiCompletionService.cs` | Implementation |
777
+ | `Infrastructure/Services/AI/PromptService.cs` | Implementation prompts |
778
+ | `web/src/services/api/aiApi.ts` | API frontend |