@atlashub/smartstack-cli 1.11.0 → 1.13.1
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/.documentation/agents.html +7 -2
- package/.documentation/apex.html +7 -2
- package/.documentation/business-analyse.html +7 -2
- package/.documentation/cli-commands.html +871 -0
- package/.documentation/commands.html +7 -2
- package/.documentation/efcore.html +7 -2
- package/.documentation/gitflow.html +7 -2
- package/.documentation/hooks.html +7 -2
- package/.documentation/index.html +7 -2
- package/.documentation/init.html +7 -2
- package/.documentation/installation.html +7 -2
- package/.documentation/ralph-loop.html +7 -2
- package/.documentation/test-web.html +7 -2
- package/dist/index.js +1932 -336
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/templates/agents/efcore/squash.md +67 -31
- package/templates/agents/gitflow/finish.md +68 -56
- package/templates/commands/business-analyse/0-orchestrate.md +72 -556
- package/templates/commands/business-analyse/1-init.md +23 -193
- package/templates/commands/business-analyse/2-discover.md +85 -462
- package/templates/commands/business-analyse/3-analyse.md +40 -342
- package/templates/commands/business-analyse/4-specify.md +72 -537
- package/templates/commands/business-analyse/5-validate.md +43 -237
- package/templates/commands/business-analyse/6-handoff.md +93 -682
- package/templates/commands/business-analyse/7-doc-html.md +45 -544
- package/templates/commands/business-analyse/_shared.md +176 -0
- package/templates/commands/business-analyse/bug.md +50 -257
- package/templates/commands/business-analyse/change-request.md +59 -283
- package/templates/commands/business-analyse/hotfix.md +36 -120
- package/templates/commands/business-analyse.md +55 -574
- package/templates/commands/efcore/_shared.md +206 -0
- package/templates/commands/efcore/conflicts.md +39 -201
- package/templates/commands/efcore/db-deploy.md +28 -237
- package/templates/commands/efcore/db-reset.md +41 -390
- package/templates/commands/efcore/db-seed.md +44 -323
- package/templates/commands/efcore/db-status.md +31 -210
- package/templates/commands/efcore/migration.md +45 -368
- package/templates/commands/efcore/rebase-snapshot.md +38 -241
- package/templates/commands/efcore/scan.md +35 -204
- package/templates/commands/efcore/squash.md +158 -251
- package/templates/commands/efcore.md +49 -177
- package/templates/commands/gitflow/1-init.md +94 -1318
- package/templates/commands/gitflow/10-start.md +86 -990
- package/templates/commands/gitflow/11-finish.md +264 -454
- package/templates/commands/gitflow/12-cleanup.md +40 -213
- package/templates/commands/gitflow/2-status.md +51 -386
- package/templates/commands/gitflow/3-commit.md +108 -801
- package/templates/commands/gitflow/4-plan.md +42 -13
- package/templates/commands/gitflow/5-exec.md +60 -5
- package/templates/commands/gitflow/6-abort.md +54 -277
- package/templates/commands/gitflow/7-pull-request.md +74 -717
- package/templates/commands/gitflow/8-review.md +51 -178
- package/templates/commands/gitflow/9-merge.md +74 -404
- package/templates/commands/gitflow/_shared.md +196 -0
- package/templates/commands/quickstart.md +154 -0
- package/templates/commands/ralph-loop/ralph-loop.md +104 -2
- package/templates/hooks/hooks.json +13 -0
- package/templates/hooks/ralph-mcp-logger.sh +46 -0
- package/templates/hooks/ralph-session-end.sh +69 -0
- package/templates/ralph/README.md +91 -0
- package/templates/ralph/ralph.config.yaml +113 -0
- package/templates/scripts/setup-ralph-loop.sh +173 -0
- package/templates/skills/_shared.md +117 -0
- package/templates/skills/ai-prompt/SKILL.md +87 -654
- package/templates/skills/application/SKILL.md +76 -499
- package/templates/skills/controller/SKILL.md +38 -165
- package/templates/skills/documentation/SKILL.md +2 -1
- package/templates/skills/feature-full/SKILL.md +107 -732
- package/templates/skills/notification/SKILL.md +85 -474
- package/templates/skills/ui-components/SKILL.md +62 -762
- package/templates/skills/workflow/SKILL.md +85 -489
- package/templates/commands/gitflow/rescue.md +0 -867
- package/templates/skills/business-analyse/SKILL.md +0 -191
|
@@ -16,9 +16,9 @@ description: |
|
|
|
16
16
|
> **Architecture:** Prompt + Blocks → Provider + Model → Completion → Schema Validation
|
|
17
17
|
> Multi-provider: OpenAI, Anthropic (Claude), Azure OpenAI, Google Gemini
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
**Référence:** [_shared.md](../_shared.md) pour patterns communs
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
## QUAND CE SKILL S'ACTIVE
|
|
22
22
|
|
|
23
23
|
| Declencheur | Exemple |
|
|
24
24
|
|-------------|---------|
|
|
@@ -28,751 +28,184 @@ Claude invoque automatiquement ce skill quand il detecte :
|
|
|
28
28
|
| Analyse texte | "Analyse le sentiment des commentaires" |
|
|
29
29
|
| Mots-cles | "prompt", "GPT", "Claude", "IA", "completion", "LLM" |
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## ARCHITECTURE IA
|
|
31
|
+
## FLOW IA
|
|
34
32
|
|
|
35
33
|
```
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
└────────────────────────────────────────────────────────────────────────────┘
|
|
34
|
+
Feature → IAiCompletionService.ExecutePromptAsync()
|
|
35
|
+
↓
|
|
36
|
+
Prompt + Blocks → Provider Instance → External API (OpenAI/Claude/Azure/Gemini)
|
|
37
|
+
↓
|
|
38
|
+
AI Response → OutputSchema Validation → Typed Result<T>
|
|
88
39
|
```
|
|
89
40
|
|
|
90
|
-
---
|
|
91
|
-
|
|
92
41
|
## ENTITES PRINCIPALES
|
|
93
42
|
|
|
94
|
-
### AiProvider
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
```
|
|
43
|
+
### AiProvider & AiModel
|
|
44
|
+
- **Providers:** OpenAI, Anthropic, Azure OpenAI, Google
|
|
45
|
+
- **Models:** gpt-4o, claude-3-opus/sonnet/haiku, gemini-pro
|
|
46
|
+
- Properties: Code, ContextWindow, MaxOutputTokens, InputCost, OutputCost, SupportsVision, SupportsStreaming
|
|
121
47
|
|
|
122
48
|
### AiProviderInstance
|
|
49
|
+
Instance configurée: Code, Name, SystemContext, DefaultModel, MonthlyBudgetLimit, EncryptedApiKey
|
|
123
50
|
|
|
51
|
+
### Prompt & PromptBlock
|
|
124
52
|
```csharp
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
53
|
+
// Prompt: Code, Name, Version, Status, IsTemplate, OutputSchemaId, Blocks
|
|
54
|
+
// PromptBlockType: System, User, Assistant, Tool, Context, Examples
|
|
55
|
+
// Block: Label, Content, DisplayOrder, IsRequired, Condition
|
|
172
56
|
```
|
|
173
57
|
|
|
174
58
|
### OutputSchema
|
|
59
|
+
JSON Schema pour validation: Code, Name, JsonSchema, DotNetType
|
|
175
60
|
|
|
176
|
-
|
|
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
|
|
61
|
+
## WORKFLOW INTEGRATION
|
|
189
62
|
|
|
63
|
+
### 1. Definir le Use Case
|
|
190
64
|
| Question | Impact |
|
|
191
65
|
|----------|--------|
|
|
192
|
-
|
|
|
193
|
-
| Reponse structuree
|
|
66
|
+
| Type de contenu ? | Choix modele |
|
|
67
|
+
| Reponse structuree ? | OutputSchema |
|
|
194
68
|
| Multi-langue ? | Variables langue |
|
|
195
|
-
| Budget limite ? | ProviderInstance
|
|
196
|
-
| Temps reel
|
|
197
|
-
|
|
198
|
-
### ETAPE 2: Creer le Prompt
|
|
69
|
+
| Budget limite ? | ProviderInstance |
|
|
70
|
+
| Temps reel ? | Streaming |
|
|
199
71
|
|
|
72
|
+
### 2. Creer le Prompt
|
|
200
73
|
```csharp
|
|
201
|
-
// Via IPromptService
|
|
202
74
|
var promptId = await _promptService.CreatePromptAsync(new CreatePromptRequest
|
|
203
75
|
{
|
|
204
76
|
Code = "ticket-analyzer",
|
|
205
77
|
Name = "Ticket Analyzer",
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
Blocks = new[]
|
|
209
|
-
{
|
|
210
|
-
new CreateBlockRequest
|
|
211
|
-
{
|
|
78
|
+
Blocks = new[] {
|
|
79
|
+
new CreateBlockRequest {
|
|
212
80
|
BlockType = PromptBlockType.System,
|
|
213
|
-
|
|
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,
|
|
81
|
+
Content = "Instructions...",
|
|
223
82
|
IsRequired = true
|
|
224
83
|
},
|
|
225
|
-
new CreateBlockRequest
|
|
226
|
-
{
|
|
84
|
+
new CreateBlockRequest {
|
|
227
85
|
BlockType = PromptBlockType.User,
|
|
228
|
-
Label = "Ticket",
|
|
229
86
|
Content = "Ticket: {{ticketTitle}}\nDescription: {{ticketDescription}}",
|
|
230
|
-
DisplayOrder = 2,
|
|
231
87
|
IsRequired = true
|
|
232
88
|
}
|
|
233
89
|
}
|
|
234
90
|
});
|
|
235
91
|
```
|
|
236
92
|
|
|
237
|
-
###
|
|
238
|
-
|
|
93
|
+
### 3. Definir OutputSchema (si reponse structuree)
|
|
239
94
|
```csharp
|
|
240
|
-
// JSON Schema pour validation automatique
|
|
241
95
|
var schemaId = await _schemaService.CreateAsync(new CreateSchemaRequest
|
|
242
96
|
{
|
|
243
97
|
Code = "ticket-analysis-result",
|
|
244
|
-
|
|
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
|
-
}",
|
|
98
|
+
JsonSchema = @"{ ""type"": ""object"", ""required"": [""category"", ""priority""], ... }",
|
|
270
99
|
DotNetType = "SmartStack.Application.AI.TicketAnalysisResult"
|
|
271
100
|
});
|
|
272
|
-
|
|
273
|
-
// Lier au prompt
|
|
274
|
-
await _promptService.UpdatePromptAsync(promptId, new UpdatePromptRequest
|
|
275
|
-
{
|
|
276
|
-
OutputSchemaId = schemaId
|
|
277
|
-
});
|
|
278
101
|
```
|
|
279
102
|
|
|
280
|
-
###
|
|
281
|
-
|
|
103
|
+
### 4. Executer le Prompt
|
|
282
104
|
```csharp
|
|
283
|
-
// Execution simple
|
|
284
|
-
var result = await
|
|
285
|
-
promptId,
|
|
286
|
-
new Dictionary<string, object>
|
|
287
|
-
{
|
|
288
|
-
["ticketTitle"] = ticket.Title,
|
|
289
|
-
["ticketDescription"] = ticket.Description
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
if (result.Success)
|
|
105
|
+
// Execution simple
|
|
106
|
+
var result = await _aiService.ExecutePromptAsync(promptId, new Dictionary<string, object>
|
|
293
107
|
{
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
result.ExecutionTimeMs,
|
|
298
|
-
result.InputTokens,
|
|
299
|
-
result.OutputTokens);
|
|
300
|
-
}
|
|
108
|
+
["ticketTitle"] = ticket.Title,
|
|
109
|
+
["ticketDescription"] = ticket.Description
|
|
110
|
+
});
|
|
301
111
|
|
|
302
|
-
//
|
|
303
|
-
var typedResult = await
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
}
|
|
112
|
+
// Avec validation typee
|
|
113
|
+
var typedResult = await _aiService.ExecutePromptWithValidationAsync<TicketAnalysisResult>(
|
|
114
|
+
promptId, variables);
|
|
115
|
+
if (typedResult.Success && typedResult.IsValid) { /* use typedResult.Data */ }
|
|
325
116
|
```
|
|
326
117
|
|
|
327
|
-
---
|
|
328
|
-
|
|
329
118
|
## SERVICES IA
|
|
330
119
|
|
|
331
120
|
### IAiCompletionService
|
|
332
|
-
|
|
333
121
|
```csharp
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
}
|
|
122
|
+
Task<AiCompletionResult> CompleteAsync(AiCompletionRequest request, CancellationToken ct);
|
|
123
|
+
Task<AiCompletionResult> ExecutePromptAsync(Guid promptId, Dictionary<string, object>? variables, ...);
|
|
124
|
+
Task<AiValidatedCompletionResult<T>> ExecutePromptWithValidationAsync<T>(Guid promptId, ...);
|
|
125
|
+
Task<AiValidatedCompletionResult<T>> ExecutePromptByCodeWithValidationAsync<T>(string promptCode, ...);
|
|
368
126
|
```
|
|
369
127
|
|
|
370
128
|
### IPromptService
|
|
371
|
-
|
|
372
129
|
```csharp
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
}
|
|
130
|
+
Task<PromptDto> GetPromptByCodeAsync(string code, string? version = null);
|
|
131
|
+
Task<string> RenderPromptAsync(string code, Dictionary<string, object>? variables);
|
|
132
|
+
Task<Guid> CreatePromptAsync(CreatePromptRequest request);
|
|
133
|
+
Task ActivatePromptAsync(Guid promptId);
|
|
134
|
+
// Block management: AddBlockToPromptAsync, UpdateBlockAsync, ReorderBlocksAsync
|
|
399
135
|
```
|
|
400
136
|
|
|
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
137
|
## FRONTEND INTEGRATION
|
|
528
138
|
|
|
529
|
-
### API
|
|
530
|
-
|
|
139
|
+
### API
|
|
531
140
|
```typescript
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
};
|
|
141
|
+
aiApi.getPrompts(status?, isTemplate?)
|
|
142
|
+
aiApi.executePrompt(promptId, variables)
|
|
143
|
+
aiApi.previewPrompt(promptId, variables)
|
|
144
|
+
aiApi.getProviders() / aiApi.getModels(providerId?)
|
|
145
|
+
aiApi.getSchemas() / aiApi.createSchema(data)
|
|
565
146
|
```
|
|
566
147
|
|
|
567
148
|
### Hook useAiCompletion
|
|
568
|
-
|
|
569
149
|
```typescript
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
}
|
|
150
|
+
const { execute, loading, error, result } = useAiCompletion<T>(promptCode);
|
|
151
|
+
const data = await execute(variables);
|
|
603
152
|
```
|
|
604
153
|
|
|
605
|
-
### Composant
|
|
606
|
-
|
|
154
|
+
### Composant AiAssistantButton
|
|
607
155
|
```tsx
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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
|
-
}
|
|
156
|
+
<AiAssistantButton
|
|
157
|
+
promptCode="ticket-analyzer"
|
|
158
|
+
variables={{ ticketTitle, ticketDescription }}
|
|
159
|
+
onResult={(result) => handleAnalysis(result)}
|
|
160
|
+
label="Analyser avec IA"
|
|
161
|
+
/>
|
|
654
162
|
```
|
|
655
163
|
|
|
656
|
-
---
|
|
657
|
-
|
|
658
164
|
## PATTERNS AVANCES
|
|
659
165
|
|
|
660
|
-
###
|
|
661
|
-
|
|
166
|
+
### Streaming Response
|
|
662
167
|
```typescript
|
|
663
|
-
|
|
664
|
-
|
|
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
|
-
}
|
|
168
|
+
const reader = response.body?.getReader();
|
|
169
|
+
while (true) { const { done, value } = await reader.read(); if (done) break; /* append chunk */ }
|
|
683
170
|
```
|
|
684
171
|
|
|
685
|
-
###
|
|
686
|
-
|
|
172
|
+
### Fallback Provider
|
|
687
173
|
```csharp
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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" };
|
|
174
|
+
foreach (var provider in providers.OrderBy(p => p.Priority)) {
|
|
175
|
+
var result = await _aiService.ExecutePromptAsync(promptId, variables, providerId: provider.Id);
|
|
176
|
+
if (result.Success) return result;
|
|
714
177
|
}
|
|
715
178
|
```
|
|
716
179
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
## CHECKLIST INTEGRATION IA
|
|
180
|
+
## CHECKLIST
|
|
720
181
|
|
|
721
182
|
```
|
|
722
|
-
□
|
|
723
|
-
□
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
□
|
|
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
|
|
183
|
+
□ Prompt cree (Code unique, Blocks, Variables, Version)
|
|
184
|
+
□ OutputSchema si reponse structuree
|
|
185
|
+
□ Service avec IAiCompletionService injecte
|
|
186
|
+
□ Gestion erreurs + Logging (tokens, temps)
|
|
187
|
+
□ Frontend: Hook/composant, Loading, Error handling
|
|
188
|
+
□ ProviderInstance avec limite budget
|
|
189
|
+
□ Tests: execution, variables, validation schema
|
|
745
190
|
```
|
|
746
191
|
|
|
747
|
-
---
|
|
748
|
-
|
|
749
192
|
## REGLES ABSOLUES
|
|
750
193
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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
|
-
---
|
|
194
|
+
| DO | DON'T |
|
|
195
|
+
|----|-------|
|
|
196
|
+
| IAiCompletionService pour executions | Appels API directs |
|
|
197
|
+
| OutputSchema pour reponses structurees | Reponses non validees |
|
|
198
|
+
| Gestion erreurs (API down, quota) | Ignorer les limites tokens |
|
|
199
|
+
| Logger executions (tokens, duree) | Hardcoder cles API |
|
|
200
|
+
| Variables pour contenu dynamique | Exposer prompts systeme au frontend |
|
|
763
201
|
|
|
764
202
|
## FICHIERS CLES
|
|
765
203
|
|
|
766
204
|
| Fichier | Role |
|
|
767
205
|
|---------|------|
|
|
768
206
|
| `Domain/AI/Prompts/Prompt.cs` | Entite prompt |
|
|
769
|
-
| `Domain/AI/Prompts/PromptBlock.cs` | Blocks du prompt |
|
|
770
207
|
| `Domain/AI/Schemas/OutputSchema.cs` | Schema validation |
|
|
771
|
-
| `Domain/AI/AiProvider.cs` | Provider IA |
|
|
772
|
-
| `Domain/AI/AiModel.cs` | Modele IA |
|
|
773
208
|
| `Domain/AI/AiProviderInstance.cs` | Instance configuree |
|
|
774
209
|
| `Application/Common/Interfaces/IAiCompletionService.cs` | Interface service |
|
|
775
|
-
| `Application/Common/Interfaces/IPromptService.cs` | Interface prompts |
|
|
776
210
|
| `Infrastructure/Services/AI/AiCompletionService.cs` | Implementation |
|
|
777
|
-
| `Infrastructure/Services/AI/PromptService.cs` | Implementation prompts |
|
|
778
211
|
| `web/src/services/api/aiApi.ts` | API frontend |
|