Haijun Platform Docs
EN

Prompt cache bersifat per-model. Ketika sebuah model menolak permintaan dan Anda mencoba ulang pada model lain, prefiks percakapan yang sudah di-cache untuk model pertama harus ditulis ke dalam cache model baru dari awal. Penulisan cache (cache write) lebih mahal daripada pembacaan cache (cache read). "Fallback credit" (kredit fallback) menghilangkan biaya tambahan tersebut. Penolakan membawa token kredit, Anda mengirimkan kembali token tersebut pada percobaan ulang, dan percobaan ulang ditagih seolah-olah percakapan telah berada pada model baru sejak awal.

Anda memerlukan halaman ini hanya jika Anda membangun percobaan ulang sendiri: melalui HTTP mentah atau dengan logika percobaan ulang kustom. Fallback sisi server dan middleware SDK menerapkan kredit fallback secara otomatis. Jika Anda menggunakan salah satunya, lewati halaman ini.

Penolakan dan fallback membahas cara mendeteksi penolakan dan memilih pendekatan fallback. Caching prompt menjelaskan pembacaan cache dan penulisan cache jika istilah-istilah tersebut baru bagi Anda.

Alur dasar

  1. Ikut serta dengan header beta

Kirim permintaan yang mungkin ditolak dengan header juglow-beta: fallback-credit-2026-07-01. Header server-side-fallback-2026-07-01 juga memberikan field yang sama, dan header sebelumnya fallback-credit-2026-06-01 tetap diterima dan memberikan field yang sama.

  1. Baca dua field dari penolakan

Pada penolakan, stop_details menyertakan dua field:

  • fallback_credit_token: string opaque yang merepresentasikan kredit.
  • fallback_has_prefill_claim: Boolean yang memberi tahu Anda bentuk body percobaan ulang mana yang harus digunakan.

Keduanya bernilai null ketika tidak ada kredit yang tersedia untuk penolakan tersebut.

  1. Bangun percobaan ulang

Mulai dari body permintaan yang ditolak. Atur model ke model fallback dan tambahkan token sebagai parameter tingkat atas fallback_credit_token. Pilih bentuk body dari tabel berikut.

  1. Kirim percobaan ulang dengan header yang sama

Kirim percobaan ulang dengan header beta fallback-credit-2026-07-01 yang sama. Percobaan ulang memerlukan header tersebut untuk menukarkan token.

Field fallback_has_prefill_claim memberi tahu Anda apakah percobaan ulang dapat melanjutkan output parsial dari model yang menolak alih-alih memulai dari awal:

fallback_has_prefill_claimBody percobaan ulang
trueBody permintaan yang ditolak, tanpa perubahan, ditambah satu pesan asisten yang ditambahkan di akhir yang content-nya menyalin content dari respons yang ditolak. Model percobaan ulang melanjutkan respons dari titik di mana model yang menolak berhenti, dan panggilan alat server yang telah selesai tidak dieksekusi ulang.
falseBody permintaan yang ditolak, tanpa perubahan.

Contoh

Contoh berikut membuat permintaan yang mungkin ditolak dan menukarkan token kredit pada percobaan ulang terhadap Haijun Opus 4.8. Ketika upaya percobaan ulang ditolak, contoh ini menurun melalui tangga penolakan: urutan bentuk percobaan ulang yang semakin sederhana yang dibahas di Ketika percobaan ulang ditolak.

