---
name: ai-prompt
description: |
  Integre les capacites IA dans les features SmartStack.
  Utiliser ce skill quand:
  - L'utilisateur veut integrer l'IA dans une fonctionnalite
  - L'utilisateur mentionne "prompt", "GPT", "Claude", "IA", "AI"
  - Creation d'un assistant ou chatbot
  - Generation de contenu automatique
  - Validation de reponses IA avec schema
  Types: Prompt, OutputSchema, Provider, Model
---

# Skill AI Prompt SmartStack

> **Architecture:** Prompt + Blocks → Provider + Model → Completion → Schema Validation
> Multi-provider: OpenAI, Anthropic (Claude), Azure OpenAI, Google Gemini

## QUAND CE SKILL S'ACTIVE

Claude invoque automatiquement ce skill quand il detecte :

| Declencheur | Exemple |
|-------------|---------|
| Demande explicite | "Integre GPT pour generer des descriptions" |
| Chatbot/Assistant | "Cree un assistant pour le support" |
| Generation contenu | "Genere automatiquement des reponses" |
| Analyse texte | "Analyse le sentiment des commentaires" |
| Mots-cles | "prompt", "GPT", "Claude", "IA", "completion", "LLM" |

---

## ARCHITECTURE IA

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                         AI COMPLETION FLOW                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  [FEATURE] ─────────────────────────────────────────────────────────────┐    │
│     │                                                                   │    │
│     ▼                                                                   │    │
│  ┌─────────────────────────────┐                                       │    │
│  │  IAiCompletionService       │                                       │    │
│  │  ExecutePromptAsync()       │                                       │    │
│  └─────────────┬───────────────┘                                       │    │
│                │                                                        │    │
│       ┌────────┴────────┐                                              │    │
│       ▼                 ▼                                              │    │
│  ┌─────────────┐   ┌─────────────────┐                                 │    │
│  │   Prompt    │   │ Provider        │                                 │    │
│  │   + Blocks  │   │ Instance        │                                 │    │
│  └──────┬──────┘   └────────┬────────┘                                 │    │
│         │                   │                                          │    │
│         ▼                   ▼                                          │    │
│  ┌─────────────────────────────────────┐                               │    │
│  │        RENDERED PROMPT              │                               │    │
│  │  System: {{instructions}}           │                               │    │
│  │  User: {{userInput}}                │                               │    │
│  └─────────────────┬───────────────────┘                               │    │
│                    │                                                   │    │
│                    ▼                                                   │    │
│  ┌─────────────────────────────────────┐                               │    │
│  │        EXTERNAL API                 │                               │    │
│  │  OpenAI / Claude / Azure / Gemini   │                               │    │
│  └─────────────────┬───────────────────┘                               │    │
│                    │                                                   │    │
│                    ▼                                                   │    │
│  ┌─────────────────────────────────────┐                               │    │
│  │        AI RESPONSE                  │                               │    │
│  │  { "content": "...", ... }          │                               │    │
│  └─────────────────┬───────────────────┘                               │    │
│                    │                                                   │    │
│       ┌────────────┴────────────┐                                      │    │
│       ▼                         ▼                                      │    │
│  ┌─────────────┐         ┌─────────────────┐                           │    │
│  │   RAW       │         │ OutputSchema    │                           │    │
│  │   Response  │         │ Validation      │                           │    │
│  └─────────────┘         └────────┬────────┘                           │    │
│                                   │                                    │    │
│                                   ▼                                    │    │
│                          ┌─────────────────┐                           │    │
│                          │ Typed Result<T> │                           │    │
│                          │ + Validation    │                           │    │
│                          └─────────────────┘                           │    │
│                                                                        │    │
└────────────────────────────────────────────────────────────────────────────┘
```

---

## ENTITES PRINCIPALES

### AiProvider

```csharp
// Providers disponibles (seed data)
- OpenAI (gpt-4o, gpt-4-turbo, gpt-3.5-turbo)
- Anthropic (claude-3-opus, claude-3-sonnet, claude-3-haiku)
- Azure OpenAI (deploiements custom)
- Google (gemini-pro, gemini-1.5-pro)
```

### AiModel

```csharp
// Proprietes cles
public string Code { get; }              // "gpt-4o"
public string ShortCode { get; }         // "GPT4O"
public ModelCategory Category { get; }   // TextGeneration, Embedding, etc.
public int ContextWindow { get; }        // 128000
public int MaxOutputTokens { get; }      // 16384
public decimal InputCostPerMillion { get; }
public decimal OutputCostPerMillion { get; }

