> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chattler.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Идемпотентӣ

> Дархости чатро бехатар такрор кунед, бе ҷавоби дуюм ва бе пардохти дуюм.

Ҳарду эндпоинти чат, `POST /v1/agents/{agent_id}/chat` ва `POST /v1/agents/{agent_id}/chat/stream`, сарлавҳаи `Idempotency-Key`-ро **талаб мекунанд**. Дархост бе он `422 validation_error` аст.

## Интихоби калид

Ҳар қимати 1-255 аломат, ки барои ҳар дархости мантиқӣ беназир аст. UUID, ки дар лаҳзаи қарори фиристодани паём тавлид мешавад, интихоби соддатарин аст. Калидҳо дар доираи калиди API-и шумо амал мекунанд, бинобар ин дар бораи мувофиқат бо калидҳои дигар ташвиш кашидан лозим нест.

```http theme={null}
Idempotency-Key: 7f20a5f0-5dde-48cf-94aa-38acbbd218e0
```

## Такрор чӣ бармегардонад

Такрор бо ҳамон `Idempotency-Key` **ва ҳамон бадан** натиҷаи нигоҳдошташударо бармегардонад: `request_id`-и аслӣ, ҳамон паёми ассистент ва ҳамон `usage`. Ҷавоби дуюм тавлид намешавад ва ҳеҷ чиз дубора пардохт намешавад.

Сабтҳо **24 соат** нигоҳ дошта мешаванд. Пас аз он ҳамон калид дархости навро оғоз мекунад.

<Note>
  Натиҷаи нигоҳдошташуда он аст, ки муҳити иҷро ба ҳар ҳол сабт кардааст: паёми ассистент бо нишони идентификатори дархост ва истифодаи LLM, ки таҳти ин идентификатор пардохт шудааст. Бинобар ин такрор ҳеҷ гоҳ моделро даъват намекунад ва ба баланс даст намезанад, ҳатто агар кӯшиши аввал дар нимароҳ қатъ шуда бошад.
</Note>

## Ихтилофҳо

| Статус | Код                       | Кай                                                                                                                               |
| ------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `409`  | `idempotency_in_progress` | Кӯшиши аввал ҳанӯз иҷро мешавад. `Retry-After` сония (пешфарз 2) интизор шавед ва бо ҳамон калид такрор кунед.                    |
| `409`  | `idempotency_conflict`    | Ҳамон калид бо бадани дигар фиристода шуд (`external_user_id` ё `message`-и дигар). Барои дархости нав калиди нав истифода баред. |

## Ҷараён ва қатъи пайвастшавӣ

Қоидаҳо дар эндпоинти ҷараёнӣ ҳамон гунаанд. Агар мизоҷи шумо дар миёнаи ҷараён пайвастшавиро қатъ кунад, ҷавоб тавлид ва нигоҳ дошта мешавад. Такрор бо ҳамон калид `response.created`-ро ва бевосита пас аз он `response.completed`-ро бо натиҷаи нигоҳдошташуда мефиристад, бе такрори делтаҳо. Ниг. [Ҷавоби ҷараёнӣ](/tj/guides/streaming).

## Ҳалқаи тавсияшудаи такрор

<CodeGroup>
  ```bash curl theme={null}
  # One Idempotency-Key for the whole logical request; retried on transient codes.
  key=$(uuidgen)
  for attempt in 1 2 3 4 5; do
    status=$(curl -s -o body.json -D headers.txt -w '%{http_code}' \
      -X POST "$CHATTLER_BASE_URL/v1/agents/$CHATTLER_AGENT_ID/chat" \
      -H "Authorization: Bearer $CHATTLER_API_KEY" \
      -H "Idempotency-Key: $key" \
      -H "Content-Type: application/json" \
      -d '{"external_user_id": "customer-123", "message": "I want to know the price"}')
    if [ "$status" -lt 400 ]; then cat body.json; break; fi
    case "$(jq -r '.error.code' body.json)" in
      idempotency_in_progress|conversation_busy|rate_limit_exceeded|api_rate_limit_unavailable|service_unavailable)
        sleep "$(grep -i '^Retry-After:' headers.txt | tr -d '\r' | awk '{print $2}')" ;;
      *) cat body.json; break ;;
    esac
  done
  ```

  ```python Python theme={null}
  import time
  import uuid

  import httpx


  def chat_with_retry(external_user_id: str, message: str, attempts: int = 5) -> dict:
      idempotency_key = str(uuid.uuid4())  # one key for the whole logical request
      for _ in range(attempts):
          response = httpx.post(
              f"{BASE_URL}/v1/agents/{AGENT_ID}/chat",
              headers={**HEADERS, "Idempotency-Key": idempotency_key},
              json={"external_user_id": external_user_id, "message": message},
              timeout=120,
          )
          if response.status_code < 400:
              return response.json()
          code = response.json()["error"]["code"]
          if code in ("idempotency_in_progress", "conversation_busy", "rate_limit_exceeded",
                      "api_rate_limit_unavailable", "service_unavailable"):
              time.sleep(float(response.headers.get("Retry-After", "2")))
              continue
          raise RuntimeError(code)
      raise TimeoutError("gave up")
  ```

  ```javascript Node theme={null}
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
  const RETRYABLE = new Set([
    "idempotency_in_progress",
    "conversation_busy",
    "rate_limit_exceeded",
    "api_rate_limit_unavailable",
    "service_unavailable",
  ]);

  async function chatWithRetry(externalUserId, message, attempts = 5) {
    const idempotencyKey = crypto.randomUUID(); // one key for the whole logical request
    for (let i = 0; i < attempts; i++) {
      const response = await fetch(`${baseUrl}/v1/agents/${agentId}/chat`, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Idempotency-Key": idempotencyKey,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ external_user_id: externalUserId, message }),
      });
      const body = await response.json();
      if (response.ok) return body;
      if (!RETRYABLE.has(body.error.code)) throw new Error(body.error.code);
      await sleep(Number(response.headers.get("Retry-After") ?? 2) * 1000);
    }
    throw new Error("gave up");
  }
  ```
</CodeGroup>