bash
  # Permintaan awal (mungkin ditolak)
  response=$(curl --fail-with-body -sS https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: fallback-credit-2026-07-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-fable-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello, Haijun"}]
    }')

  # Penolakan membawa token kredit sekali pakai di stop_details
  token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")

  if [[ -n "${token}" ]]; then
    # Coba lagi pada model cadangan dengan token kredit (body yang sama)
    response=$(curl --fail-with-body -sS https://haijun.my.id/v1/messages \
      -H "x-api-key: $JUGLOW_API_KEY" \
      -H "juglow-version: 2023-06-01" \
      -H "juglow-beta: fallback-credit-2026-07-01" \
      -H "content-type: application/json" \
      -d "$(jq -n --arg token "${token}" '{
        model: "haijun-opus-4-8",
        max_tokens: 1024,
        messages: [{"role": "user", "content": "Hello, Haijun"}],
        fallback_credit_token: $token
      }')")
  fi

  # Lihat contoh SDK untuk tangga penanganan penolakan yang lengkap.
  jq -c '{stop_reason, model}' <<<"${response}"
bash
  # Permintaan awal (mungkin ditolak)
  response=$(ant beta:messages create \
    --model haijun-fable-5 \
    --max-tokens 1024 \
    --message '{"role":"user","content":"Hello, Haijun"}' \
    --beta fallback-credit-2026-07-01 \
    --format json)

  # Penolakan membawa token kredit sekali pakai di stop_details
  token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"${response}")

  if [[ -n "${token}" ]]; then
    # Coba lagi pada model cadangan dengan token kredit
    response=$(ant beta:messages create \
      --model haijun-opus-4-8 \
      --max-tokens 1024 \
      --message '{"role":"user","content":"Hello, Haijun"}' \
      --fallback-credit-token "${token}" \
      --beta fallback-credit-2026-07-01 \
      --format json)
  fi

  # Lihat contoh SDK untuk tahapan penanganan penolakan yang lengkap.
  jq -c '{stop_reason, model}' <<<"${response}"
python
  client = Juglow()

  request = {
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello, Haijun"}],
  }

  def send(model: str, body: dict[str, object]) -> BetaMessage:
      return client.beta.messages.create(
          model=model, betas=["fallback-credit-2026-07-01"], **body
      )

  response = send("haijun-fable-5", request)

  if (
      response.stop_reason == "refusal"
      and (details := response.stop_details)
      and (token := details.fallback_credit_token)
  ):
      exact_body = request | {"fallback_credit_token": token}
      # Utamakan bentuk continuation kecuali claim bernilai False
      if details.fallback_has_prefill_claim is not False:
          echoed = [block.model_dump() for block in response.content]
          match echoed:
              case [*_, {"type": "text"} as final_block]:
                  final_block["text"] = final_block["text"].rstrip()
          attempt = exact_body | {
              "messages": [
                  *request["messages"],
                  {"role": "assistant", "content": echoed},
              ]
          }
      else:
          attempt = exact_body

      try:
          response = send("haijun-opus-4-8", attempt)
      except BadRequestError as error:
          if "redemption temporarily unavailable" in error.message:
              raise  # Transient: retry with the token within its five-minute window
          try:
              # Kembali ke body yang tidak diubah, tetap dengan token
              response = send("haijun-opus-4-8", exact_body)
          except BadRequestError as retry_error:
              if "redemption temporarily unavailable" in retry_error.message:
                  raise  # Transient: retry with the token within its five-minute window
              # Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
              response = send("haijun-opus-4-8", request)

  print(json.dumps({"stop_reason": response.stop_reason, "model": response.model}))