// Capabilities
public bool SupportsVision { get; }
public bool SupportsFunctionCalling { get; }
public bool SupportsStreaming { get; }
```

### AiProviderInstance

```csharp
// Instance configuree pour un use-case
public string Code { get; }              // "support-assistant"
public string Name { get; }              // "Assistant Support"
public string SystemContext { get; }     // Prompt systeme personnalise
public string DefaultModel { get; }      // "gpt-4o"
public decimal MonthlyBudgetLimit { get; }
public decimal CurrentMonthUsage { get; }

// Securite
public string EncryptedApiKey { get; }   // Cle chiffree
```

### Prompt

```csharp
// Structure d'un prompt
public string Code { get; }              // "ticket-analyzer"
public string Name { get; }              // "Ticket Analyzer"
public string Version { get; }           // "1.0.0"
public PromptStatus Status { get; }      // Draft, Active, Deprecated
public bool IsTemplate { get; }          // Template reutilisable
public Guid? OutputSchemaId { get; }     // Schema de validation

// Blocks = Sections du prompt
public ICollection<PromptBlock> Blocks { get; }
```

### PromptBlock

```csharp
// Types de blocks
public enum PromptBlockType
{
    System,        // Instructions systeme
    User,          // Message utilisateur
    Assistant,     // Reponse exemple
    Tool,          // Definition d'outil
    Context,       // Contexte additionnel
    Examples       // Exemples few-shot
}

// Proprietes
public string Label { get; }             // "Instructions"
public string Content { get; }           // "Tu es un assistant..."
public int DisplayOrder { get; }
public bool IsRequired { get; }
public string Condition { get; }         // Condition d'inclusion
```

### OutputSchema

```csharp
// Schema JSON pour validation
public string Code { get; }              // "ticket-analysis"
public string Name { get; }              // "Ticket Analysis Schema"
public string JsonSchema { get; }        // JSON Schema valide
public string DotNetType { get; }        // "SmartStack.Application.AI.TicketAnalysisResult"
```

---

## WORKFLOW INTEGRATION IA

### ETAPE 1: Definir le Use Case

| Question | Impact |
|----------|--------|
| Quel type de contenu generer ? | Choix du modele |
| Reponse structuree requise ? | OutputSchema |
| Multi-langue ? | Variables langue |
| Budget limite ? | ProviderInstance avec limit |
| Temps reel requis ? | Streaming |

### ETAPE 2: Creer le Prompt

```csharp
// Via IPromptService
var promptId = await _promptService.CreatePromptAsync(new CreatePromptRequest
{
    Code = "ticket-analyzer",
    Name = "Ticket Analyzer",
    Description = "Analyse les tickets support pour categorisation",
    IsTemplate = false,
    Blocks = new[]
    {
        new CreateBlockRequest
        {
            BlockType = PromptBlockType.System,
            Label = "Instructions",
            Content = @"Tu es un expert en support technique.
Analyse le ticket fourni et retourne:
- Une categorie (technical, billing, general, urgent)
- Un score de priorite (1-5)
- Un resume en une phrase
- Des tags pertinents

Reponds UNIQUEMENT en JSON valide.",
            DisplayOrder = 1,
            IsRequired = true
        },
        new CreateBlockRequest
        {
            BlockType = PromptBlockType.User,
            Label = "Ticket",
            Content = "Ticket: {{ticketTitle}}\nDescription: {{ticketDescription}}",
            DisplayOrder = 2,
            IsRequired = true
        }
    }
});
```

### ETAPE 3: Definir le Schema de Validation

```csharp
// JSON Schema pour validation automatique
var schemaId = await _schemaService.CreateAsync(new CreateSchemaRequest
{
    Code = "ticket-analysis-result",
    Name = "Ticket Analysis Result",
    JsonSchema = @"{
        ""$schema"": ""http://json-schema.org/draft-07/schema#"",
        ""type"": ""object"",
        ""required"": [""category"", ""priority"", ""summary"", ""tags""],
        ""properties"": {
            ""category"": {
                ""type"": ""string"",
                ""enum"": [""technical"", ""billing"", ""general"", ""urgent""]
            },
            ""priority"": {
                ""type"": ""integer"",
                ""minimum"": 1,
                ""maximum"": 5
            },
            ""summary"": {
                ""type"": ""string"",
                ""maxLength"": 200
            },
            ""tags"": {
                ""type"": ""array"",
                ""items"": { ""type"": ""string"" },
                ""maxItems"": 5
            }
        }
    }",
    DotNetType = "SmartStack.Application.AI.TicketAnalysisResult"
});

