> ## 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`՝ պահված արդյունքով, առանց դելտաները վերարտադրելու: Տե՛ս [Հոսքային պատասխան](/am/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>