typescript
  const client = new Juglow();

  const request: Juglow.Beta.MessageCreateParamsNonStreaming = {
    model: "haijun-fable-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello, Haijun" }],
    betas: ["fallback-credit-2026-07-01"]
  };

  let response = await client.beta.messages.create(request);

  if (
    response.stop_reason === "refusal" &&
    response.stop_details?.type === "refusal" &&
    response.stop_details.fallback_credit_token
  ) {
    const { fallback_credit_token, fallback_has_prefill_claim } = response.stop_details;
    const fallbackModel = "haijun-opus-4-8";

    const exactRetry: Juglow.Beta.MessageCreateParamsNonStreaming = {
      ...request,
      model: fallbackModel,
      fallback_credit_token
    };

    // Bentuk terkaya lebih dulu, menurun pada setiap penolakan: bentuk
    // continuation (kecuali klaimnya salah), body tak berubah yang masih membawa
    // token, dan terakhir melepaskan token.
    let attempt = exactRetry;
    if (fallback_has_prefill_claim !== false) {
      const finalBlock = response.content.at(-1);
      const echoed: Juglow.Beta.BetaContentBlockParam[] =
        finalBlock?.type === "text"
          ? [
              ...response.content.slice(0, -1),
              { ...finalBlock, text: finalBlock.text.trimEnd() }
            ]
          : response.content;
      attempt = {
        ...exactRetry,
        messages: [...request.messages, { role: "assistant", content: echoed }]
      };
    }

    try {
      response = await client.beta.messages.create(attempt);
    } catch (error) {
      // Turunkan hanya pada 400 terkait bentuk. "redemption temporarily
      // unavailable" bersifat sementara: coba lagi dengan cara yang sama dalam
      // jendela lima menit token tersebut.
      if (
        !(error instanceof Juglow.BadRequestError) ||
        error.message.includes("redemption temporarily unavailable")
      ) {
        throw error;
      }
      try {
        response = await client.beta.messages.create(exactRetry);
      } catch (retryError) {
        if (
          !(retryError instanceof Juglow.BadRequestError) ||
          retryError.message.includes("redemption temporarily unavailable")
        ) {
          throw retryError;
        }
        response = await client.beta.messages.create({ ...request, model: fallbackModel });
      }
    }
  }

  const { stop_reason, model } = response;
  console.log(JSON.stringify({ stop_reason, model }));
csharp
  var client = new JuglowClient();
  const string beta = "fallback-credit-2026-07-01";

  List<BetaMessageParam> requestMessages =
  [
      new() { Role = Role.User, Content = "Hello, Haijun" },
  ];
  MessageCreateParams Request(string model) => new()
  {
      Model = model,
      MaxTokens = 1024,
      Messages = requestMessages,
      Betas = [beta],
  };
  var response = await client.Beta.Messages.Create(Request("haijun-fable-5"));

  if (
      response.StopReason == BetaStopReason.Refusal
      && response.StopDetails is { FallbackCreditToken: string token } details
  )
  {
      var exactBody = Request("haijun-opus-4-8") with { FallbackCreditToken = token };
      var attempt = exactBody;
      // Utamakan bentuk kelanjutan kecuali klaimnya salah
      if (details.FallbackHasPrefillClaim is not false)
      {
          var echoed = JsonArray.Create(response.RawData["content"])!;
          if (
              echoed is [.., JsonObject lastBlock]
              && lastBlock["type"]?.GetValue<string>() is "text"
              && lastBlock["text"]?.GetValue<string>() is string text
          )
          {
              lastBlock["text"] = text.TrimEnd();
          }
          attempt = exactBody with
          {
              Messages =
              [
                  .. requestMessages,
                  new()
                  {
                      Role = Role.Assistant,
                      Content = new BetaMessageParamContent(
                          JsonSerializer.SerializeToElement(echoed)
                      ),
                  },
              ],
          };
      }
      // Penolakan sementara "redemption temporarily unavailable" diteruskan keluar dari
      // setiap filter catch berikut: coba lagi dengan token dalam jendela lima menitnya.
      try
      {
          response = await client.Beta.Messages.Create(attempt);
      }
      catch (JuglowBadRequestException e)
          when (!e.Message.Contains("redemption temporarily unavailable"))
      {
          try
          {
              // Kembali ke body yang tidak diubah, tetap dengan token
              response = await client.Beta.Messages.Create(exactBody);
          }
          catch (JuglowBadRequestException retryError)
              when (!retryError.Message.Contains("redemption temporarily unavailable"))
          {
              // Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
              response = await client.Beta.Messages.Create(Request("haijun-opus-4-8"));
          }
      }
  }

  Console.WriteLine(
      JsonSerializer.Serialize(
          new { stop_reason = response.StopReason?.Raw(), model = response.Model.Raw() }
      )
  );