// Lier au prompt
await _promptService.UpdatePromptAsync(promptId, new UpdatePromptRequest
{
    OutputSchemaId = schemaId
});
```

### ETAPE 4: Executer le Prompt

```csharp
// Execution simple (retourne string)
var result = await _aiCompletionService.ExecutePromptAsync(
    promptId,
    new Dictionary<string, object>
    {
        ["ticketTitle"] = ticket.Title,
        ["ticketDescription"] = ticket.Description
    });

if (result.Success)
{
    var content = result.Content; // JSON string
    _logger.LogInformation(
        "AI completed in {Ms}ms, {Input}/{Output} tokens",
        result.ExecutionTimeMs,
        result.InputTokens,
        result.OutputTokens);
}

// Execution avec validation typee
var typedResult = await _aiCompletionService
    .ExecutePromptWithValidationAsync<TicketAnalysisResult>(
        promptId,
        new Dictionary<string, object>
        {
            ["ticketTitle"] = ticket.Title,
            ["ticketDescription"] = ticket.Description
        });

if (typedResult.Success && typedResult.IsValid)
{
    var analysis = typedResult.Data; // TicketAnalysisResult type
    ticket.SetCategory(analysis.Category);
    ticket.SetPriority(analysis.Priority);
    ticket.AddTags(analysis.Tags);
}
else if (!typedResult.IsValid)
{
    _logger.LogWarning(
        "AI response validation failed: {Errors}",
        string.Join(", ", typedResult.ValidationErrors));
}
```

---

## SERVICES IA

### IAiCompletionService

```csharp
public interface IAiCompletionService
{
    // Execution basique
    Task<AiCompletionResult> CompleteAsync(
        AiCompletionRequest request,
        CancellationToken ct = default);

    // Execution par prompt ID
    Task<AiCompletionResult> ExecutePromptAsync(
        Guid promptId,
        Dictionary<string, object>? variables = null,
        Guid? providerId = null,
        Guid? modelId = null,
        CancellationToken ct = default);

    // Execution avec validation schema
    Task<AiValidatedCompletionResult<T>> ExecutePromptWithValidationAsync<T>(
        Guid promptId,
        Dictionary<string, object>? variables = null,
        Guid? providerId = null,
        Guid? modelId = null,
        CancellationToken ct = default);

    // Par code de prompt
    Task<AiValidatedCompletionResult<T>> ExecutePromptByCodeWithValidationAsync<T>(
        string promptCode,
        Dictionary<string, object>? variables = null,
        Guid? providerId = null,
        Guid? modelId = null,
        CancellationToken ct = default);

