# Claude Code и Anthropic SDK

AgentAPI отдаёт Anthropic-совместимый эндпоинт
[`POST /v1/ai/messages`](https://agentapi.ru/docs/endpoints/messages) с настоящим Anthropic SSE. Но путь
содержит сегмент `/ai`, и это создаёт нюанс, который надо понимать до настройки.

## Почему нельзя просто подменить base URL

Anthropic SDK и Claude Code дописывают к базовому URL фиксированный путь `/v1/messages`.
Нужный адрес — `/v1/**ai**/messages`, где лишний сегмент стоит **посередине**. Подставить
его через базовый URL невозможно:

```mermaid
flowchart TD
    A["ANTHROPIC_BASE_URL=<br/>https://api.agentapi.ru"] --> A1["→ /v1/messages"] --> A2["404 ❌"]
    B["ANTHROPIC_BASE_URL=<br/>https://api.agentapi.ru/ai"] --> B1["→ /ai/v1/messages"] --> B2["404 ❌"]
    C["Нужно попасть в"] --> C1["/v1/ai/messages ✅"]
```

Поэтому ниже — три рабочих варианта в порядке простоты.

## Вариант 1: прямые HTTP-запросы (работает сразу)

Если вы пишете код сами, а не подключаете готовый клиент, — просто вызывайте эндпоинт
по полному пути. Никаких обходных путей не нужно.

```bash
curl https://api.agentapi.ru/v1/ai/messages \
  -H "x-api-key: agentapi-ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-3.5-sonnet",
    "max_tokens": 300,
    "system": "Отвечай кратко.",
    "messages": [{"role": "user", "content": "Привет!"}]
  }'
```

```python
import os, requests

response = requests.post(
    "https://api.agentapi.ru/v1/ai/messages",
    headers={"x-api-key": os.environ["AGENTAPI_KEY"]},
    json={
        "model": "anthropic/claude-3.5-sonnet",
        "max_tokens": 300,
        "messages": [{"role": "user", "content": "Привет!"}],
    },
    timeout=120,
)

data = response.json()
print(data["content"][0]["text"])
```

Формат ответа — [Messages](https://agentapi.ru/docs/endpoints/messages).

## Вариант 2: Anthropic SDK с явным путём

SDK можно оставить ради типов и обработки ошибок, обойдя сборку пути через
низкоуровневый `post`:

```typescript
import Anthropic from "@anthropic-ai/sdk";
import type { Message } from "@anthropic-ai/sdk/resources/messages";

const client = new Anthropic({
  apiKey: process.env.AGENTAPI_KEY,
  baseURL: "https://api.agentapi.ru",
});

// Путь указывается целиком, поэтому `/v1/messages` не подставляется автоматически.
const message = await client.post<Message>("/v1/ai/messages", {
  body: {
    model: "anthropic/claude-3.5-sonnet",
    max_tokens: 300,
    messages: [{ role: "user", content: "Привет!" }],
  },
});

console.log(message.content);
```

```python
import os
from anthropic import Anthropic

client = Anthropic(
    api_key=os.environ["AGENTAPI_KEY"],
    base_url="https://api.agentapi.ru",
)

message = client.post(
    "/v1/ai/messages",
    body={
        "model": "anthropic/claude-3.5-sonnet",
        "max_tokens": 300,
        "messages": [{"role": "user", "content": "Привет!"}],
    },
    cast_to=object,
)

print(message)
```

Штатные вызовы `client.messages.create(...)` при этом **не** заработают — они собирают
путь сами. Учтите и то, что `post` — низкоуровневый метод: его сигнатура может меняться
между версиями SDK.

## Вариант 3: Claude Code через локальный прокси

Claude Code настраивается только переменной `ANTHROPIC_BASE_URL` и жёстко дописывает
`/v1/messages`, поэтому напрямую подключить его нельзя. Нужен локальный прокси,
переписывающий путь.

Минимальный вариант на Node.js (без зависимостей):

```javascript
// agentapi-proxy.mjs — запуск: node agentapi-proxy.mjs
import { createServer } from "node:http";

const UPSTREAM = "https://api.agentapi.ru";
const API_KEY = process.env.AGENTAPI_KEY;

if (!API_KEY) throw new Error("Задайте AGENTAPI_KEY");

createServer(async (req, res) => {
  // /v1/messages -> /v1/ai/messages, остальное проксируем как есть.
  const path = req.url.startsWith("/v1/messages")
    ? req.url.replace("/v1/messages", "/v1/ai/messages")
    : req.url;

  const chunks = [];
  for await (const chunk of req) chunks.push(chunk);

  const upstream = await fetch(`${UPSTREAM}${path}`, {
    method: req.method,
    headers: {
      "content-type": "application/json",
      "x-api-key": API_KEY,
    },
    body: chunks.length ? Buffer.concat(chunks) : undefined,
  });

  res.writeHead(upstream.status, {
    "content-type":
      upstream.headers.get("content-type") ?? "application/json",
    "cache-control": "no-cache",
  });

  // Ответ может быть SSE, поэтому передаём его потоком, а не целиком.
  if (upstream.body) {
    for await (const chunk of upstream.body) res.write(chunk);
  }

  res.end();
}).listen(8787, () => console.log("Прокси: http://localhost:8787"));
```

Запуск:

```bash
export AGENTAPI_KEY=agentapi-ВАШ_КЛЮЧ
node agentapi-proxy.mjs
```

В другом терминале:

```bash
export ANTHROPIC_BASE_URL=http://localhost:8787
export ANTHROPIC_AUTH_TOKEN=agentapi-ВАШ_КЛЮЧ
export ANTHROPIC_MODEL=anthropic/claude-3.5-sonnet

claude
```

Прокси слушает только localhost и подставляет ключ сам — не публикуйте его в сеть.

### Альтернатива на LiteLLM

Если прокси уже есть в инфраструктуре, тот же результат даёт
[LiteLLM](https://docs.litellm.ai):

```yaml
# litellm-config.yaml — запуск: litellm --config litellm-config.yaml
model_list:
  - model_name: claude-3.5-sonnet
    litellm_params:
      model: openai/anthropic/claude-3.5-sonnet
      api_base: https://api.agentapi.ru/v1/ai
      api_key: os.environ/AGENTAPI_KEY
```

Здесь используется OpenAI-совместимый эндпоинт, который подставляется базовым URL без
проблем, а Anthropic-формат для клиента обеспечивает сам LiteLLM.

## Что было бы правильным решением

Нюанс с путём — следствие того, что внешний модуль смонтирован как `/v1` + контроллер
`ai`. Достаточно добавить алиас `POST /v1/messages` на тот же обработчик, и Claude Code
с Anthropic SDK заработают без прокси. Сейчас такого алиаса нет — если он важен для вашего
сценария, напишите на [agent@agentapi.ru](mailto:agent@agentapi.ru).

## Диагностика

| Симптом | Причина |
|---|---|
| `404` на `/v1/messages` | Ожидаемо: правильный путь — `/v1/ai/messages` |
| `401 Invalid API key format` | В `ANTHROPIC_AUTH_TOKEN` не ключ `agentapi-*` |
| Claude Code не видит модель | В `ANTHROPIC_MODEL` нужен полный слаг: `anthropic/claude-3.5-sonnet` |
| `400 Negative or zero balance` | Пополните баланс |