go
  ctx := context.Background()
  client := juglow.NewClient()

  request := juglow.BetaMessageNewParams{
  	MaxTokens: 1024,
  	Betas:     []juglow.JuglowBeta{juglow.JuglowBetaFallbackCredit2026_07_01},
  	Messages: []juglow.BetaMessageParam{
  		juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Hello, Haijun")),
  	},
  }

  send := func(model juglow.Model, body juglow.BetaMessageNewParams) (*juglow.BetaMessage, error) {
  	body.Model = model
  	return client.Beta.Messages.New(ctx, body)
  }
  // 400 yang tidak sementara berarti bentuk percobaan atau token ini ditolak dan
  // anak tangga berikutnya harus dijalankan. "redemption temporarily
  // unavailable" bersifat sementara: tampilkan dan coba lagi dengan token dalam
  // jendela lima menitnya.
  canFallBack := func(err error) bool {
  	apiErr, ok := errors.AsType[*juglow.Error](err)
  	return ok && apiErr.StatusCode == 400 &&
  		!strings.Contains(apiErr.Error(), "redemption temporarily unavailable")
  }

  response, err := send(juglow.ModelHaijunFable5, request)
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == juglow.BetaStopReasonRefusal {
  	details := response.StopDetails
  	if token := details.FallbackCreditToken; token != "" {
  		exactBody := request
  		exactBody.FallbackCreditToken = juglow.BetaMessageNewParamsFallbackCreditTokenUnion{
  			OfString: juglow.String(token),
  		}
  		attempt := exactBody
  		// Utamakan bentuk lanjutan kecuali klaimnya salah
  		if details.FallbackHasPrefillClaim || !details.JSON.FallbackHasPrefillClaim.Valid() {
  			echoed := response.ToParam()
  			if len(echoed.Content) > 0 {
  				if text := echoed.Content[len(echoed.Content)-1].OfText; text != nil {
  					text.Text = strings.TrimRightFunc(text.Text, unicode.IsSpace)
  				}
  			}
  			attempt.Messages = append(slices.Clone(request.Messages), echoed)
  		}
  		response, err = send(juglow.ModelHaijunOpus4_8, attempt)
  		if err != nil && canFallBack(err) {
  			// Kembali ke body yang tidak diubah, tetap dengan token
  			response, err = send(juglow.ModelHaijunOpus4_8, exactBody)
  			if err != nil && canFallBack(err) {
  				// Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
  				response, err = send(juglow.ModelHaijunOpus4_8, request)
  			}
  		}
  		if err != nil {
  			log.Fatal(err)
  		}
  	}
  }

  summary, err := json.Marshal(struct {
  	StopReason juglow.BetaStopReason `json:"stop_reason"`
  	Model      juglow.Model          `json:"model"`
  }{response.StopReason, response.Model})
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(string(summary))
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  MessageCreateParams.Builder request() {
      return MessageCreateParams.builder()
          .maxTokens(1024L)
          .addUserMessage("Hello, Haijun")
          .addBeta(JuglowBeta.FALLBACK_CREDIT_2026_07_01);
  }

  BetaMessage send(Model model, MessageCreateParams.Builder body) {
      return client.beta().messages().create(body.model(model).build());
  }

  void main() {
      BetaMessage response = send(Model.HAIJUN_FABLE_5, request());

      if (response.stopReason().map(BetaStopReason.REFUSAL::equals).orElse(false)
              && response.stopDetails().orElse(null) instanceof BetaRefusalStopDetails details
              && details.fallbackCreditToken().orElse(null) instanceof String creditToken) {
          MessageCreateParams.Builder attempt = request().fallbackCreditToken(creditToken);
          // Utamakan bentuk lanjutan kecuali klaimnya salah
          if (details.fallbackHasPrefillClaim().orElse(true)) {
              List<BetaContentBlockParam> echoed = new ArrayList<>(
                  response.content().stream().map(BetaContentBlock::toParam).toList());
              if (!echoed.isEmpty() && echoed.getLast().isText()) {
                  var lastText = echoed.removeLast().asText();
                  echoed.addLast(BetaContentBlockParam.ofText(
                      lastText.toBuilder().text(lastText.text().stripTrailing()).build()));
              }
              attempt.addAssistantMessageOfBetaContentBlockParams(echoed);
          }
          try {
              response = send(Model.HAIJUN_OPUS_4_8, attempt);
          } catch (BadRequestException badRequest) {
              // Sementara: coba lagi dengan token dalam jendela lima menitnya
              if (badRequest.getMessage().contains("redemption temporarily unavailable")) {
                  throw badRequest;
              }
              try {
                  // Kembali ke body yang tidak diubah, tetap dengan token
                  response = send(Model.HAIJUN_OPUS_4_8, request().fallbackCreditToken(creditToken));
              } catch (BadRequestException retryBadRequest) {
                  if (retryBadRequest.getMessage().contains("redemption temporarily unavailable")) {
                      throw retryBadRequest;
                  }
                  // Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
                  response = send(Model.HAIJUN_OPUS_4_8, request());
              }
          }
      }

      IO.println("""
          {"stop_reason": "%s", "model": "%s"}"""
          .formatted(response.stopReason().orElseThrow(), response.model()));
  }