    // Provider systeme par defaut
    Task<Guid?> GetSystemProviderIdAsync(CancellationToken ct = default);
}
```

### IPromptService

```csharp
public interface IPromptService
{
    // CRUD Prompts
    Task<List<PromptDto>> GetAllPromptsAsync(PromptStatus? status, bool? isTemplate);
    Task<PromptDto> GetPromptByIdAsync(Guid promptId);
    Task<PromptDto> GetPromptByCodeAsync(string code, string? version = null);
    Task<PromptDto> GetActivePromptByCodeAsync(string code);

    // Rendu
    Task<string> RenderPromptAsync(string code, Dictionary<string, object>? variables = null);

    // Lifecycle
    Task<Guid> CreatePromptAsync(CreatePromptRequest request);
    Task UpdatePromptAsync(Guid promptId, UpdatePromptRequest request);
    Task<Guid> DuplicatePromptAsync(Guid promptId, string newVersion);
    Task ActivatePromptAsync(Guid promptId);
    Task DeactivatePromptAsync(Guid promptId);
    Task DeprecatePromptAsync(Guid promptId);
    Task DeletePromptAsync(Guid promptId);

    // Blocks
    Task AddBlockToPromptAsync(Guid promptId, CreateBlockRequest request);
    Task UpdateBlockAsync(Guid blockId, UpdateBlockRequest request);
    Task RemoveBlockFromPromptAsync(Guid promptId, Guid blockId);
    Task ReorderBlocksAsync(Guid promptId, Guid[] orderedBlockIds);
}
```

---

## TEMPLATES INTEGRATION

### Template Service avec IA

```csharp
public class TicketAnalysisService : ITicketAnalysisService
{
    private readonly IAiCompletionService _aiService;
    private readonly ITicketRepository _ticketRepository;
    private readonly ILogger<TicketAnalysisService> _logger;

    public TicketAnalysisService(
        IAiCompletionService aiService,
        ITicketRepository ticketRepository,
        ILogger<TicketAnalysisService> logger)
    {
        _aiService = aiService;
        _ticketRepository = ticketRepository;
        _logger = logger;
    }

    public async Task<TicketAnalysisResult?> AnalyzeTicketAsync(
        Guid ticketId,
        CancellationToken ct)
    {
        var ticket = await _ticketRepository.GetByIdAsync(ticketId, ct);
        if (ticket == null) return null;

        try
        {
            var result = await _aiService
                .ExecutePromptByCodeWithValidationAsync<TicketAnalysisResult>(
                    "ticket-analyzer",
                    new Dictionary<string, object>
                    {
                        ["ticketTitle"] = ticket.Title,
                        ["ticketDescription"] = ticket.Description,
                        ["ticketCategory"] = ticket.Category?.Name ?? "Unknown"
                    },
                    cancellationToken: ct);

            if (result.Success && result.IsValid)
            {
                _logger.LogInformation(
                    "Ticket {TicketId} analyzed: category={Category}, priority={Priority}",
                    ticketId, result.Data.Category, result.Data.Priority);

                return result.Data;
            }

            if (!result.Success)
            {
                _logger.LogError(
                    "AI analysis failed for ticket {TicketId}: {Error}",
                    ticketId, result.Error);
            }
            else if (!result.IsValid)
            {
                _logger.LogWarning(
                    "AI response invalid for ticket {TicketId}: {Errors}",
                    ticketId, string.Join(", ", result.ValidationErrors));
            }

            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Exception analyzing ticket {TicketId}", ticketId);
            return null;
        }
    }
}
```

### Template DTO Result

```csharp
// Application/AI/TicketAnalysisResult.cs
public class TicketAnalysisResult
{
    [JsonPropertyName("category")]
    public string Category { get; set; } = string.Empty;

    [JsonPropertyName("priority")]
    public int Priority { get; set; }

    [JsonPropertyName("summary")]
    public string Summary { get; set; } = string.Empty;

