# Section 1 — Official Anthropic C# SDK Fundamentals

> **Summary — what this page covers**
> The foundation for everything on Day 2: install and wire up the official Anthropic NuGet
> package, make single- and multi-turn calls, build a `ClaudeService` abstraction, manage
> conversation history, and apply prompt caching and Polly retries. Get to a working API call
> within the first 20 minutes. Pair with **Lab 1**.

**9:00 – 10:30 AM · 90 min** — 45 min lecture/demo + 45 min lab

## Learning objectives

- Install/configure the official **Anthropic** NuGet package in ASP.NET Core
- Register `AnthropicClient` via DI with the options pattern
- Make async single-turn and multi-turn calls
- Design a `ClaudeService` abstraction following .NET best practices
- Manage conversation history across HTTP requests (`IDistributedCache`)
- Apply **prompt caching** to cut cost on repeated system prompts
- Handle errors, rate limits, transient failures with **Polly**
- Understand `IChatClient` from `Microsoft.Extensions.AI`

## Content

### Block 1A — SDK landscape & setup (≈20 min)

**The .NET SDK landscape.** Use the **official `Anthropic` package (v12.x)** — it's the one
Anthropic maintains and the one this workshop targets. Community options exist (`Anthropic.SDK`,
`Claudia`) and are fine, but the official package is the reference. ⚠️ Watch the name: the old
`tryAGI.Anthropic` v3.x is a *different lineage* — don't pull it by mistake.

**Install & setup.** Add the package, store the key in user-secrets (never commit it), bind it with
the options pattern, and register the client in DI:

```bash
dotnet add package Anthropic
```

```csharp
using Anthropic;

// Bind Anthropic:ApiKey from configuration/user-secrets via the options pattern,
// then register the client. AnthropicClient reads ANTHROPIC_API_KEY by default,
// or set it explicitly from your bound options.
builder.Services.AddSingleton(new AnthropicClient {
    ApiKey = builder.Configuration["Anthropic:ApiKey"]
});
```

> **Register `AnthropicClient` as a Singleton.** It holds an `HttpClient` internally — registering
> it Scoped or Transient creates a new socket per request and leads to **socket exhaustion** under
> load. This is the same reason you use `IHttpClientFactory` elsewhere. Your `IClaudeService`
> wrapper can be Scoped; the client underneath is Singleton.

### Block 1B — First API calls (≈35 min)

**Single-turn message call.** Everything goes through `client.Messages.Create`: pick a model, set a
max-tokens ceiling, pass the messages. Read `response.Usage` for the token counts (and cost):

```csharp
using Anthropic.Models.Messages;

var response = await client.Messages.Create(new MessageCreateParams {
    Model = Model.ClaudeSonnet4_6,        // default for app features; Haiku for cheap, Opus for hard
    MaxTokens = 1024,
    Messages = [new() { Role = Role.User, Content = "Recommend a book like Dune." }],
});

// ContentBlock is a union — filter to the text blocks
foreach (var text in response.Content.Select(b => b.Value).OfType<TextBlock>())
    Console.WriteLine(text.Text);

Console.WriteLine($"in: {response.Usage.InputTokens}, out: {response.Usage.OutputTokens}");
```

**System prompts & multi-turn.** The **system prompt is your assistant's constructor** — it scopes
the assistant to BookTracker once, up front. The API is **stateless**, so multi-turn means *you*
keep the history: `IClaudeService` maps your stored history → a message list and appends the new
user turn on every call.

**Prompt caching.** Mark stable, reused content (the system prompt, RAG context, few-shot examples)
with `CacheControlEphemeral` and pay up to **~90% less** for the cached input on repeat calls:

```csharp
System = new List<TextBlockParam> {
    new() { Text = longSystemPrompt, CacheControl = new CacheControlEphemeral() },
},
```

Cache when the prefix is **stable and ≥ ~1,000 tokens** and **shared across requests**. Verify it's
working by reading `response.Usage.CacheCreationInputTokens` (first call) then
`CacheReadInputTokens` (subsequent calls).

### Block 1C — Error handling & chat endpoint (≈25 min)

**Polly retry.** Wrap calls in a retry policy for **429 (rate limit)** and **529 (overloaded)** —
both are transient and safe to retry with exponential backoff. (The SDK also retries 429/5xx
automatically with a default of 2 retries; add Polly when you want custom backoff or jitter.)

**Wire `/api/chat`.** A Minimal API endpoint that takes a user message + session id, loads history
from `IDistributedCache`, calls `IClaudeService`, persists the new turn, and returns the reply.

> **`IChatClient` from `Microsoft.Extensions.AI`.** The official SDK also plugs into the
> provider-agnostic `IChatClient` abstraction — useful if you want to swap providers or use the
> broader Microsoft.Extensions.AI tooling. We use the native `AnthropicClient` directly today for
> full access to caching, tools, and streaming.

## Demos referenced here

- **First working call** · **Multi-turn with persisted history** · **Prompt-cache token counts**
  (show `CacheCreationInputTokens` then `CacheReadInputTokens`). [Scripts in `_instructor/demo-plan.md`.]

→ Continue to [**Lab 1**](04-lab-1-chat.md).