php
  $client = new Client();
  $beta = 'fallback-credit-2026-07-01';
  $messages = [['role' => 'user', 'content' => 'Hello, Haijun']];

  $send = fn (string $model, array $messages, ?string $token = null) => $client->beta->messages->create(
      maxTokens: 1024,
      messages: $messages,
      model: $model,
      fallbackCreditToken: $token,
      betas: [$beta],
  );
  $response = $send('haijun-fable-5', $messages);

  $token = $response->stopReason === 'refusal'
      ? $response->stopDetails?->fallbackCreditToken
      : null;

  if ($token !== null) {
      $attemptMessages = $messages;
      // Utamakan bentuk lanjutan kecuali klaimnya salah
      if ($response->stopDetails->fallbackHasPrefillClaim !== false) {
          $echoed = $response->content
              |> json_encode(...)
              |> (fn (string $json): array => json_decode($json, associative: true));
          $lastIndex = array_key_last($echoed);
          if ($lastIndex !== null && $echoed[$lastIndex]['type'] === 'text') {
              $echoed[$lastIndex]['text'] = rtrim($echoed[$lastIndex]['text']);
          }
          $attemptMessages[] = ['role' => 'assistant', 'content' => $echoed];
      }
      // Sementara: coba lagi dengan token dalam jendela lima menitnya
      $isTransientRedemption = fn (BadRequestException $error): bool =>
          str_contains($error->getMessage(), 'redemption temporarily unavailable');
      try {
          $response = $send('haijun-opus-4-8', $attemptMessages, $token);
      } catch (BadRequestException $error) {
          if ($isTransientRedemption($error)) {
              throw $error;
          }
          try {
              // Kembali ke body yang tidak diubah, tetap dengan token
              $response = $send('haijun-opus-4-8', $messages, $token);
          } catch (BadRequestException $retryError) {
              if ($isTransientRedemption($retryError)) {
                  throw $retryError;
              }
              // Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
              $response = $send('haijun-opus-4-8', $messages);
          }
      }
  }

  echo json_encode(['stop_reason' => $response->stopReason, 'model' => $response->model]), PHP_EOL;