    [JsonPropertyName("tags")]
    public List<string> Tags { get; set; } = new();

    [JsonPropertyName("confidence")]
    public double? Confidence { get; set; }
}
```

### Template Controller

```csharp
[ApiController]
[Route("api/[controller]")]
public class TicketAnalysisController : ControllerBase
{
    private readonly ITicketAnalysisService _analysisService;

    [HttpPost("{ticketId}/analyze")]
    [RequirePermission(Permissions.Support.Tickets.Update)]
    public async Task<ActionResult<TicketAnalysisResult>> AnalyzeTicket(
        Guid ticketId,
        CancellationToken ct)
    {
        var result = await _analysisService.AnalyzeTicketAsync(ticketId, ct);

        if (result == null)
            return BadRequest(new { message = "Analysis failed" });

        return Ok(result);
    }
}
```

---

## FRONTEND INTEGRATION

### API IA

```typescript
// services/api/aiApi.ts
export const aiApi = {
  // Prompts
  getPrompts: (status?: PromptStatus, isTemplate?: boolean) =>
    apiClient.get<PromptDto[]>('/admin/ai/prompts', { params: { status, isTemplate } }),

  getPromptById: (id: string) =>
    apiClient.get<PromptDto>(`/admin/ai/prompts/${id}`),

  createPrompt: (data: CreatePromptRequest) =>
    apiClient.post<{ id: string }>('/admin/ai/prompts', data),

  // Execution
  executePrompt: (promptId: string, variables: Record<string, unknown>) =>
    apiClient.post<AiCompletionResult>(`/admin/ai/prompts/${promptId}/execute`, { variables }),

  previewPrompt: (promptId: string, variables: Record<string, unknown>) =>
    apiClient.post<{ rendered: string }>(`/admin/ai/prompts/${promptId}/preview`, { variables }),

  // Providers
  getProviders: () =>
    apiClient.get<AiProviderDto[]>('/admin/ai/providers'),

  getModels: (providerId?: string) =>
    apiClient.get<AiModelDto[]>('/admin/ai/models', { params: { providerId } }),

  // Schemas
  getSchemas: () =>
    apiClient.get<OutputSchemaDto[]>('/admin/ai/schemas'),

  createSchema: (data: CreateSchemaRequest) =>
    apiClient.post<{ id: string }>('/admin/ai/schemas', data),
};
```

### Hook useAiCompletion

```typescript
// hooks/useAiCompletion.ts
import { useState } from 'react';
import { aiApi } from '@/services/api/aiApi';

export function useAiCompletion<T = unknown>(promptCode: string) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [result, setResult] = useState<T | null>(null);

  const execute = async (variables: Record<string, unknown>) => {
    setLoading(true);
    setError(null);

    try {
      const response = await aiApi.executePrompt(promptCode, variables);

      if (response.success && response.isValid) {
        setResult(response.data as T);
        return response.data as T;
      } else {
        setError(response.error || 'Validation failed');
        return null;
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
      return null;
    } finally {
      setLoading(false);
    }
  };

  return { execute, loading, error, result };
}
```

### Composant AI Assistant

```tsx
// components/ai/AiAssistantButton.tsx
import { Sparkles, Loader2 } from 'lucide-react';
import { useAiCompletion } from '@/hooks/useAiCompletion';

interface AiAssistantButtonProps {
  promptCode: string;
  variables: Record<string, unknown>;
  onResult: (result: unknown) => void;
  label?: string;
}

