# LLMApi: Contexte de l'API: Maintenir la cohérence entre les appels d'API Mock

<!--category-- AI, LLM, LLMApi, ASP.NET Core, API, Nuget, mockllmapi, AI-Article-->
<datetime class="hidden">2025-11-05T13:52</datetime>

> NOTE: Cet article est principalement AI généré dans le cadre de mon paquet nuget en tant que documentation de diffusion.

# Introduction

Lorsque vous construisez et testez des applications, l'une des plus grandes difficultés avec les API-moquettes traditionnelles est leur nature instaurée. Chaque demande retourne des données complètement aléatoires sans rapport aux appels antérieursM SK2 Si vous reçois un utilisateur ayant une ID 123, alors reçoivez leurs commandesMska4 làM Ska5 il n'y a aucune garantie que la commande fera référence à cette même ID d'utilisateurMSKA6

**Contextes de l'API** Résoudre ce problème en donnant à votre API modèle une mémoire.

[![NuGet](https://img.shields.io/nuget/v/mostlylucid.mockllmapi.svg)](https://www.nuget.org/packages/mostlylucid.mockllmapi)
[![NuGet](https://img.shields.io/nuget/dt/mostlylucid.mockllmapi.svg)](https://www.nuget.org/packages/mostlylucid.mockllmapi)

## Vous pouvez trouver le[ GitHub ici ](https://github.com/scottgal/LLMApi)pour le projet, tous les domaines publics, etc.

[TOC]

## Le problème: Chaos sans état

Les API modèles traditionnels génèrent des données indépendamment de chaque demande:

```mermaid
sequenceDiagram
    participant Client
    participant MockAPI
    participant LLM

    Client->>MockAPI: GET /users/1
    MockAPI->>LLM: Generate user data
    LLM-->>MockAPI: {"id": 42, "name": "Alice"}
    MockAPI-->>Client: User data

    Client->>MockAPI: GET /orders?userId=42
    MockAPI->>LLM: Generate order data
    LLM-->>MockAPI: {"userId": 99, ...}
    MockAPI-->>Client: Order data (userId mismatch!)
```

Remarquez le problème? L'utilisateur avait une ID 42, mais l'ordonnance est retournée avec la userId 99. Il n'y a pas de cohérence entre les appels connexesM SK4

## La solution: Memory contextuelle

Avec Contextes API,, l’API-moquette maintient un contexte partagé à travers les demandes connexes:

```mermaid
sequenceDiagram
    participant Client
    participant MockAPI
    participant Context as Context Manager
    participant LLM

    Client->>MockAPI: GET /users/1?context=session-1
    MockAPI->>Context: Get history for "session-1"
    Context-->>MockAPI: (empty - first call)
    MockAPI->>LLM: Generate user (no context)
    LLM-->>MockAPI: {"id": 42, "name": "Alice"}
    MockAPI->>Context: Store: GET /users/1 → {"id": 42, ...}
    MockAPI-->>Client: User data

    Client->>MockAPI: GET /orders?context=session-1
    MockAPI->>Context: Get history for "session-1"
    Context-->>MockAPI: Previous call: user with id=42, name=Alice
    MockAPI->>LLM: Generate order (with context history)
    LLM-->>MockAPI: {"userId": 42, "customerName": "Alice", ...}
    MockAPI->>Context: Store: GET /orders → {"userId": 42, ...}
    MockAPI-->>Client: Order data (consistent!)
```

Maintenant, le LLM voit l’appel de l’utilisateur précédent et génère des commandes qui renvoient à la même ID et au même nom d’utilisateur.

## Comment cela fonctionne

### Architecture

Le système de contexte comprend trois composantes principales:

```mermaid
graph TD
    A[HTTP Request] --> B[ContextExtractor]
    B --> C{Context Name?}
    C -->|Yes| D[OpenApiContextManager]
    C -->|No| E[Generate without context]
    D --> F[Retrieve Context History]
    F --> G[PromptBuilder]
    E --> G
    G --> H[LLM]
    H --> I[Response]
    I --> J{Context Name?}
    J -->|Yes| K[Store in Context]
    J -->|No| L[Return Response]
    K --> L
```

**1. ContextExtractor** - Extrait le nom du contexte de la demande
**2. OpenApiContextManager** - Génie l'entreposage et la récupération du contexte
**3. PromptBuilder** - Comprend l'historique du contexte dans les prompts LLM

### Entreposage du contexte

Les contextes sont stockés dans la mémoire -à l'aide d'un thread -safe `ConcurrentDictionary`:

```csharp
public class OpenApiContextManager
{
    private readonly ConcurrentDictionary<string, ApiContext> _contexts;
    private const int MaxRecentCalls = 15;
    private const int SummarizeThreshold = 20;

    public void AddToContext(
        string contextName,
        string method,
        string path,
        string? requestBody,
        string responseBody)
    {
        var context = _contexts.GetOrAdd(contextName, _ => new ApiContext
        {
            Name = contextName,
            CreatedAt = DateTimeOffset.UtcNow,
            RecentCalls = new List<RequestSummary>(),
            SharedData = new Dictionary<string, string>(),
            TotalCalls = 0
        });

        context.RecentCalls.Add(new RequestSummary
        {
            Timestamp = DateTimeOffset.UtcNow,
            Method = method,
            Path = path,
            RequestBody = requestBody,
            ResponseBody = responseBody
        });

        ExtractSharedData(context, responseBody);

        if (context.RecentCalls.Count > MaxRecentCalls)
        {
            SummarizeOldCalls(context);
        }
    }
}
```

### Sommaire automatique

Pour empêcher le contexte de croître indéfiniment et d'excéder les limites des tokens LLM,, le système résume automatiquement les anciens appels lorsque le nombre dépasse M SK1

```mermaid
graph LR
    A[20+ Calls] --> B[Keep 15 Most Recent]
    A --> C[Summarize Older Calls]
    B --> D[Full Request/Response]
    C --> E[Summary: 'GET /users - called 5 times']
    D --> F[Included in LLM Prompt]
    E --> F
```

```csharp
private void SummarizeOldCalls(ApiContext context)
{
    var toSummarize = context.RecentCalls
        .Take(context.RecentCalls.Count - MaxRecentCalls)
        .ToList();

    var summary = new StringBuilder();
    summary.AppendLine($"Earlier calls ({toSummarize.Count}):");

    var groupedByPath = toSummarize
        .GroupBy(c => $"{c.Method} {c.Path.Split('?')[0]}");

    foreach (var group in groupedByPath)
    {
        summary.AppendLine($"  {group.Key} - called {group.Count()} time(s)");
    }

    context.ContextSummary = summary.ToString();
    context.RecentCalls.RemoveRange(0, toSummarize.Count);
}
```

### Extraction de données partagées

Le gestionnaire de contexte extrait automatiquement des identifiants communs des réponses pour les rendre facilement accessibles:

```csharp
private void ExtractSharedData(ApiContext context, string responseBody)
{
    using var doc = JsonDocument.Parse(responseBody);
    var root = doc.RootElement;

    if (root.ValueKind == JsonValueKind.Array && root.GetArrayLength() > 0)
    {
        var firstItem = root[0];
        ExtractValueIfExists(context, firstItem, "id", "lastId");
        ExtractValueIfExists(context, firstItem, "userId", "lastUserId");
        ExtractValueIfExists(context, firstItem, "name", "lastName");
        ExtractValueIfExists(context, firstItem, "email", "lastEmail");
    }
    else if (root.ValueKind == JsonValueKind.Object)
    {
        ExtractValueIfExists(context, root, "id", "lastId");
        ExtractValueIfExists(context, root, "userId", "lastUserId");
        ExtractValueIfExists(context, root, "name", "lastName");
        // ... more common patterns
    }
}
```

Cela permet au système de suivre l’ID d’utilisateur le plus récent, ID d’ordonnance, etc.

## Utilisation de contextes

### Trois façons de spécifier le contexte

Vous pouvez transmettre le nom du contexte de trois façons différentes, avec cette ordonnance de priorité:

**Paramètre de la demande 1.** (priorité la plus élevéeM SK1

```http
GET /api/mock/users?context=my-session
GET /api/mock/users?api-context=my-session
```

**2. Intitulé HTTP**

```http
GET /api/mock/users
X-Api-Context: my-session
```

**3. Organe de demande**

```http
POST /api/mock/orders
Content-Type: application/json

{
  "context": "my-session",
  "shape": {"orderId": 0, "userId": 0}
}
```

### Types de point final appuyés

Contextes fonctionnent à travers **tous** types de point final:

#### REST APIs

```http
GET /api/mock/users/123?context=session-1
```

#### Appareils d'aide à la diffusion

```http
GET /api/mock/stream/stock-prices?context=trading-session
Accept: text/event-stream
```

#### GraphQL

```http
POST /graphql?context=my-app
Content-Type: application/json

{
  "query": "{ users { id name } }"
}
```

#### SignalR (par la configuration)

```json
{
  "mostlylucid.mockllmapi": {
    "HubContexts": [
      {
        "Name": "stock-ticker",
        "Description": "Real-time stock prices",
        "ApiContextName": "stocks-session",
        "Shape": "{\"symbol\":\"string\",\"price\":0}"
      }
    ]
  }
}
```

## Cases d'utilisation mondiale Real-

### Case d'utilisation 1: E-Commerce Flow

Simulation d’une expérience complète de l’achat avec des données uniformes sur les utilisateurs et les commandes:

```http
### 1. Create user
POST /api/mock/users?context=checkout-flow
{
  "shape": {
    "userId": 0,
    "name": "string",
    "email": "string",
    "address": {"street": "string", "city": "string"}
  }
}

### Response: {"userId": 42, "name": "Alice", ...}

### 2. Create cart (will reference same user)
POST /api/mock/cart?context=checkout-flow
{
  "shape": {
    "cartId": 0,
    "userId": 0,
    "items": [{"productId": 0, "quantity": 0}]
  }
}

### Response: {"cartId": 123, "userId": 42, ...}

### 3. Create order (consistent user and cart)
POST /api/mock/orders?context=checkout-flow
{
  "shape": {
    "orderId": 0,
    "userId": 0,
    "cartId": 0,
    "total": 0
  }
}

### Response: {"orderId": 789, "userId": 42, "cartId": 123, ...}
```

### Utiliser le cas 2: Simulation du prix des stocks

Gérer des mouvements réalistes du prix des actions au lieu de valeurs aléatoires:

```http
### First call - establishes baseline
GET /api/mock/stocks?context=market-data
    &shape={"symbol":"string","price":0,"volume":0}

### Response: {"symbol": "ACME", "price": 145.50, "volume": 10000}

### Second call - price changes realistically
GET /api/mock/stocks?context=market-data
    &shape={"symbol":"string","price":0,"volume":0}

### Response: {"symbol": "ACME", "price": 146.20, "volume": 12000}
### Notice: Same symbol, price increased by $0.70 (realistic)

### Third call - continues the trend
GET /api/mock/stocks?context=market-data
    &shape={"symbol":"string","price":0,"volume":0}

### Response: {"symbol": "ACME", "price": 145.80, "volume": 11500}
### Notice: Price fluctuates but stays in realistic range
```

Sans contexte, chaque appel retournerait un symbole et un prix complètement aléatoires. Avec le contexte , la LLM maintient le même stock et ajuste les prix de façon réalisteM SK3

### Utiliser le cas 3: Progression de l'état du jeu

Suivi des progrès du joueur dans les sessions de jeu:

```http
### Start game
POST /api/mock/game/start?context=game-session-123
{
  "shape": {
    "playerId": 0,
    "level": 0,
    "health": 0,
    "score": 0,
    "inventory": []
  }
}

### Response: {"playerId": 42, "level": 1, "health": 100, "score": 0}

### Complete quest
POST /api/mock/game/quest?context=game-session-123
{
  "shape": {
    "playerId": 0,
    "level": 0,
    "score": 0,
    "reward": {"item": "string", "value": 0}
  }
}

### Response: {"playerId": 42, "level": 2, "score": 500,
###           "reward": {"item": "Sword", "value": 100}}
### Notice: Same player, level increased, score increased

### Get stats
GET /api/mock/game/player?context=game-session-123
    &shape={"playerId":0,"level":0,"health":0,"score":0}

### Response: {"playerId": 42, "level": 2, "health": 100, "score": 500}
### Notice: Consistent with quest completion
```

## Context Management API

### Liste de tous les contextes

```http
GET /api/openapi/contexts

### Response:
{
  "contexts": [
    {
      "name": "session-1",
      "totalCalls": 5,
      "recentCallCount": 5,
      "sharedDataCount": 3,
      "createdAt": "2025-01-15T10:00:00Z",
      "lastUsedAt": "2025-01-15T10:05:00Z",
      "hasSummary": false
    }
  ],
  "count": 1
}
```

### Obtenir les détails du contexte

```http
GET /api/openapi/contexts/session-1

### Response shows full context including:
### - All recent calls with timestamps
### - Extracted shared data (IDs, names, emails)
### - Summary of older calls (if any)
```

### Supprimer un contexte spécifique

```http
DELETE /api/openapi/contexts/session-1
```

### Supprimer tous les contextes

```http
DELETE /api/openapi/contexts
```

## Détails de mise en œuvre

### Intégration dans les gestionnaires de demandes

Chacun des gestionnaires de demande (REST, StreamingM SK2 GraphQL , SignalRMSC4 suit le même schéma

```csharp
public async Task<string> HandleRequestAsync(
    string method,
    string fullPathWithQuery,
    string? body,
    HttpRequest request,
    HttpContext context,
    CancellationToken cancellationToken = default)
{
    // 1. Extract context name from request
    var contextName = _contextExtractor.ExtractContextName(request, body);

    // 2. Get context history if context specified
    var contextHistory = !string.IsNullOrWhiteSpace(contextName)
        ? _contextManager.GetContextForPrompt(contextName)
        : null;

    // 3. Build prompt with context history
    var prompt = _promptBuilder.BuildPrompt(
        method, fullPathWithQuery, body, shapeInfo,
        streaming: false, contextHistory: contextHistory);

    // 4. Get response from LLM
    var response = await _llmClient.GetCompletionAsync(prompt, cancellationToken);

    // 5. Store in context if context name provided
    if (!string.IsNullOrWhiteSpace(contextName))
    {
        _contextManager.AddToContext(
            contextName, method, fullPathWithQuery, body, response);
    }

    return response;
}
```

### Contexte dans les prompts LLM

Lorsqu'un contexte existe, son histoire est incluse dans le prompt LLM:

```
TASK: Generate a varied mock API response.
RULES: Output ONLY valid JSON. No markdown, no comments.

API Context: session-1
Total calls in session: 3

Shared data to maintain consistency:
  lastId: 42
  lastName: Alice
  lastEmail: alice@example.com

Recent API calls:
  [10:00:05] GET /users/42
    Response: {"id": 42, "name": "Alice", "email": "alice@example.com"}
  [10:00:12] GET /orders?userId=42
    Response: {"orderId": 123, "userId": 42, "items": [...]}

Generate a response that maintains consistency with the above context.

Method: POST
Path: /shipping/123
Body: {"orderId": 123}
```

Le LLM voit tous les appels antérieurs et génère des réponses qui renvoient aux mêmes ID, nomsM SK1 et autres données, en conservant la cohérence .

## Pratiques exemplaires

### 1. Utiliser des noms de contexte descriptifs

```http
 Bad:  ?context=test1
 Good: ?context=user-checkout-flow-jan15
```

### 2. Supprimer les contextes lorsque cela est fait

Les contextes persistent dans la mémoire jusqu’à ce que le serveur soit explicitement nettoyé ou relancé:

```http
### After completing your test scenario
DELETE /api/openapi/contexts/user-checkout-flow-jan15
```

### 3. Contextes partagés à travers les points finals connexes

Utiliser le même nom de contexte pour tous les appels connexes:

```http
GET /api/mock/users?context=demo-session
GET /api/mock/orders?context=demo-session
GET /api/mock/shipping?context=demo-session
```

### 4. Taille du contexte de contrôle

Vérifiez les détails du contexte pour voir combien d'appels sont stockés:

```http
GET /api/openapi/contexts/demo-session
```

Si vous avez beaucoup d’appels (>100), envisagez de débloquer et de commencer de nouveau pour éviter des problèmes de longueur immédiate

### 5. Combinez avec les spécifications OpenAPI

Pour un réalisme maximal, utiliser des contextes avec les spécifications OpenAPI:

```http
### Load spec with context
POST /api/openapi/specs
{
  "name": "petstore",
  "source": "https://petstore3.swagger.io/api/v3/openapi.json",
  "basePath": "/petstore",
  "contextName": "petstore-session"
}

### All petstore endpoints will share the same context
```

## Considérations relatives au rendement

### Utilisation de la mémoire

Chaque contexte stocke:

- Jusqu’à 15 appels récents (requête complèteM SK2réponse)
- Résumé des appels anciens (compressed)
- Extrait de données partagées (small dictionary)

**Mémoire type par contexte**: ~50-200 Kb selon les tailles de la réponse

### Sécurité des fils

`OpenApiContextManager` utilisations `ConcurrentDictionary` pour le thread-entreposage sécuritaire. Plusieurs demandes peuvent être lues en toute sécuritéM SK2écrire des contextes simultanément .

### Limites du token LLM

L’historique du contexte est inclus dans chaque prompt. Si vous avez des réponses très importantes ou plusieurs appels, vous pouvez approcher les limites de tokenM SK2 La sommation automatique aide à prévenir ce type d’appels , mais pour des séances très longuesSSK4 envisagez de nettoyer périodiquement le contexteMSC5

## Limitations

1. **En-Memory seulement** - Contextes sont perdus lors du redémarrage du serveur
2. **Aucune persistance** - Ne convient pas à l'entreposage de données de production
3. **Un seul serveur** - Contextes ne synchronisent pas ' entre plusieurs instances de serveur
4. **LLM dépendant** - La qualité de la conformité dépend de l’aptitude du LLM à suivre les instructions

## Conclusion

Contextes de l’API Transformer des APIs modèles instaurés en simulations instaurées qui maintiennent la cohérence entre les appels connexes. Que vous testiez un flux d’e-commerce -, simuliez une situation réelle

L’extraction automatique des données partagées, la synthèse intelligente, et l’intégration sans fil entre tous les types de points d’entrée rendent les contextes un outil puissant pour le test et le développement réalistes d’API .

Pour des scénarios plus avancés, combinaison de contextes avec les spécifications OpenAPI pour créer pleinement-APIs mock caractéristiques qui se comportent comme la chose réelle .