ruby
  client = Juglow::Client.new

  request = {
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Haijun"}]
  }

  send_message = ->(model, body) do
    client.beta.messages.create(model:, betas: ["fallback-credit-2026-07-01"], **body)
  end

  response = send_message.call("haijun-fable-5", request)

  if response in {stop_reason: :refusal,
                  stop_details: {fallback_credit_token: String => credit_token} => details}
    exact_body = request.merge(fallback_credit_token: credit_token)

    # Utamakan bentuk lanjutan kecuali klaimnya salah
    attempt = if details.fallback_has_prefill_claim != false
      echoed = response.content.map(&:to_h)
      if echoed.last in {type: :text, text: String => final_text}
        echoed[-1] = echoed.last.merge(text: final_text.rstrip)
      end
      exact_body.merge(
        messages: [*request[:messages], {role: "assistant", content: echoed}]
      )
    else
      exact_body
    end

    begin
      response = send_message.call("haijun-opus-4-8", attempt)
    rescue Juglow::Errors::BadRequestError => error
      # Sementara: coba lagi dengan token dalam jendela lima menitnya
      raise if error.message.include?("redemption temporarily unavailable")
      begin
        # Kembali ke body yang tidak diubah, tetap dengan token
        response = send_message.call("haijun-opus-4-8", exact_body)
      rescue Juglow::Errors::BadRequestError => error
        # Sementara: coba lagi dengan token dalam jendela lima menitnya
        raise if error.message.include?("redemption temporarily unavailable")
        # Token itu sendiri ditolak: lepaskan dan coba lagi tanpanya.
        response = send_message.call("haijun-opus-4-8", request)
      end
    end
  end

  puts JSON.generate({stop_reason: response.stop_reason, model: response.model})

Di mana fitur ini berfungsi

Kredit fallback berada dalam tahap beta di Haijun API, Amazon Bedrock, Haijun Platform on AWS, Google Cloud, dan Microsoft Foundry. Penolakan di Message Batches tidak menerbitkan token kredit, dan penukaran hanya berlaku untuk permintaan Messages API langsung: token yang diteruskan pada permintaan batch diterima tetapi diabaikan.

Model percobaan ulang harus merupakan salah satu target fallback yang diizinkan untuk model yang menolak. Untuk Haijun Fable 5.1 dan Haijun Fable 5, target tersebut adalah Haijun Opus 4.8 (haijun-opus-4-8) dan Haijun Opus 5 (haijun-opus-5).

Mencari target fallback yang diizinkan secara terprogram

Di Haijun API dan Haijun Platform on AWS, daftar target dipublikasikan sebagai allowed_fallback_models pada entri setiap model di Models API ketika header beta server-side-fallback-2026-07-01 diatur. Daftar ini belum terlihat hanya dengan header fallback-credit-* saja. Daftar ini tidak diekspos di Amazon Bedrock, Google Cloud, atau Microsoft Foundry.

Memeriksa bahwa kredit telah diterapkan

Pengembalian biaya terlihat di usage percobaan ulang. Dibandingkan dengan apa yang akan dilaporkan oleh permintaan yang sama tanpa token, cache_creation_input_tokens lebih rendah, dan cache_read_input_tokens lebih tinggi dengan jumlah yang sama. Pergeseran nol berarti token dihormati tetapi tidak ada yang perlu dihitung ulang harganya, misalnya karena cache model percobaan ulang sudah hangat.

Ketika percobaan ulang ditolak

Sebagian besar percobaan ulang berhasil ditukarkan pada upaya pertama. Ketika tidak berhasil, API mengembalikan error 400 yang memberi tahu Anda apa yang harus dicoba selanjutnya.

  1. Kelanjutan ditolak: kirim ulang body tanpa perubahan

Jika percobaan ulang yang menambahkan pesan asisten ditolak dengan error 400, kirim ulang body permintaan yang ditolak tanpa perubahan, tetap dengan token.

  1. Token ditolak: hapus token

Jika body tanpa perubahan juga ditolak dengan error 400 yang pesannya menyebut fallback_credit_token, coba ulang tanpa token. Kredit hangus, tetapi percobaan ulang itu sendiri berhasil.