export function AiAssistantButton({
  promptCode,
  variables,
  onResult,
  label = 'Analyser avec IA'
}: AiAssistantButtonProps) {
  const { execute, loading, error } = useAiCompletion(promptCode);

  const handleClick = async () => {
    const result = await execute(variables);
    if (result) {
      onResult(result);
    }
  };

  return (
    <div>
      <button
        onClick={handleClick}
        disabled={loading}
        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"
      >
        {loading ? (
          <Loader2 className="w-4 h-4 animate-spin" />
        ) : (
          <Sparkles className="w-4 h-4" />
        )}
        {label}
      </button>
      {error && (
        <p className="mt-2 text-sm text-red-500">{error}</p>
      )}
    </div>
  );
}
```

---

## PATTERNS AVANCES

### Pattern: Streaming Response

```typescript
// Pour les reponses longues, utiliser le streaming
async function streamCompletion(promptId: string, variables: Record<string, unknown>) {
  const response = await fetch(`/api/ai/prompts/${promptId}/stream`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ variables })
  });

  const reader = response.body?.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader!.read();
    if (done) break;

    const chunk = decoder.decode(value);
    // Afficher progressivement
    appendToOutput(chunk);
  }
}
```

### Pattern: Fallback Provider

```csharp
// Si le provider principal echoue, fallback sur un autre
public async Task<AiCompletionResult> CompleteWithFallbackAsync(
    Guid promptId,
    Dictionary<string, object> variables)
{
    var providers = await _providerService.GetActiveProvidersAsync();

    foreach (var provider in providers.OrderBy(p => p.Priority))
    {
        try
        {
            var result = await _aiService.ExecutePromptAsync(
                promptId, variables, providerId: provider.Id);

            if (result.Success)
                return result;
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex,
                "Provider {Provider} failed, trying next",
                provider.Name);
        }
    }

    return new AiCompletionResult { Success = false, Error = "All providers failed" };
}
```

---

## CHECKLIST INTEGRATION IA

```
□ Use case defini (generation, analyse, classification)
□ Prompt cree avec:
  □ Code unique
  □ Blocks structures (System, User, etc.)
  □ Variables documentees
  □ Version definie
□ OutputSchema cree si reponse structuree
□ Service cree avec injection IAiCompletionService
□ Gestion erreurs implementee
□ Logging des executions (tokens, temps, erreurs)
□ Frontend:
  □ Hook ou composant pour l'execution
  □ Loading state
  □ Error handling
  □ Affichage resultat
□ Tests:
  □ Prompt execute correctement
  □ Variables substituees
  □ Schema valide la reponse
  □ Fallback fonctionne
□ Budget/Usage:
  □ ProviderInstance avec limite
  □ Tracking des tokens
```

---

## REGLES ABSOLUES

1. **TOUJOURS** utiliser IAiCompletionService (jamais appel API direct)
2. **TOUJOURS** definir un OutputSchema pour les reponses structurees
3. **TOUJOURS** gerer les erreurs (API down, quota depasse, validation)
4. **TOUJOURS** logger les executions avec tokens et duree
5. **TOUJOURS** specifier la version du prompt
6. **TOUJOURS** utiliser des variables pour le contenu dynamique
7. **JAMAIS** hardcoder les cles API
8. **JAMAIS** exposer les prompts systeme au frontend
9. **JAMAIS** ignorer les limites de tokens
10. **JAMAIS** faire confiance aux reponses sans validation

---

## FICHIERS CLES

| Fichier | Role |
|---------|------|
| `Domain/AI/Prompts/Prompt.cs` | Entite prompt |
| `Domain/AI/Prompts/PromptBlock.cs` | Blocks du prompt |
| `Domain/AI/Schemas/OutputSchema.cs` | Schema validation |
| `Domain/AI/AiProvider.cs` | Provider IA |
| `Domain/AI/AiModel.cs` | Modele IA |
| `Domain/AI/AiProviderInstance.cs` | Instance configuree |
| `Application/Common/Interfaces/IAiCompletionService.cs` | Interface service |
| `Application/Common/Interfaces/IPromptService.cs` | Interface prompts |
| `Infrastructure/Services/AI/AiCompletionService.cs` | Implementation |
| `Infrastructure/Services/AI/PromptService.cs` | Implementation prompts |
| `web/src/services/api/aiApi.ts` | API frontend |