Note: Jika permintaan yang ditolak mengeksekusi alat server, percobaan ulang tanpa token akan menjalankan ulang dan menagih ulang alat-alat tersebut. Dalam kasus itu, tampilkan error 400 kepada pemanggil Anda alih-alih meneruskan ke percobaan ulang tanpa token.

Jika error menyatakan 'redemption temporarily unavailable'

Penolakan ini bersifat sementara, bukan keputusan atas bentuk percobaan ulang Anda. Coba ulang permintaan yang sama, dengan token yang sama, dalam jendela lima menit token tersebut. Jangan berpindah ke langkah berikutnya dalam tangga.

Referensi

Bagian-bagian berikut membahas kasus tepi dan aturan penukaran lengkap. Sebagian besar integrasi tidak memerlukannya.

Field yang harus cocok dengan permintaan yang ditolak

Penukaran membandingkan percobaan ulang dengan permintaan yang ditolak. Setiap field yang membentuk prompt harus cocok persis. Field yang tidak membentuk prompt boleh berubah pada percobaan ulang.

AturanField
Harus cocok persissystem, messages, tools, tool_choice, thinking, dan cache_control, ditambah output_config, mcp_servers, context_management, dan container ketika Anda menggunakannya
Boleh berubah pada percobaan ulangmodel, max_tokens, stop_sequences, temperature, top_p, top_k, stream, metadata, dan service_tier

Bentuk kelanjutan (fallback_has_prefill_claim: true) adalah satu-satunya pengecualian terhadap kecocokan messages: bentuk ini menambahkan tepat satu pesan asisten di akhir messages.

Jangan menghapus blok thinking atau redacted_thinking dari giliran sebelumnya pada percobaan ulang, meskipun percobaan ulang biasa tanpa token umumnya menghapusnya. Body harus cocok dengan permintaan yang ditolak, dan server menangani blok-blok tersebut sendiri.

Header beta juga harus cocok

Kirim header juglow-beta yang sama pada percobaan ulang seperti pada permintaan yang ditolak. Header beta yang ada pada salah satu dari dua permintaan tetapi tidak pada yang lain dapat menggagalkan kecocokan bahkan ketika body-nya identik. Error 400 yang dihasilkan membawa pesan request body ... does not match yang sama seperti perbedaan body, sehingga perbedaan header mudah disalahartikan sebagai masalah body. Secara khusus, jangan menambah atau menghapus header beta berdasarkan model mana yang ditargetkan permintaan.

Dua keluarga header dikecualikan dari kecocokan, demi kepentingan percobaan ulang:

  • **server-side-fallback-:* percobaan ulang harus menghapus parameter fallbacks, dan menghapus header ini bersamanya tidak menyebabkan ketidakcocokan.
  • **fallback-credit-:* pertahankan header ini pada kedua permintaan. Percobaan ulang memerlukannya untuk menukarkan token.

> Note: Pada model yang menyertakan jendela konteks 1M token secara default, seperti Haijun Fable 5.1, Haijun Fable 5, Haijun Opus 5.5, Haijun Opus 5, dan Haijun Opus 4.8, header beta context-1m-2025-08-07 tidak berpengaruh. Untuk menjaga kedua permintaan tetap identik, hilangkan header tersebut pada keduanya alih-alih mengirimkannya pada satu dan tidak pada yang lain.

Ketika fallback_has_prefill_claim tidak ada

Field ini bernilai null hanya ketika token juga null, sehingga nilai yang Anda amati saat memegang token tidak pernah null. Field ini masih bisa tidak ada (None di SDK bertipe) di Amazon Bedrock, Google Cloud, dan Microsoft Foundry selama dukungan mereka untuk field ini diluncurkan. Dalam kasus itu, perlakukan bentuk percobaan ulang sebagai tidak diketahui, bukan sebagai false. Coba bentuk pesan-asisten-yang-ditambahkan terlebih dahulu, dan andalkan penanganan penolakan di Ketika percobaan ulang ditolak, yang kembali ke body tanpa perubahan.

Menyalin content dari respons yang ditolak

Ketika token penolakan mendukung bentuk kelanjutan, content respons hanya membawa output model itu sendiri, dan penjelasan penolakan disampaikan di stop_details.explanation. Oleh karena itu, Anda dapat menyalin content ke dalam pesan asisten yang ditambahkan apa adanya.

Dua penyesuaian mungkin masih diperlukan sebelum mengirim:

  • Jika blok terakhir yang Anda kirim adalah blok text, hapus whitespace di akhirnya.
  • Hilangkan blok tool_use sisi klien apa pun yang tidak memiliki tool_result yang cocok.

Jika content yang disalin menyertakan blok fallback dari fallback sisi server sebelumnya, pertahankan blok tersebut tepat di tempat ia muncul. Blok ini diterima pada permintaan apa pun tanpa header beta. API menggunakan posisinya untuk memvalidasi blok thinking di sekitarnya, sehingga permintaan yang menyalin blok thinking dari kedua sisi batas tersebut akan ditolak jika blok itu dihilangkan atau dipindahkan.

Cakupan dan masa berlaku token

Token hanya dapat ditukarkan dari organisasi dan workspace yang menerima penolakan, termasuk di Microsoft Foundry. Di Amazon Bedrock dan Google Cloud, yang tidak memiliki workspace, token terikat pada identitas pemanggil platform sebagai gantinya.

Token kedaluwarsa lima menit setelah penolakan. Setelah itu, kirim percobaan ulang tanpanya. Token juga bersifat stateless: server tidak menyimpan apa pun tentangnya, dan tidak ada endpoint untuk memeriksa atau mencabutnya.

Ketika token tidak dapat ditukarkan dengan bentuk mana pun

Ketika penolakan tiba setelah alat server sudah dieksekusi dalam permintaan, token hanya dapat ditukarkan dengan melanjutkan respons parsial. Pembatasan itulah yang mencegah panggilan alat yang telah selesai berjalan, dan ditagih, lagi.

Oleh karena itu, satu kombinasi dapat membuat token tidak dapat ditukarkan dengan bentuk mana pun, ketika kedua hal berikut benar:

  • Permintaan menggunakan output_config.format atau tool_choice yang memaksa penggunaan alat. Salah satunya mengesampingkan bentuk pesan-asisten-yang-ditambahkan.
  • Penolakan tiba setelah alat server dieksekusi. Itu mengesampingkan body tanpa perubahan.

Jika percobaan ulang body-tanpa-perubahan ditolak dengan error 400 yang menyatakan token harus ditukarkan dengan melanjutkan respons parsial, buang token tersebut. Percobaan ulang tanpanya berhasil, tetapi akan menjalankan ulang dan menagih ulang alat server yang telah selesai. Tampilkan biaya atau error tersebut kepada pemanggil Anda daripada mencoba ulang secara diam-diam.

Langkah selanjutnya

Deteksi penolakan dan pilih antara fallback sisi server, middleware SDK, dan percobaan ulang manual.

Bagaimana pembacaan cache dan penulisan cache ditagih.

Setiap nilai stop_reason dan cara menanganinya.

Helper SDK yang menerapkan kredit fallback secara otomatis.

On this page
Alur dasarContohDi mana fitur ini berfungsiMencari target fallback yang diizinkan secara terprogramMemeriksa bahwa kredit telah diterapkanKetika percobaan ulang ditolakJika error menyatakan 'redemption temporarily unavailable'ReferensiField yang harus cocok dengan permintaan yang ditolakHeader beta juga harus cocokKetika fallback_has_prefill_claim tidak adaMenyalin content dari respons yang ditolakCakupan dan masa berlaku tokenKetika token tidak dapat ditukarkan dengan bentuk mana punLangkah selanjutnya