Haijun Platform Docs
EN

Setiap respons Messages API menyertakan field stop_reason yang memberi tahu Anda mengapa Haijun berhenti menghasilkan output. Periksa field ini untuk memutuskan apakah akan menggunakan respons apa adanya, melanjutkan percakapan, mencoba ulang, atau beralih (fallback) ke model lain.

Untuk skema respons lengkap, lihat referensi Messages API.

Referensi cepat

NilaiKapan terjadiApa yang harus dilakukan
end_turnHaijun menyelesaikan responsnya secara alami.Gunakan respons tersebut.
max_tokensRespons mencapai batas max_tokens Anda.Naikkan max_tokens atau lanjutkan respons.
stop_sequenceHaijun mengeluarkan salah satu stop_sequences Anda.Baca stop_sequence untuk melihat mana yang terpicu.
tool_useHaijun sedang memanggil alat.Jalankan alat dan kembalikan hasilnya. Panggilan alat server yang masih belum memiliki blok hasilnya akan selesai dalam respons berikutnya.
pause_turnLoop alat server mencapai batas iterasinya.Kirim kembali konten asisten untuk melanjutkan.
refusalHaijun menolak untuk merespons.Baca stop_details dan coba ulang pada model fallback.
model_context_window_exceededRespons memenuhi jendela konteks model.Perlakukan respons sebagai terpotong.

Field stop\_reason

Field stop_reason adalah bagian dari setiap respons Messages API yang berhasil. Tidak seperti error, yang menunjukkan kegagalan dalam memproses permintaan Anda, stop_reason memberi tahu Anda mengapa Haijun menyelesaikan pembuatan responsnya.

json
{
  "id": "msg_01234",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Here's the answer to your question..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "stop_details": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

Nilai stop reason

end\_turn

Alasan berhenti yang paling umum. Menunjukkan bahwa Haijun menyelesaikan responsnya secara alami.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello!"}]
    }' | jq 'if .stop_reason == "end_turn" then (.content[] | select(.type == "text") | .text) else . end'
bash
  ant messages create \
    --model haijun-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello!"}' \
    --format json | jq 'if .stop_reason == "end_turn" then (.content[] | select(.type == "text") | .text) else . end'
python
  client = juglow.Juglow()

  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello!"}],
  )
  if response.stop_reason == "end_turn":
      # Proses respons lengkap
      for block in response.content:
          if block.type == "text":
              print(block.text)
typescript
  const client = new Juglow();

  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }]
  });

  if (response.stop_reason === "end_turn") {
    // Proses respons lengkap
    const textBlock = response.content.find(
      (block): block is Juglow.TextBlock => block.type === "text"
    );
    console.log(textBlock?.text);
  }
csharp
  JuglowClient client = new();

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello!" }]
  });

  if (response.StopReason == "end_turn")
  {
      // Proses respons lengkap
      foreach (var block in response.Content)
      {
          if (block.TryPickText(out var textBlock))
          {
              Console.WriteLine(textBlock.Text);
          }
      }
  }
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Hello!")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "end_turn" {
  	// Proses respons lengkap
  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(juglow.TextBlock); ok {
  			fmt.Println(textBlock.Text)
  		}
  	}
  }
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(1024L)
          .addUserMessage("Hello!")
          .build()
  );

  if (response.stopReason().map(StopReason.END_TURN::equals).orElse(false)) {
      // Proses respons lengkap
      response.content().stream()
          .flatMap(block -> block.text().stream())
          .forEach(textBlock -> IO.println(textBlock.text()));
  }
php
  $client = new Client();

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello!']],
      model: 'haijun-opus-5-5',
  );

  if ($response->stopReason === 'end_turn') {
      // Proses respons lengkap
      foreach ($response->content as $block) {
          if ($block->type === 'text') {
              echo $block->text, PHP_EOL;
          }
      }
  }
ruby
  client = Juglow::Client.new

  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }]
  )

  if response.stop_reason == :end_turn
    # Proses respons lengkap
    response.content.each do |block|
      puts block.text if block.type == :text
    end
  end

Respons kosong dengan end_turn

Terkadang Haijun mengembalikan respons kosong (tepat 2–3 token tanpa konten) dengan stop_reason: "end_turn". Ini biasanya terjadi ketika Haijun menafsirkan bahwa giliran asisten telah selesai, terutama setelah hasil alat.

Penyebab umum:

  • Menambahkan blok teks tepat setelah hasil alat (Haijun belajar untuk mengharapkan pengguna selalu menyisipkan teks setelah hasil alat, sehingga ia mengakhiri gilirannya untuk mengikuti pola tersebut)
  • Mengirim kembali respons Haijun yang sudah selesai tanpa menambahkan apa pun (Haijun sudah menentukan bahwa ia selesai, jadi ia akan tetap selesai)

Cara mencegah respons kosong:

python
    # SALAH: Menambahkan teks langsung setelah tool_result
    messages = [
        {"role": "user", "content": "Calculate the sum of 1234 and 5678"},
        {
            "role": "assistant",
            "content": [
                {
                    "type": "tool_use",
                    "id": "toolu_123",
                    "name": "calculator",
                    "input": {"operation": "add", "a": 1234, "b": 5678},
                }
            ],
        },
        {
            "role": "user",
            "content": [
                {"type": "tool_result", "tool_use_id": "toolu_123", "content": "6912"},
                {
                    "type": "text",
                    "text": "Here's the result",  # Don't add text after tool_result
                },
            ],
        },
    ]

    # BENAR: Kirim hasil alat secara langsung tanpa teks tambahan
    messages = [
        {"role": "user", "content": "Calculate the sum of 1234 and 5678"},
        {
            "role": "assistant",
            "content": [
                {
                    "type": "tool_use",
                    "id": "toolu_123",
                    "name": "calculator",
                    "input": {"operation": "add", "a": 1234, "b": 5678},
                }
            ],
        },
        {
            "role": "user",
            "content": [
                {"type": "tool_result", "tool_use_id": "toolu_123", "content": "6912"}
            ],
        },  # Just the tool_result, no additional text
    ]
typescript
    // SALAH: Menambahkan teks langsung setelah tool_result
    let messages: Juglow.MessageParam[] = [
      { role: "user", content: "Calculate the sum of 1234 and 5678" },
      {
        role: "assistant",
        content: [
          {
            type: "tool_use",
            id: "toolu_123",
            name: "calculator",
            input: { operation: "add", a: 1234, b: 5678 }
          }
        ]
      },
      {
        role: "user",
        content: [
          { type: "tool_result", tool_use_id: "toolu_123", content: "6912" },
          { type: "text", text: "Here's the result" } // Don't add text after tool_result
        ]
      }
    ];

    // BENAR: Kirim hasil alat secara langsung tanpa teks tambahan
    messages = [
      { role: "user", content: "Calculate the sum of 1234 and 5678" },
      {
        role: "assistant",
        content: [
          {
            type: "tool_use",
            id: "toolu_123",
            name: "calculator",
            input: { operation: "add", a: 1234, b: 5678 }
          }
        ]
      },
      {
        role: "user",
        // Hanya tool_result, tanpa teks tambahan
        content: [{ type: "tool_result", tool_use_id: "toolu_123", content: "6912" }]
      }
    ];
csharp
    using System.Text.Json;
    using Juglow.Models.Messages;

    var input = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
        """{"operation":"add","a":1234,"b":5678}"""
    )!;

    // SALAH: Menambahkan teks langsung setelah tool_result
    List<MessageParam> messages =
    [
        new() { Role = Role.User, Content = "Calculate the sum of 1234 and 5678" },
        new()
        {
            Role = Role.Assistant,
            Content = new List<ContentBlockParam>
            {
                new ToolUseBlockParam { ID = "toolu_123", Name = "calculator", Input = input }
            }
        },
        new()
        {
            Role = Role.User,
            Content = new List<ContentBlockParam>
            {
                new ToolResultBlockParam { ToolUseID = "toolu_123", Content = "6912" },
                new TextBlockParam { Text = "Here's the result" } // Don't add text after tool_result
            }
        }
    ];

    // BENAR: Kirim hasil alat secara langsung tanpa teks tambahan
    messages =
    [
        new() { Role = Role.User, Content = "Calculate the sum of 1234 and 5678" },
        new()
        {
            Role = Role.Assistant,
            Content = new List<ContentBlockParam>
            {
                new ToolUseBlockParam { ID = "toolu_123", Name = "calculator", Input = input }
            }
        },
        new()
        {
            Role = Role.User,
            // Hanya tool_result, tanpa teks tambahan
            Content = new List<ContentBlockParam>
            {
                new ToolResultBlockParam { ToolUseID = "toolu_123", Content = "6912" }
            }
        }
    ];
go
    input := map[string]any{"operation": "add", "a": 1234, "b": 5678}

    // SALAH: Menambahkan teks langsung setelah tool_result
    messages := []juglow.MessageParam{
    	juglow.NewUserMessage(juglow.NewTextBlock("Calculate the sum of 1234 and 5678")),
    	juglow.NewAssistantMessage(
    		juglow.NewToolUseBlock("toolu_123", input, "calculator"),
    	),
    	juglow.NewUserMessage(
    		juglow.NewToolResultBlock("toolu_123", "6912", false),
    		juglow.NewTextBlock("Here's the result"), // Don't add text after tool_result
    	),
    }

    // BENAR: Kirim hasil alat secara langsung tanpa teks tambahan
    messages = []juglow.MessageParam{
    	juglow.NewUserMessage(juglow.NewTextBlock("Calculate the sum of 1234 and 5678")),
    	juglow.NewAssistantMessage(
    		juglow.NewToolUseBlock("toolu_123", input, "calculator"),
    	),
    	// Hanya tool_result, tanpa teks tambahan
    	juglow.NewUserMessage(
    		juglow.NewToolResultBlock("toolu_123", "6912", false),
    	),
    }
java
    ToolUseBlockParam toolUse = ToolUseBlockParam.builder()
        .id("toolu_123")
        .name("calculator")
        .input(ToolUseBlockParam.Input.builder()
            .putAdditionalProperty("operation", JsonValue.from("add"))
            .putAdditionalProperty("a", JsonValue.from(1234))
            .putAdditionalProperty("b", JsonValue.from(5678))
            .build())
        .build();

    // SALAH: Menambahkan teks langsung setelah tool_result
    List<MessageParam> messages = List.of(
        MessageParam.builder().role(MessageParam.Role.USER)
            .content("Calculate the sum of 1234 and 5678").build(),
        MessageParam.builder().role(MessageParam.Role.ASSISTANT)
            .contentOfBlockParams(List.of(ContentBlockParam.ofToolUse(toolUse))).build(),
        MessageParam.builder().role(MessageParam.Role.USER)
            .contentOfBlockParams(List.of(
                ContentBlockParam.ofToolResult(
                    ToolResultBlockParam.builder().toolUseId("toolu_123").content("6912").build()),
                // Jangan tambahkan teks setelah tool_result
                ContentBlockParam.ofText(TextBlockParam.builder().text("Here's the result").build())
            )).build()
    );

    // BENAR: Kirim hasil alat secara langsung tanpa teks tambahan
    messages = List.of(
        MessageParam.builder().role(MessageParam.Role.USER)
            .content("Calculate the sum of 1234 and 5678").build(),
        MessageParam.builder().role(MessageParam.Role.ASSISTANT)
            .contentOfBlockParams(List.of(ContentBlockParam.ofToolUse(toolUse))).build(),
        // Hanya tool_result, tanpa teks tambahan
        MessageParam.builder().role(MessageParam.Role.USER)
            .contentOfBlockParams(List.of(
                ContentBlockParam.ofToolResult(
                    ToolResultBlockParam.builder().toolUseId("toolu_123").content("6912").build())
            )).build()
    );
php
    // SALAH: Menambahkan teks langsung setelah tool_result
    $messages = [
        ['role' => 'user', 'content' => 'Calculate the sum of 1234 and 5678'],
        [
            'role' => 'assistant',
            'content' => [
                [
                    'type' => 'tool_use',
                    'id' => 'toolu_123',
                    'name' => 'calculator',
                    'input' => ['operation' => 'add', 'a' => 1234, 'b' => 5678],
                ],
            ],
        ],
        [
            'role' => 'user',
            'content' => [
                ['type' => 'tool_result', 'tool_use_id' => 'toolu_123', 'content' => '6912'],
                // Jangan tambahkan teks setelah tool_result
                ['type' => 'text', 'text' => "Here's the result"],
            ],
        ],
    ];

    // BENAR: Kirim hasil alat secara langsung tanpa teks tambahan
    $messages = [
        ['role' => 'user', 'content' => 'Calculate the sum of 1234 and 5678'],
        [
            'role' => 'assistant',
            'content' => [
                [
                    'type' => 'tool_use',
                    'id' => 'toolu_123',
                    'name' => 'calculator',
                    'input' => ['operation' => 'add', 'a' => 1234, 'b' => 5678],
                ],
            ],
        ],
        [
            'role' => 'user',
            // Hanya tool_result, tanpa teks tambahan
            'content' => [
                ['type' => 'tool_result', 'tool_use_id' => 'toolu_123', 'content' => '6912'],
            ],
        ],
    ];
ruby
    # SALAH: Menambahkan teks langsung setelah tool_result
    messages = [
      { role: "user", content: "Calculate the sum of 1234 and 5678" },
      {
        role: "assistant",
        content: [
          {
            type: "tool_use",
            id: "toolu_123",
            name: "calculator",
            input: { operation: "add", a: 1234, b: 5678 }
          }
        ]
      },
      {
        role: "user",
        content: [
          { type: "tool_result", tool_use_id: "toolu_123", content: "6912" },
          # Jangan tambahkan teks setelah tool_result
          { type: "text", text: "Here's the result" }
        ]
      }
    ]

    # BENAR: Kirim hasil alat secara langsung tanpa teks tambahan
    messages = [
      { role: "user", content: "Calculate the sum of 1234 and 5678" },
      {
        role: "assistant",
        content: [
          {
            type: "tool_use",
            id: "toolu_123",
            name: "calculator",
            input: { operation: "add", a: 1234, b: 5678 }
          }
        ]
      },
      {
        role: "user",
        # Hanya tool_result, tanpa teks tambahan
        content: [
          { type: "tool_result", tool_use_id: "toolu_123", content: "6912" }
        ]
      }
    ]

Jika Anda masih mendapatkan respons kosong setelah memperbaiki struktur pesan, tambahkan prompt lanjutan dalam pesan pengguna baru alih-alih mencoba ulang dengan respons kosong tersebut:

python
    def handle_empty_response(client, messages):
        response = client.messages.create(
            model="haijun-opus-5-5", max_tokens=1024, messages=messages
        )

        # Periksa apakah respons kosong
        if response.stop_reason == "end_turn" and not response.content:
            # SALAH: Jangan hanya mencoba ulang dengan respons kosong
            # Ini tidak akan berhasil karena Haijun sudah memutuskan bahwa ia selesai

            # BENAR: Tambahkan prompt lanjutan dalam pesan pengguna BARU
            messages.append({"role": "user", "content": "Please continue"})

            response = client.messages.create(
                model="haijun-opus-5-5", max_tokens=1024, messages=messages
            )

        return response
typescript
    async function handleEmptyResponse(
      client: Juglow,
      messages: Juglow.MessageParam[]
    ): Promise<Juglow.Message> {
      let response = await client.messages.create({
        model: "haijun-opus-5-5",
        max_tokens: 1024,
        messages
      });

      // Periksa apakah respons kosong
      if (response.stop_reason === "end_turn" && response.content.length === 0) {
        // SALAH: Jangan hanya mencoba ulang dengan respons yang kosong
        // Ini tidak akan berhasil karena Haijun sudah memutuskan bahwa ia selesai

        // BENAR: Tambahkan prompt lanjutan dalam pesan pengguna BARU
        messages.push({ role: "user", content: "Please continue" });

        response = await client.messages.create({
          model: "haijun-opus-5-5",
          max_tokens: 1024,
          messages
        });
      }

      return response;
    }
csharp
    static async Task<Message> HandleEmptyResponse(JuglowClient client, List<MessageParam> messages)
    {
        var response = await client.Messages.Create(new MessageCreateParams
        {
            Model = Model.HaijunOpus5_5,
            MaxTokens = 1024,
            Messages = messages
        });

        // Periksa apakah respons kosong
        if (response.StopReason == "end_turn" && response.Content.Count == 0)
        {
            // BENAR: Tambahkan prompt lanjutan dalam pesan pengguna BARU
            messages.Add(new() { Role = Role.User, Content = "Please continue" });

            response = await client.Messages.Create(new MessageCreateParams
            {
                Model = Model.HaijunOpus5_5,
                MaxTokens = 1024,
                Messages = messages
            });
        }

        return response;
    }
go
    func handleEmptyResponse(client juglow.Client, messages []juglow.MessageParam) (*juglow.Message, error) {
    	response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
    		Model:     juglow.ModelHaijunOpus5_5,
    		MaxTokens: 1024,
    		Messages:  messages,
    	})
    	if err != nil {
    		return nil, err
    	}

    	// Periksa apakah respons kosong
    	if response.StopReason == "end_turn" && len(response.Content) == 0 {
    		// BENAR: Tambahkan prompt lanjutan dalam pesan pengguna BARU
    		messages = append(messages, juglow.NewUserMessage(juglow.NewTextBlock("Please continue")))

    		response, err = client.Messages.New(context.TODO(), juglow.MessageNewParams{
    			Model:     juglow.ModelHaijunOpus5_5,
    			MaxTokens: 1024,
    			Messages:  messages,
    		})
    		if err != nil {
    			return nil, err
    		}
    	}

    	return response, nil
    }
java
    static Message handleEmptyResponse(JuglowClient client, List<MessageParam> messages) {
        Message response = client.messages().create(
            MessageCreateParams.builder()
                .model(Model.HAIJUN_OPUS_5_5)
                .maxTokens(1024L)
                .messages(messages)
                .build()
        );

        // Periksa apakah respons kosong
        boolean isEndTurn = response.stopReason().map(StopReason.END_TURN::equals).orElse(false);
        if (isEndTurn && response.content().isEmpty()) {
            // BENAR: Tambahkan prompt lanjutan dalam pesan pengguna BARU
            List<MessageParam> extended = new ArrayList<>(messages);
            extended.add(MessageParam.builder()
                .role(MessageParam.Role.USER)
                .content("Please continue")
                .build());

            response = client.messages().create(
                MessageCreateParams.builder()
                    .model(Model.HAIJUN_OPUS_5_5)
                    .maxTokens(1024L)
                    .messages(extended)
                    .build()
            );
        }

        return response;
    }
php
    function handle_empty_response(Client $client, array $messages)
    {
        $response = $client->messages->create(
            maxTokens: 1024,
            messages: $messages,
            model: 'haijun-opus-5-5',
        );

        // Periksa apakah respons kosong
        if ($response->stopReason === 'end_turn' && count($response->content) === 0) {
            // BENAR: Tambahkan prompt lanjutan dalam pesan pengguna BARU
            $messages[] = ['role' => 'user', 'content' => 'Please continue'];

            $response = $client->messages->create(
                maxTokens: 1024,
                messages: $messages,
                model: 'haijun-opus-5-5',
            );
        }

        return $response;
    }
ruby
    def handle_empty_response(client, messages)
      response = client.messages.create(
        model: "haijun-opus-5-5",
        max_tokens: 1024,
        messages: messages
      )

      # Periksa apakah respons kosong
      if response.stop_reason == :end_turn && response.content.empty?
        # BENAR: Tambahkan prompt lanjutan dalam pesan pengguna BARU
        messages << { role: "user", content: "Please continue" }

        response = client.messages.create(
          model: "haijun-opus-5-5",
          max_tokens: 1024,
          messages: messages
        )
      end

      response
    end

Praktik terbaik:

  1. Jangan pernah menambahkan blok teks tepat setelah hasil alat: Ini mengajarkan Haijun untuk mengharapkan input pengguna setelah setiap penggunaan alat.
  2. Jangan mencoba ulang respons kosong tanpa modifikasi: Mengirim kembali respons kosong tidak akan membantu.
  3. Gunakan prompt lanjutan sebagai upaya terakhir: Hanya jika perbaikan ini tidak menyelesaikan masalah.

max\_tokens

Haijun berhenti karena mencapai batas max_tokens yang ditentukan dalam permintaan Anda.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 10,
      "messages": [{"role": "user", "content": "Explain quantum physics"}]
    }' | jq '.stop_reason'
bash
  ant messages create \
    --model haijun-opus-5-5 \
    --max-tokens 10 \
    --message '{role: user, content: "Explain quantum physics"}' \
    --format json | jq '.stop_reason'
python
  client = juglow.Juglow()
  # Permintaan dengan token terbatas
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=10,
      messages=[{"role": "user", "content": "Explain quantum physics"}],
  )

  if response.stop_reason == "max_tokens":
      # Respons terpotong
      print("Response was cut off at token limit")
      # Pertimbangkan untuk membuat permintaan lain agar dapat melanjutkan
typescript
  const client = new Juglow();

  // Permintaan dengan token terbatas
  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 10,
    messages: [{ role: "user", content: "Explain quantum physics" }]
  });

  if (response.stop_reason === "max_tokens") {
    // Respons terpotong
    console.log("Response was cut off at token limit");
    // Pertimbangkan untuk membuat permintaan lain guna melanjutkan
  }
csharp
  JuglowClient client = new();

  // Permintaan dengan token terbatas
  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 10,
      Messages = [new() { Role = Role.User, Content = "Explain quantum physics" }]
  });

  if (response.StopReason == "max_tokens")
  {
      // Respons terpotong
      Console.WriteLine("Response was cut off at token limit");
      // Pertimbangkan untuk membuat permintaan lain guna melanjutkan
  }
go
  client := juglow.NewClient()

  // Permintaan dengan token terbatas
  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 10,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Explain quantum physics")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "max_tokens" {
  	// Respons terpotong
  	fmt.Println("Response was cut off at token limit")
  	// Pertimbangkan untuk membuat permintaan lain guna melanjutkan
  }
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  // Permintaan dengan token terbatas
  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(10L)
          .addUserMessage("Explain quantum physics")
          .build()
  );

  if (response.stopReason().map(StopReason.MAX_TOKENS::equals).orElse(false)) {
      // Respons terpotong
      IO.println("Response was cut off at token limit");
      // Pertimbangkan untuk membuat permintaan lain guna melanjutkan
  }
php
  $client = new Client();

  // Permintaan dengan token terbatas
  $response = $client->messages->create(
      maxTokens: 10,
      messages: [['role' => 'user', 'content' => 'Explain quantum physics']],
      model: 'haijun-opus-5-5',
  );

  if ($response->stopReason === 'max_tokens') {
      // Respons terpotong
      echo 'Response was cut off at token limit', PHP_EOL;
      // Pertimbangkan untuk membuat permintaan lain guna melanjutkan
  }
ruby
  client = Juglow::Client.new

  # Permintaan dengan jumlah token terbatas
  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 10,
    messages: [{ role: "user", content: "Explain quantum physics" }]
  )

  if response.stop_reason == :max_tokens
    # Respons terpotong
    puts "Response was cut off at token limit"
    # Pertimbangkan untuk mengirim permintaan lain guna melanjutkan
  end

Blok penggunaan alat yang tidak lengkap

Jika respons Haijun terpotong karena mencapai batas max_tokens, dan respons yang terpotong tersebut berisi blok penggunaan alat yang tidak lengkap, Anda perlu mencoba ulang permintaan dengan nilai max_tokens yang lebih tinggi untuk mendapatkan penggunaan alat secara lengkap.

bash
    RESPONSE=$(ant messages create --max-tokens 1024 --format jsonl <<'YAML'
    model: haijun-opus-5-5
    tools:
      - name: get_weather
        description: Get the current weather in a given location
        input_schema:
          type: object
          properties:
            location:
              type: string
          required:
            - location
    messages:
      - role: user
        content: What is the weather in San Francisco?
    YAML
    )

    # Periksa apakah respons terpotong di tengah penggunaan alat
    STOP_REASON=$(jq -r '.stop_reason' <<<"$RESPONSE")
    LAST_TYPE=$(jq -r '.content[-1].type' <<<"$RESPONSE")
    if [ "$STOP_REASON" = "max_tokens" ] && [ "$LAST_TYPE" = "tool_use" ]; then
      # Coba lagi dengan max_tokens yang lebih tinggi
      ant messages create --max-tokens 4096 <<'YAML'
    model: haijun-opus-5-5
    tools:
      - name: get_weather
        description: Get the current weather in a given location
        input_schema:
          type: object
          properties:
            location:
              type: string
          required:
            - location
    messages:
      - role: user
        content: What is the weather in San Francisco?
    YAML
    fi
python
    # Periksa apakah respons terpotong saat penggunaan alat
    if response.stop_reason == "max_tokens":
        # Periksa apakah blok konten terakhir adalah tool_use yang tidak lengkap
        last_block = response.content[-1]
        if last_block.type == "tool_use":
            # Kirim permintaan dengan max_tokens yang lebih tinggi
            response = client.messages.create(
                model="haijun-opus-5-5",
                max_tokens=4096,  # Increased limit
                messages=messages,
                tools=tools,
            )
typescript
    // Periksa apakah respons terpotong selama penggunaan alat
    if (response.stop_reason === "max_tokens") {
      // Periksa apakah blok konten terakhir adalah tool_use yang tidak lengkap
      const lastBlock = response.content[response.content.length - 1];
      if (lastBlock.type === "tool_use") {
        // Kirim permintaan dengan max_tokens yang lebih tinggi
        response = await client.messages.create({
          model: "haijun-opus-5-5",
          max_tokens: 4096, // Increased limit
          messages: messages,
          tools: tools
        });
      }
    }
csharp
    using System.Linq;
    using Juglow;
    using Juglow.Models.Messages;

    JuglowClient client = new();

    var parameters = new MessageCreateParams
    {
        Model = Model.HaijunOpus5_5,
        MaxTokens = 1024,
        Messages = messages,
        Tools = tools
    };

    var response = await client.Messages.Create(parameters);

    if (response.StopReason == "max_tokens")
    {
        var lastBlock = response.Content.Last();
        if (lastBlock.TryPickToolUse(out _))
        {
            response = await client.Messages.Create(parameters with { MaxTokens = 4096 });
        }
    }
go
    response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
    	Model:     juglow.ModelHaijunOpus5_5,
    	MaxTokens: 1024,
    	Messages:  messages,
    	Tools:     tools,
    })
    if err != nil {
    	log.Fatal(err)
    }

    if response.StopReason == "max_tokens" {
    	lastBlock := response.Content[len(response.Content)-1]
    	switch lastBlock.AsAny().(type) {
    	case juglow.ToolUseBlock:
    		response, err = client.Messages.New(context.TODO(), juglow.MessageNewParams{
    			Model:     juglow.ModelHaijunOpus5_5,
    			MaxTokens: 4096,
    			Messages:  messages,
    			Tools:     tools,
    		})
    		if err != nil {
    			log.Fatal(err)
    		}
    	}
    }
java
    // Periksa apakah respons terpotong selama penggunaan alat
    if (response.stopReason().isPresent() && response.stopReason().get().equals(StopReason.MAX_TOKENS)) {
        ContentBlock lastBlock = response.content().get(response.content().size() - 1);
        if (lastBlock.toolUse().isPresent()) {
            // Kirim permintaan dengan max_tokens yang lebih tinggi
            response = client.messages().create(
                MessageCreateParams.builder()
                    .model(Model.HAIJUN_OPUS_5_5)
                    .maxTokens(4096L) // Increased limit
                    .messages(messages)
                    .tools(tools)
                    .build()
            );
        }
    }
php
    $response = $client->messages->create(
        maxTokens: 1024,
        messages: $messages,
        model: 'haijun-opus-5-5',
        tools: $tools,
    );

    if ($response->stopReason === 'max_tokens') {
        $lastBlock = end($response->content);
        if ($lastBlock->type === 'tool_use') {
            $response = $client->messages->create(
                maxTokens: 4096,
                messages: $messages,
                model: 'haijun-opus-5-5',
                tools: $tools,
            );
        }
    }
ruby
    response = client.messages.create(
      model: "haijun-opus-5-5",
      max_tokens: 1024,
      messages: messages,
      tools: tools
    )

    if response.stop_reason == :max_tokens
      last_block = response.content.last
      if last_block.type == :tool_use
        response = client.messages.create(
          model: "haijun-opus-5-5",
          max_tokens: 4096,
          messages: messages,
          tools: tools
        )
      end
    end

stop\_sequence

Haijun menemukan salah satu stop sequence kustom Anda.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 1024,
      "stop_sequences": ["END", "STOP"],
      "messages": [{"role": "user", "content": "Generate text until you say END"}]
    }' | jq '{stop_reason, stop_sequence}'
bash
  ant messages create \
    --model haijun-opus-5-5 \
    --max-tokens 1024 \
    --stop-sequence END --stop-sequence STOP \
    --message '{role: user, content: "Generate text until you say END"}' \
    --format json | jq '{stop_reason, stop_sequence}'
python
  client = juglow.Juglow()
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      stop_sequences=["END", "STOP"],
      messages=[{"role": "user", "content": "Generate text until you say END"}],
  )

  if response.stop_reason == "stop_sequence":
      print(f"Stopped at sequence: {response.stop_sequence}")
typescript
  const client = new Juglow();

  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    stop_sequences: ["END", "STOP"],
    messages: [{ role: "user", content: "Generate text until you say END" }]
  });

  if (response.stop_reason === "stop_sequence") {
    console.log(`Stopped at sequence: ${response.stop_sequence}`);
  }
csharp
  JuglowClient client = new();

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      StopSequences = ["END", "STOP"],
      Messages = [new() { Role = Role.User, Content = "Generate text until you say END" }]
  });

  if (response.StopReason == "stop_sequence")
  {
      Console.WriteLine($"Stopped at sequence: {response.StopSequence}");
  }
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:         juglow.ModelHaijunOpus5_5,
  	MaxTokens:     1024,
  	StopSequences: []string{"END", "STOP"},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Generate text until you say END")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "stop_sequence" {
  	fmt.Printf("Stopped at sequence: %s\n", response.StopSequence)
  }
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(1024L)
          .addStopSequence("END")
          .addStopSequence("STOP")
          .addUserMessage("Generate text until you say END")
          .build()
  );

  if (response.stopReason().map(StopReason.STOP_SEQUENCE::equals).orElse(false)) {
      IO.println("Stopped at sequence: " + response.stopSequence().orElse(""));
  }
php
  $client = new Client();

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Generate text until you say END']],
      model: 'haijun-opus-5-5',
      stopSequences: ['END', 'STOP'],
  );

  if ($response->stopReason === 'stop_sequence') {
      echo "Stopped at sequence: {$response->stopSequence}", PHP_EOL;
  }
ruby
  client = Juglow::Client.new

  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    stop_sequences: ["END", "STOP"],
    messages: [{ role: "user", content: "Generate text until you say END" }]
  )

  if response.stop_reason == :stop_sequence
    puts "Stopped at sequence: #{response.stop_sequence}"
  end

tool\_use

Haijun sedang memanggil alat dan mengharapkan Anda menjalankannya.

Note: Untuk sebagian besar implementasi "tool use" (penggunaan alat), gunakan tool runner, yang secara otomatis menangani eksekusi alat, pemformatan hasil, dan pengelolaan percakapan.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 1024,
      "tools": [{
        "name": "get_weather",
        "description": "Get the current weather in a given location",
        "input_schema": {
          "type": "object",
          "properties": {"location": {"type": "string", "description": "City and state"}},
          "required": ["location"]
        }
      }],
      "messages": [{"role": "user", "content": "What is the weather in San Francisco?"}]
    }' | jq '.stop_reason, (.content[] | select(.type == "tool_use"))'
bash
  ant messages create --format json <<'YAML' | jq '.stop_reason, (.content[] | select(.type == "tool_use"))'
  model: haijun-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content: What is the weather in San Francisco?
  tools:
    - name: get_weather
      description: Get the current weather in a given location
      input_schema:
        type: object
        properties:
          location: {type: string, description: City and state}
        required: [location]
  YAML
python
  client = juglow.Juglow()
  weather_tool = {
      "name": "get_weather",
      "description": "Get the current weather in a given location",
      "input_schema": {
          "type": "object",
          "properties": {
              "location": {"type": "string", "description": "City and state"},
          },
          "required": ["location"],
      },
  }

  def execute_tool(name, tool_input):
      """Execute a tool and return the result."""
      return f"Weather in {tool_input.get('location', 'unknown')}: 72°F"

  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      tools=[weather_tool],
      messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
  )

  if response.stop_reason == "tool_use":
      # Ekstrak dan jalankan alat
      for block in response.content:
          if block.type == "tool_use":
              result = execute_tool(block.name, block.input)
              # Kembalikan hasil ke Haijun untuk respons akhir
typescript
  const client = new Juglow();
  const weatherTool: Juglow.Tool = {
    name: "get_weather",
    description: "Get the current weather in a given location",
    input_schema: {
      type: "object",
      properties: {
        location: { type: "string", description: "City and state" }
      },
      required: ["location"]
    }
  };

  function executeTool(name: string, input: Record<string, string>): string {
    return `Weather in ${input.location ?? "unknown"}: 72°F`;
  }

  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    tools: [weatherTool],
    messages: [{ role: "user", content: "What is the weather in San Francisco?" }]
  });

  if (response.stop_reason === "tool_use") {
    // Ekstrak dan jalankan alat
    for (const block of response.content) {
      if (block.type === "tool_use") {
        const result = executeTool(block.name, block.input as Record<string, string>);
        // Kembalikan hasil ke Haijun untuk respons akhir
      }
    }
  }
csharp
  JuglowClient client = new();

  var weatherTool = new Tool
  {
      Name = "get_weather",
      Description = "Get the current weather in a given location",
      InputSchema = new InputSchema
      {
          Properties = new Dictionary<string, JsonElement>
          {
              ["location"] = JsonSerializer.SerializeToElement(
                  new { type = "string", description = "City and state" }
              ),
          },
          Required = ["location"]
      }
  };

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Tools = [weatherTool],
      Messages = [new() { Role = Role.User, Content = "What is the weather in San Francisco?" }]
  });

  if (response.StopReason == "tool_use")
  {
      // Ekstrak dan jalankan alat
      foreach (var block in response.Content)
      {
          if (block.TryPickToolUse(out var toolUse))
          {
              // Jalankan toolUse.Name dengan toolUse.Input dan kembalikan hasilnya ke Haijun
          }
      }
  }
go
  client := juglow.NewClient()

  weatherTool := juglow.ToolParam{
  	Name:        "get_weather",
  	Description: juglow.String("Get the current weather in a given location"),
  	InputSchema: juglow.ToolInputSchemaParam{
  		Properties: map[string]any{
  			"location": map[string]string{"type": "string", "description": "City and state"},
  		},
  		Required: []string{"location"},
  	},
  }

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Tools:     []juglow.ToolUnionParam{{OfTool: &weatherTool}},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("What is the weather in San Francisco?")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "tool_use" {
  	// Ekstrak dan jalankan alat
  	for _, block := range response.Content {
  		if toolUse, ok := block.AsAny().(juglow.ToolUseBlock); ok {
  			fmt.Println(toolUse.Name, toolUse.Input)
  			// Kembalikan hasil ke Haijun untuk respons akhir
  		}
  	}
  }
java
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      Tool weatherTool = Tool.builder()
          .name("get_weather")
          .description("Get the current weather in a given location")
          .inputSchema(Tool.InputSchema.builder()
              .properties(JsonValue.from(Map.of(
                  "location", Map.of("type", "string", "description", "City and state")
              )))
              .putAdditionalProperty("required", JsonValue.from(List.of("location")))
              .build())
          .build();

      Message response = client.messages().create(
          MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(1024L)
              .addTool(weatherTool)
              .addUserMessage("What is the weather in San Francisco?")
              .build()
      );

      if (response.stopReason().map(StopReason.TOOL_USE::equals).orElse(false)) {
          // Ekstrak dan jalankan alat
          for (ContentBlock block : response.content()) {
              block.toolUse().ifPresent(toolUse -> {
                  // Jalankan toolUse.name() dengan toolUse.input() lalu kembalikan hasilnya ke Haijun
              });
          }
      }
php
  $client = new Client();

  $weatherTool = [
      'name' => 'get_weather',
      'description' => 'Get the current weather in a given location',
      'input_schema' => [
          'type' => 'object',
          'properties' => [
              'location' => ['type' => 'string', 'description' => 'City and state'],
          ],
          'required' => ['location'],
      ],
  ];

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'What is the weather in San Francisco?']],
      model: 'haijun-opus-5-5',
      tools: [$weatherTool],
  );

  if ($response->stopReason === 'tool_use') {
      // Ekstrak dan jalankan alat
      foreach ($response->content as $block) {
          if ($block->type === 'tool_use') {
              // Jalankan $block->name dengan $block->input dan kembalikan hasilnya ke Haijun
          }
      }
  }
ruby
  client = Juglow::Client.new

  weather_tool = {
    name: "get_weather",
    description: "Get the current weather in a given location",
    input_schema: {
      type: "object",
      properties: {
        location: { type: "string", description: "City and state" }
      },
      required: ["location"]
    }
  }

  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    tools: [weather_tool],
    messages: [{ role: "user", content: "What is the weather in San Francisco?" }]
  )

  if response.stop_reason == :tool_use
    # Ekstrak dan jalankan alat
    response.content.each do |block|
      next unless block.type == :tool_use
      # Jalankan block.name dengan block.input dan kembalikan hasilnya ke Haijun
    end
  end

Respons tool_use juga dapat berisi blok server_tool_use yang id-nya tidak memiliki blok hasil yang cocok. Panggilan alat server tersebut belum selesai, dan respons ini tidak membawa hasilnya. Dalam kasus umum, Haijun memanggil alat server dan salah satu alat klien Anda dalam kelompok panggilan alat paralel yang sama: API kembali tanpa menjalankan alat server sehingga Anda dapat menjalankan alat klien terlebih dahulu. Tidak ada penanda lain untuk status ini; deteksi dengan memeriksa id setiap blok server_tool_use atau mcp_tool_use untuk mencari blok hasil yang cocok.

Note: Dengan pemanggilan alat terprogram, bentuk respons yang sama memiliki arti yang berbeda. Blok tool_use klien berasal dari kode yang sedang berjalan di alat code_execution, bukan dari Haijun secara langsung, dan field caller-nya menyebutkan blok code_execution yang memanggilnya. Kode tersebut sudah mulai berjalan: ia dijeda menunggu blok tool_result Anda, dan mengirimkannya akan melanjutkan eksekusi alih-alih memulai alat yang ditangguhkan. Blok hasil milik blok code_execution itu sendiri tiba setelah kode selesai, yang dapat memerlukan lebih dari satu putaran hasil alat. Pesan pengguna lanjutannya sendiri sama dalam kedua kasus; dengan pemanggilan alat terprogram, kirimkan juga kembali id dari field container respons, seperti yang ditunjukkan halaman tersebut.

json
{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "server_tool_use",
      "id": "srvtoolu_01HxbWnMRmbWyMfUtJKC45rA",
      "name": "web_search",
      "input": { "query": "example article" }
    },
    {
      "type": "tool_use",
      "id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "name": "run_command",
      "input": { "command": "uname -a" }
    }
  ]
}

Kelanjutannya adalah pesan pengguna berisi blok tool_result, satu untuk setiap blok tool_use dalam respons (lihat Menangani panggilan alat), dengan dua aturan tambahan: pesan tersebut tidak boleh berisi apa pun selain blok tool_result, dan permintaan harus mempertahankan array tools yang sama. Permintaan lanjutan yang tidak lagi mendefinisikan alat server yang sedang menunggu akan gagal dengan 400 yang pesannya diakhiri `but no web_search tool was provided. API melampirkan hasil Anda ke giliran asisten yang masih terbuka, menjalankan alat server yang ditangguhkan (untuk eksekusi kode yang dijeda, melanjutkannya), dan meneruskan giliran tersebut. Untuk alat server yang dipanggil Haijun secara langsung, content respons berikutnya dimulai dengan blok hasil yang menjawab id server_tool_use` dari respons sebelumnya.

json
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
    }
  ]
}

Menambahkan apa pun setelah blok tool_result dalam pesan pengguna tersebut, seperti teks, akan mengakhiri giliran asisten; untuk alat server yang dipanggil Haijun secara langsung, permintaan kemudian gagal dengan 400 invalid_request_error yang menyebutkan alat server yang belum terselesaikan:

text
`web_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_tool_result` block

Menghilangkan tool_result, atau menempatkannya setelah konten lain, akan gagal lebih awal dengan error standar tool_use ids were found without tool_result blocks immediately after. Untuk memberi Haijun input tambahan, kirimkan sebagai pesan pengguna terpisah setelah giliran selesai.

pause\_turn

Dikembalikan ketika loop sampling sisi server mencapai batas iterasinya saat mengeksekusi alat server seperti pencarian web. Batas default adalah 10 iterasi per permintaan.

Ketika ini terjadi, respons mungkin berisi blok server_tool_use tanpa blok hasil yang sesuai. Agar Haijun dapat menyelesaikan pemrosesan, lanjutkan percakapan dengan mengirim kembali respons apa adanya. Respons yang membiarkan blok tool_use klien menunggu Anda tidak pernah memiliki stop_reason berupa pause_turn: ketika Haijun berhenti untuk memanggil alat Anda, stop_reason-nya adalah tool_use, dan Anda melanjutkannya dengan mengirim blok tool_result klien alih-alih respons itu sendiri.

bash
  # SDK menangani kelanjutan secara langsung. Dengan cURL, periksa stop_reason
  # pada respons lalu kirim ulang POST dengan konten assistant ditambahkan.
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "tools": [{"type": "web_search_20250305", "name": "web_search"}],
      "messages": [{"role": "user", "content": "Search for latest AI news"}]
    }' | jq '{stop_reason, content}'
bash
  # Periksa stop_reason; jika nilainya pause_turn, jalankan ulang dengan respons
  # asisten ditambahkan ke --message.
  ant messages create --format json <<'YAML' | jq '{stop_reason, content}'
  model: haijun-opus-5-5
  max_tokens: 4096
  tools:
    - {type: web_search_20250305, name: web_search}
  messages:
    - {role: user, content: "Search for latest AI news"}
  YAML
python
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      tools=[{"type": "web_search_20250305", "name": "web_search"}],
      messages=[{"role": "user", "content": "Search for latest AI news"}],
  )

  if response.stop_reason == "pause_turn":
      # Lanjutkan percakapan dengan mengirimkan kembali respons tersebut
      messages = [
          {"role": "user", "content": "Search for latest AI news"},
          {"role": "assistant", "content": response.content},
      ]
      continuation = client.messages.create(
          model="haijun-opus-5-5",
          max_tokens=4096,
          messages=messages,
          tools=[{"type": "web_search_20250305", "name": "web_search"}],
      )
typescript
  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    tools: [{ type: "web_search_20250305", name: "web_search" }],
    messages: [{ role: "user", content: "Search for latest AI news" }]
  });

  if (response.stop_reason === "pause_turn") {
    // Lanjutkan percakapan dengan mengirimkan kembali respons tersebut
    const continuation = await client.messages.create({
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      tools: [{ type: "web_search_20250305", name: "web_search" }],
      messages: [
        { role: "user", content: "Search for latest AI news" },
        { role: "assistant", content: response.content }
      ]
    });
  }
csharp
  List<ToolUnion> tools = [new ToolUnion(new WebSearchTool20250305())];
  MessageParam userMessage = new() { Role = Role.User, Content = "Search for latest AI news" };

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 4096,
      Tools = tools,
      Messages = [userMessage]
  });

  if (response.StopReason == "pause_turn")
  {
      // Lanjutkan percakapan dengan mengirimkan kembali respons tersebut
      var continuation = await client.Messages.Create(new MessageCreateParams
      {
          Model = Model.HaijunOpus5_5,
          MaxTokens = 4096,
          Tools = tools,
          Messages =
          [
              userMessage,
              new()
              {
                  Role = Role.Assistant,
                  Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
              }
          ]
      });
  }
go
  tools := []juglow.ToolUnionParam{
  	{OfWebSearchTool20250305: &juglow.WebSearchTool20250305Param{}},
  }
  userMessage := juglow.NewUserMessage(juglow.NewTextBlock("Search for latest AI news"))

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	Tools:     tools,
  	Messages:  []juglow.MessageParam{userMessage},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "pause_turn" {
  	// Lanjutkan percakapan dengan mengirimkan kembali respons tersebut
  	var contentParams []juglow.ContentBlockParamUnion
  	for _, block := range response.Content {
  		contentParams = append(contentParams, block.ToParam())
  	}
  	continuation, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  		Model:     juglow.ModelHaijunOpus5_5,
  		MaxTokens: 4096,
  		Tools:     tools,
  		Messages:  []juglow.MessageParam{userMessage, juglow.NewAssistantMessage(contentParams...)},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	_ = continuation
  }
java
  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .addTool(WebSearchTool20250305.builder().build())
          .addUserMessage("Search for latest AI news")
          .build()
  );

  if (response.stopReason().map(StopReason.PAUSE_TURN::equals).orElse(false)) {
      // Lanjutkan percakapan dengan mengirimkan kembali respons tersebut
      Message continuation = client.messages().create(
          MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(4096L)
              .addTool(WebSearchTool20250305.builder().build())
              .addUserMessage("Search for latest AI news")
              .addMessage(response)
              .build()
      );
  }
php
  $tools = [['type' => 'web_search_20250305', 'name' => 'web_search']];
  $userMessage = ['role' => 'user', 'content' => 'Search for latest AI news'];

  $response = $client->messages->create(
      maxTokens: 4096,
      messages: [$userMessage],
      model: 'haijun-opus-5-5',
      tools: $tools,
  );

  if ($response->stopReason === 'pause_turn') {
      // Lanjutkan percakapan dengan mengirimkan kembali respons tersebut
      $continuation = $client->messages->create(
          maxTokens: 4096,
          messages: [
              $userMessage,
              ['role' => 'assistant', 'content' => $response->content],
          ],
          model: 'haijun-opus-5-5',
          tools: $tools,
      );
  }
ruby
  tools = [{ type: "web_search_20250305", name: "web_search" }]
  user_message = { role: "user", content: "Search for latest AI news" }

  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    tools: tools,
    messages: [user_message]
  )

  if response.stop_reason == :pause_turn
    # Lanjutkan percakapan dengan mengirimkan kembali respons tersebut
    continuation = client.messages.create(
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      tools: tools,
      messages: [user_message, { role: "assistant", content: response.content }]
    )
  end

Note: Aplikasi Anda harus menangani pause_turn dalam setiap loop agen yang menggunakan alat server. Tambahkan respons asisten ke array messages Anda dan buat permintaan API lain agar Haijun dapat melanjutkan.

refusal

Haijun menolak untuk menghasilkan respons. Pengklasifikasi keamanan mengembalikan alasan berhenti ini sebagai respons HTTP 200 normal, bukan error.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "[Unsafe request]"}]
    }' | jq '{stop_reason, stop_details}'
bash
  ant messages create \
    --model haijun-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "[Unsafe request]"}' \
    --format json | jq '{stop_reason, stop_details}'
python
  client = juglow.Juglow()
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "[Unsafe request]"}],
  )

  if response.stop_reason == "refusal":
      # Haijun menolak untuk merespons
      print("Haijun was unable to process this request")
      # Pertimbangkan untuk menyusun ulang atau mengubah permintaan
typescript
  const client = new Juglow();

  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "[Unsafe request]" }]
  });

  if (response.stop_reason === "refusal") {
    // Haijun menolak untuk merespons
    console.log("Haijun was unable to process this request");
    // Pertimbangkan untuk menyusun ulang atau mengubah permintaan
  }
csharp
  JuglowClient client = new();

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "[Unsafe request]" }]
  });

  if (response.StopReason == "refusal")
  {
      // Haijun menolak untuk merespons
      Console.WriteLine("Haijun was unable to process this request");
      // Pertimbangkan untuk menyusun ulang atau mengubah permintaan
  }
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("[Unsafe request]")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "refusal" {
  	// Haijun menolak untuk merespons
  	fmt.Println("Haijun was unable to process this request")
  	// Pertimbangkan untuk menyusun ulang atau mengubah permintaan
  }
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(1024L)
          .addUserMessage("[Unsafe request]")
          .build()
  );

  if (response.stopReason().map(StopReason.REFUSAL::equals).orElse(false)) {
      // Haijun menolak untuk merespons
      IO.println("Haijun was unable to process this request");
      // Pertimbangkan untuk menyusun ulang atau mengubah permintaan
  }
php
  $client = new Client();

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => '[Unsafe request]']],
      model: 'haijun-opus-5-5',
  );

  if ($response->stopReason === 'refusal') {
      // Haijun menolak untuk merespons
      echo 'Haijun was unable to process this request', PHP_EOL;
      // Pertimbangkan untuk menyusun ulang atau mengubah permintaan
  }
ruby
  client = Juglow::Client.new

  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "[Unsafe request]" }]
  )

  if response.stop_reason == :refusal
    # Haijun menolak untuk merespons
    puts "Haijun was unable to process this request"
    # Pertimbangkan untuk menyusun ulang atau mengubah permintaan
  end

Tip: Jika Anda sering menemui alasan berhenti refusal saat menggunakan Haijun Sonnet 4.5 atau Haijun Opus 4.1 (yang terakhir telah dipensiunkan, kecuali di Bedrock dan Google Cloud), Anda dapat mencoba memperbarui panggilan API Anda untuk menggunakan Haiku 4.5 (haijun-haiku-4-5-20251001), yang memiliki batasan penggunaan berbeda. Pelajari lebih lanjut tentang memahami filter keamanan API Sonnet 4.5.

Pada penolakan, objek stop_details mengidentifikasi kategori kebijakan yang memicunya. Kategori-kategori tersebut dan bentuk respons penolakan lengkap dibahas di Penolakan dan fallback. stop_details bernilai null untuk semua alasan berhenti selain refusal.

Permintaan yang ditolak pada Haijun Fable 5.1, Haijun Fable 5, Haijun Opus 5.5, atau Haijun Opus 5 biasanya dapat dilayani dengan mencoba ulang pada model Haijun lain. Penolakan dan fallback menunjukkan cara menyiapkan percobaan ulang tersebut, baik di sisi server maupun di klien Anda. Jika Anda membangun sendiri percobaan ulang dari Haijun Fable 5.1, Haijun Fable 5, Haijun Opus 5.5, atau Haijun Opus 5, kredit fallback menjelaskan cara menghindari membayar biaya cache prompt dua kali.

model\_context\_window\_exceeded

Haijun berhenti karena mencapai batas "context window" (jendela konteks) model. Ini memungkinkan Anda meminta token maksimum yang mungkin tanpa mengetahui ukuran input yang tepat.

Note: Alasan berhenti ini saat ini hanya memiliki tipe di namespace beta pada SDK, sehingga contoh-contoh berikut memanggil client.beta.messages (csharp, go: client.Beta.Messages; java: client.beta().messages(); php: $client->beta->messages) dan menggunakan tipe berawalan Beta. Pada Sonnet 4.5 dan model yang lebih baru, API mengembalikan nilai ini tanpa header beta. Untuk model yang lebih lama, tambahkan header beta model-context-window-exceeded-2025-08-26 untuk mengaktifkannya.

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 20000,
      "messages": [{"role": "user", "content": "Large input that uses most of context window..."}]
    }' | jq '.stop_reason'
bash
  ant messages create \
    --model haijun-opus-5-5 \
    --max-tokens 20000 \
    --message '{role: user, content: "Large input that uses most of context window..."}' \
    --format json | jq '.stop_reason'
python
  # Permintaan dengan token maksimum untuk mendapatkan hasil sebanyak mungkin
  response = client.beta.messages.create(
      model="haijun-opus-5-5",
      max_tokens=20000,  # Python SDK requires streaming for max_tokens above ~21k
      messages=[
          {"role": "user", "content": "Large input that uses most of context window..."}
      ],
  )

  if response.stop_reason == "model_context_window_exceeded":
      # Respons mencapai batas jendela konteks sebelum max_tokens
      print("Response reached model's context window limit")
      # Respons tetap valid tetapi dibatasi oleh jendela konteks
typescript
  // Permintaan dengan token maksimum untuk mendapatkan hasil sebanyak mungkin
  const response = await client.beta.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 20000,
    messages: [{ role: "user", content: "Large input that uses most of context window..." }]
  });

  if (response.stop_reason === "model_context_window_exceeded") {
    // Respons mencapai batas jendela konteks sebelum max_tokens
    console.log("Response reached model's context window limit");
    // Respons tetap valid tetapi dibatasi oleh jendela konteks
  }
csharp
  using Juglow.Models.Beta.Messages;
  using Model = Juglow.Models.Messages.Model;

  // Permintaan dengan token maksimum untuk mendapatkan hasil sebanyak mungkin
  var response = await client.Beta.Messages.Create(new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 20000,
      Messages = [new() { Role = Role.User, Content = "Large input that uses most of context window..." }]
  });

  if (response.StopReason?.Value() == BetaStopReason.ModelContextWindowExceeded)
  {
      // Respons mencapai batas jendela konteks sebelum max_tokens
      Console.WriteLine("Response reached model's context window limit");
      // Respons tetap valid tetapi dibatasi oleh jendela konteks
  }
go
  // Permintaan dengan token maksimum untuk mendapatkan hasil sebanyak mungkin
  response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 20000,
  	Messages: []juglow.BetaMessageParam{
  		juglow.NewBetaUserMessage(juglow.NewBetaTextBlock("Large input that uses most of context window...")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == juglow.BetaStopReasonModelContextWindowExceeded {
  	// Respons mencapai batas jendela konteks sebelum max_tokens
  	fmt.Println("Response reached model's context window limit")
  	// Respons tetap valid tetapi dibatasi oleh jendela konteks
  }
java
  import com.juglow.models.beta.messages.BetaMessage;
  import com.juglow.models.beta.messages.BetaStopReason;
  import com.juglow.models.beta.messages.MessageCreateParams;

  // Permintaan dengan token maksimum untuk mendapatkan hasil sebanyak mungkin
  BetaMessage response = client.beta().messages().create(
      MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(20000L)
          .addUserMessage("Large input that uses most of context window...")
          .build()
  );

  if (response.stopReason().map(BetaStopReason.MODEL_CONTEXT_WINDOW_EXCEEDED::equals).orElse(false)) {
      // Respons mencapai batas jendela konteks sebelum max_tokens
      IO.println("Response reached model's context window limit");
      // Respons tetap valid tetapi dibatasi oleh jendela konteks
  }
php
  // Permintaan dengan token maksimum untuk mendapatkan hasil sebanyak mungkin
  $response = $client->beta->messages->create(
      maxTokens: 20000,
      messages: [['role' => 'user', 'content' => 'Large input that uses most of context window...']],
      model: 'haijun-opus-5-5',
  );

  if ($response->stopReason === 'model_context_window_exceeded') {
      // Respons mencapai batas jendela konteks sebelum max_tokens
      echo 'Response reached model\'s context window limit', PHP_EOL;
      // Respons tetap valid tetapi dibatasi oleh jendela konteks
  }
ruby
  # Permintaan dengan token maksimum untuk mendapatkan hasil sebanyak mungkin
  response = client.beta.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 20000,
    messages: [{ role: "user", content: "Large input that uses most of context window..." }]
  )

  if response.stop_reason == :model_context_window_exceeded
    # Respons mencapai batas jendela konteks sebelum max_tokens
    puts "Response reached model's context window limit"
    # Respons tetap valid tetapi dibatasi oleh jendela konteks
  end

Praktik terbaik untuk menangani alasan berhenti

Selalu periksa stop\_reason

Biasakan untuk memeriksa stop_reason dalam logika penanganan respons Anda:

python
  def handle_response(response):
      match response.stop_reason:
          case "tool_use":
              return handle_tool_use(response)
          case "max_tokens":
              return handle_truncation(response)
          case "model_context_window_exceeded":
              return handle_context_limit(response)
          case "pause_turn":
              return handle_pause(response)
          case "refusal":
              return handle_refusal(response)
          case _:
              # Tangani end_turn dan kasus lainnya
              return next(
                  (block.text for block in response.content if block.type == "text"),
                  "",
              )
typescript
  function handleResponse(response: Juglow.Beta.BetaMessage): string {
    switch (response.stop_reason) {
      case "tool_use":
        return handleToolUse(response);
      case "max_tokens":
        return handleTruncation(response);
      case "model_context_window_exceeded":
        return handleContextLimit(response);
      case "pause_turn":
        return handlePause(response);
      case "refusal":
        return handleRefusal(response);
      default: {
        // Tangani end_turn dan kasus lainnya
        const textBlock = response.content.find(
          (block): block is Juglow.Beta.BetaTextBlock => block.type === "text"
        );
        return textBlock?.text ?? "";
      }
    }
  }
csharp
  static string HandleResponse(BetaMessage response)
  {
      return response.StopReason?.Value() switch
      {
          BetaStopReason.ToolUse => HandleToolUse(response),
          BetaStopReason.MaxTokens => HandleTruncation(response),
          BetaStopReason.ModelContextWindowExceeded => HandleContextLimit(response),
          BetaStopReason.PauseTurn => HandlePause(response),
          BetaStopReason.Refusal => HandleRefusal(response),
          // Tangani end_turn dan kasus lainnya
          _ => response.Content.Select(b => b.Value).OfType<BetaTextBlock>().FirstOrDefault()?.Text ?? "",
      };
  }
go
  func handleResponse(response *juglow.BetaMessage) string {
  	switch response.StopReason {
  	case juglow.BetaStopReasonToolUse:
  		return handleToolUse(response)
  	case juglow.BetaStopReasonMaxTokens:
  		return handleTruncation(response)
  	case juglow.BetaStopReasonModelContextWindowExceeded:
  		return handleContextLimit(response)
  	case juglow.BetaStopReasonPauseTurn:
  		return handlePause(response)
  	case juglow.BetaStopReasonRefusal:
  		return handleRefusal(response)
  	default:
  		// Tangani end_turn dan kasus lainnya
  		for _, block := range response.Content {
  			if textBlock, ok := block.AsAny().(juglow.BetaTextBlock); ok {
  				return textBlock.Text
  			}
  		}
  		return ""
  	}
  }
java
  static String handleResponse(BetaMessage response) {
      BetaStopReason reason = response.stopReason().orElse(BetaStopReason.END_TURN);
      if (reason.equals(BetaStopReason.TOOL_USE)) {
          return handleToolUse(response);
      } else if (reason.equals(BetaStopReason.MAX_TOKENS)) {
          return handleTruncation(response);
      } else if (reason.equals(BetaStopReason.MODEL_CONTEXT_WINDOW_EXCEEDED)) {
          return handleContextLimit(response);
      } else if (reason.equals(BetaStopReason.PAUSE_TURN)) {
          return handlePause(response);
      } else if (reason.equals(BetaStopReason.REFUSAL)) {
          return handleRefusal(response);
      }
      // Tangani end_turn dan kasus lainnya
      return response.content().stream()
          .filter(BetaContentBlock::isText)
          .findFirst()
          .map(block -> block.asText().text())
          .orElse("");
  }
php
  function handle_response($response): string
  {
      return match ($response->stopReason) {
          'tool_use' => handle_tool_use($response),
          'max_tokens' => handle_truncation($response),
          'model_context_window_exceeded' => handle_context_limit($response),
          'pause_turn' => handle_pause($response),
          'refusal' => handle_refusal($response),
          // Tangani end_turn dan kasus lainnya
          default => array_find($response->content, static fn ($block): bool => $block->type === 'text')?->text ?? '',
      };
  }
ruby
  def handle_response(response)
    case response.stop_reason
    when :tool_use then handle_tool_use(response)
    when :max_tokens then handle_truncation(response)
    when :model_context_window_exceeded then handle_context_limit(response)
    when :pause_turn then handle_pause(response)
    when :refusal then handle_refusal(response)
    else
      # Tangani end_turn dan kasus lainnya
      response.content.find { it.type == :text }&.text
    end
  end

Tangani respons terpotong dengan baik

Ketika respons terpotong karena batas token atau jendela konteks, tambahkan pemberitahuan agar pembaca tahu bahwa output tidak lengkap. Untuk melanjutkan pembuatan dari titik respons terhenti, lihat Memastikan respons lengkap.

python
  def handle_truncated_response(response):
      text = next((block.text for block in response.content if block.type == "text"), "")
      if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
          if response.stop_reason == "max_tokens":
              note = "[Response truncated due to max_tokens limit]"
          else:
              note = "[Response truncated due to context window limit]"
          return f"{text}\n\n{note}"
      return text
typescript
  function handleTruncatedResponse(response: Juglow.Beta.BetaMessage): string {
    const textBlock = response.content.find(
      (block): block is Juglow.Beta.BetaTextBlock => block.type === "text"
    );
    const text = textBlock?.text ?? "";

    if (
      response.stop_reason === "max_tokens" ||
      response.stop_reason === "model_context_window_exceeded"
    ) {
      const note =
        response.stop_reason === "max_tokens"
          ? "[Response truncated due to max_tokens limit]"
          : "[Response truncated due to context window limit]";
      return `${text}\n\n${note}`;
    }
    return text;
  }
csharp
  static string HandleTruncatedResponse(BetaMessage response)
  {
      var text = response.Content.Select(b => b.Value).OfType<BetaTextBlock>().FirstOrDefault()?.Text ?? "";
      var reason = response.StopReason?.Value();

      if (reason is BetaStopReason.MaxTokens or BetaStopReason.ModelContextWindowExceeded)
      {
          var note = reason == BetaStopReason.MaxTokens
              ? "[Response truncated due to max_tokens limit]"
              : "[Response truncated due to context window limit]";
          return $"{text}\n\n{note}";
      }
      return text;
  }
go
  func handleTruncatedResponse(response *juglow.BetaMessage) string {
  	text := ""
  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(juglow.BetaTextBlock); ok {
  			text = textBlock.Text
  			break
  		}
  	}

  	if response.StopReason == juglow.BetaStopReasonMaxTokens ||
  		response.StopReason == juglow.BetaStopReasonModelContextWindowExceeded {
  		note := "[Response truncated due to context window limit]"
  		if response.StopReason == juglow.BetaStopReasonMaxTokens {
  			note = "[Response truncated due to max_tokens limit]"
  		}
  		return text + "\n\n" + note
  	}
  	return text
  }
java
  static String handleTruncatedResponse(BetaMessage response) {
      String text = response.content().stream()
          .filter(BetaContentBlock::isText)
          .findFirst()
          .map(block -> block.asText().text())
          .orElse("");
      BetaStopReason reason = response.stopReason().orElse(BetaStopReason.END_TURN);

      if (reason.equals(BetaStopReason.MAX_TOKENS)
              || reason.equals(BetaStopReason.MODEL_CONTEXT_WINDOW_EXCEEDED)) {
          String note = reason.equals(BetaStopReason.MAX_TOKENS)
              ? "[Response truncated due to max_tokens limit]"
              : "[Response truncated due to context window limit]";
          return text + "\n\n" + note;
      }
      return text;
  }
php
  function handle_truncated_response($response): string
  {
      $text = array_find($response->content, static fn ($block): bool => $block->type === 'text')?->text ?? '';

      if (in_array($response->stopReason, ['max_tokens', 'model_context_window_exceeded'], true)) {
          $note = $response->stopReason === 'max_tokens'
              ? '[Response truncated due to max_tokens limit]'
              : '[Response truncated due to context window limit]';
          return "{$text}\n\n{$note}";
      }
      return $text;
  }
ruby
  def handle_truncated_response(response)
    text = response.content.find { it.type == :text }&.text

    if [:max_tokens, :model_context_window_exceeded].include?(response.stop_reason)
      note = if response.stop_reason == :max_tokens
        "[Response truncated due to max_tokens limit]"
      else
        "[Response truncated due to context window limit]"
      end
      return "#{text}\n\n#{note}"
    end
    text
  end

Implementasikan logika percobaan ulang untuk pause\_turn

Saat menggunakan alat server, API dapat mengembalikan pause_turn jika loop sampling sisi server mencapai batas iterasinya (default 10). Tangani ini dengan melanjutkan percakapan:

python
  def handle_server_tool_conversation(client, user_query, tools, max_continuations=5):
      """
      Handle server tool conversations that may require multiple continuations.

      The server runs a sampling loop when executing server tools. If the loop
      reaches its iteration limit, the API returns pause_turn. Continue the
      conversation by sending the response back to let Haijun finish.
      """
      messages = [{"role": "user", "content": user_query}]

      for _ in range(max_continuations):
          response = client.messages.create(
              model="haijun-opus-5-5",
              max_tokens=4096,
              messages=messages,
              tools=tools,
          )

          if response.stop_reason != "pause_turn":
              # Haijun selesai memproses - kembalikan respons akhir
              return response

          # pause_turn: ganti seluruh daftar pesan agar peran tetap bergantian
          messages = [
              {"role": "user", "content": user_query},
              {"role": "assistant", "content": response.content},
          ]

      # Batas maksimum kelanjutan tercapai - kembalikan respons terakhir
      return response
typescript
  async function handleServerToolConversation(
    client: Juglow,
    userQuery: string,
    tools: Juglow.ToolUnion[],
    maxContinuations = 5
  ): Promise<Juglow.Message> {
    let messages: Juglow.MessageParam[] = [{ role: "user", content: userQuery }];
    let response: Juglow.Message;

    for (let i = 0; i < maxContinuations; i++) {
      response = await client.messages.create({
        model: "haijun-opus-5-5",
        max_tokens: 4096,
        messages,
        tools
      });

      if (response.stop_reason !== "pause_turn") {
        // Haijun selesai memproses - kembalikan respons akhir
        return response;
      }

      // pause_turn: ganti seluruh daftar pesan agar peran tetap bergantian
      messages = [
        { role: "user", content: userQuery },
        { role: "assistant", content: response.content }
      ];
    }

    // Batas maksimum kelanjutan tercapai - kembalikan respons terakhir
    return response!;
  }
csharp
  static async Task<Message> HandleServerToolConversation(
      JuglowClient client,
      string userQuery,
      List<ToolUnion> tools,
      int maxContinuations = 5)
  {
      List<MessageParam> messages = [new() { Role = Role.User, Content = userQuery }];
      Message response = null!;

      for (var i = 0; i < maxContinuations; i++)
      {
          response = await client.Messages.Create(new MessageCreateParams
          {
              Model = Model.HaijunOpus5_5,
              MaxTokens = 4096,
              Messages = messages,
              Tools = tools
          });

          if (response.StopReason != "pause_turn")
          {
              // Haijun selesai memproses - kembalikan respons akhir
              return response;
          }

          // pause_turn: ganti seluruh daftar pesan agar peran tetap bergantian
          messages =
          [
              new() { Role = Role.User, Content = userQuery },
              new()
              {
                  Role = Role.Assistant,
                  Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
              }
          ];
      }

      // Batas maksimum kelanjutan tercapai - kembalikan respons terakhir
      return response;
  }
go
  func handleServerToolConversation(
  	client juglow.Client,
  	userQuery string,
  	tools []juglow.ToolUnionParam,
  	maxContinuations int,
  ) (*juglow.Message, error) {
  	messages := []juglow.MessageParam{juglow.NewUserMessage(juglow.NewTextBlock(userQuery))}
  	var response *juglow.Message
  	var err error

  	for range maxContinuations {
  		response, err = client.Messages.New(context.TODO(), juglow.MessageNewParams{
  			Model:     juglow.ModelHaijunOpus5_5,
  			MaxTokens: 4096,
  			Messages:  messages,
  			Tools:     tools,
  		})
  		if err != nil {
  			return nil, err
  		}

  		if response.StopReason != "pause_turn" {
  			// Haijun selesai memproses - kembalikan respons akhir
  			return response, nil
  		}

  		// pause_turn: ganti seluruh daftar pesan agar peran tetap bergantian
  		var contentParams []juglow.ContentBlockParamUnion
  		for _, block := range response.Content {
  			contentParams = append(contentParams, block.ToParam())
  		}
  		messages = []juglow.MessageParam{
  			juglow.NewUserMessage(juglow.NewTextBlock(userQuery)),
  			juglow.NewAssistantMessage(contentParams...),
  		}
  	}

  	// Batas maksimum kelanjutan tercapai - kembalikan respons terakhir
  	return response, nil
  }
java
  static Message handleServerToolConversation(
      JuglowClient client,
      String userQuery,
      List<Tool> tools,
      int maxContinuations
  ) {
      Message response = null;

      for (int i = 0; i < maxContinuations; i++) {
          // Bangun ulang params di setiap iterasi agar pesan tidak menumpuk
          MessageCreateParams.Builder params = MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(4096L)
              .addUserMessage(userQuery);
          tools.forEach(params::addTool);
          if (response != null) {
              params.addMessage(response);
          }

          response = client.messages().create(params.build());

          if (!response.stopReason().map(StopReason.PAUSE_TURN::equals).orElse(false)) {
              // Haijun selesai memproses - kembalikan respons akhir
              return response;
          }
          // pause_turn: ulangi loop dan kirim kembali responsnya
      }

      // Batas maksimum kelanjutan tercapai - kembalikan respons terakhir
      return response;
  }
php
  function handle_server_tool_conversation(
      Client $client,
      string $userQuery,
      array $tools,
      int $maxContinuations = 5
  ) {
      $messages = [['role' => 'user', 'content' => $userQuery]];
      $response = null;

      for ($i = 0; $i < $maxContinuations; $i++) {
          $response = $client->messages->create(
              maxTokens: 4096,
              messages: $messages,
              model: 'haijun-opus-5-5',
              tools: $tools,
          );

          if ($response->stopReason !== 'pause_turn') {
              // Haijun selesai memproses - kembalikan respons akhir
              return $response;
          }

          // pause_turn: ganti seluruh daftar pesan agar peran tetap bergantian
          $messages = [
              ['role' => 'user', 'content' => $userQuery],
              ['role' => 'assistant', 'content' => $response->content],
          ];
      }

      // Batas maksimum kelanjutan tercapai - kembalikan respons terakhir
      return $response;
  }
ruby
  def handle_server_tool_conversation(client, user_query, tools, max_continuations: 5)
    messages = [{ role: "user", content: user_query }]
    response = nil

    max_continuations.times do
      response = client.messages.create(
        model: "haijun-opus-5-5",
        max_tokens: 4096,
        messages: messages,
        tools: tools
      )

      # Haijun selesai memproses - kembalikan respons akhir
      return response unless response.stop_reason == :pause_turn

      # pause_turn: ganti seluruh daftar pesan agar peran tetap bergantian
      messages = [
        { role: "user", content: user_query },
        { role: "assistant", content: response.content }
      ]
    end

    # Batas maksimum kelanjutan tercapai - kembalikan respons terakhir
    response
  end

Alasan berhenti vs. error

Penting untuk membedakan antara nilai stop_reason dan error yang sebenarnya:

Alasan berhenti (respons berhasil)

  • Bagian dari body respons
  • Menunjukkan mengapa pembuatan berhenti secara normal
  • Respons berisi konten yang valid

Error (permintaan gagal)

  • Kode status HTTP 4xx atau 5xx
  • Menunjukkan kegagalan pemrosesan permintaan
  • Respons berisi detail error
bash
  # cURL keluar dengan kode non-nol pada error HTTP jika memakai --fail-with-body; periksa
  # $? untuk error dan stop_reason untuk respons yang berhasil.
  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 "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello!"}]
    }' | jq '.stop_reason'
bash
  # CLI keluar dengan kode non-nol saat terjadi error API; stop_reason muncul jika berhasil.
  ant messages create \
    --model haijun-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello!"}' \
    --format json | jq '.stop_reason'
python
  client = juglow.Juglow()

  try:
      response = client.messages.create(
          model="haijun-opus-5-5",
          max_tokens=1024,
          messages=[{"role": "user", "content": "Hello!"}],
      )

      # Tangani respons yang berhasil dengan stop_reason
      if response.stop_reason == "max_tokens":
          print("Response was truncated")

  except juglow.APIStatusError as e:
      # Tangani error yang sebenarnya
      match e.status_code:
          case 429:
              print("Rate limit exceeded")
          case 500:
              print("Server error")
typescript
  const client = new Juglow();

  try {
    const response = await client.messages.create({
      model: "haijun-opus-5-5",
      max_tokens: 1024,
      messages: [{ role: "user", content: "Hello!" }]
    });

    // Tangani respons yang berhasil dengan stop_reason
    if (response.stop_reason === "max_tokens") {
      console.log("Response was truncated");
    }
  } catch (err) {
    // Tangani error yang sebenarnya
    if (err instanceof Juglow.APIError) {
      switch (err.status) {
        case 429:
          console.log("Rate limit exceeded");
          break;
        case 500:
          console.log("Server error");
          break;
      }
    } else {
      throw err;
    }
  }
csharp
  JuglowClient client = new();

  try
  {
      var response = await client.Messages.Create(new MessageCreateParams
      {
          Model = Model.HaijunOpus5_5,
          MaxTokens = 1024,
          Messages = [new() { Role = Role.User, Content = "Hello!" }]
      });

      // Tangani respons yang berhasil dengan stop_reason
      if (response.StopReason == "max_tokens")
      {
          Console.WriteLine("Response was truncated");
      }
  }
  catch (JuglowRateLimitException)
  {
      // Tangani error yang sebenarnya
      Console.WriteLine("Rate limit exceeded");
  }
  catch (Juglow5xxException)
  {
      Console.WriteLine("Server error");
  }
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Hello!")),
  	},
  })
  if err != nil {
  	// Tangani error yang sebenarnya
  	var apiErr *juglow.Error
  	if errors.As(err, &apiErr) {
  		switch apiErr.StatusCode {
  		case 429:
  			fmt.Println("Rate limit exceeded")
  		case 500:
  			fmt.Println("Server error")
  		}
  	}
  	log.Fatal(err)
  }

  // Tangani respons sukses dengan stop_reason
  if response.StopReason == "max_tokens" {
  	fmt.Println("Response was truncated")
  }
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  try {
      Message response = client.messages().create(
          MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(1024L)
              .addUserMessage("Hello!")
              .build()
      );

      // Tangani respons yang berhasil dengan stop_reason
      if (response.stopReason().map(StopReason.MAX_TOKENS::equals).orElse(false)) {
          IO.println("Response was truncated");
      }
  } catch (RateLimitException e) {
      // Tangani error yang sebenarnya
      IO.println("Rate limit exceeded");
  } catch (JuglowServiceException e) {
      if (e.statusCode() == 500) {
          IO.println("Server error");
      }
  }
php
  $client = new Client();

  try {
      $response = $client->messages->create(
          maxTokens: 1024,
          messages: [['role' => 'user', 'content' => 'Hello!']],
          model: 'haijun-opus-5-5',
      );

      // Tangani respons yang berhasil dengan stop_reason
      if ($response->stopReason === 'max_tokens') {
          echo 'Response was truncated', PHP_EOL;
      }
  } catch (RateLimitException $e) {
      // Tangani error yang sebenarnya
      echo 'Rate limit exceeded', PHP_EOL;
  } catch (InternalServerException $e) {
      echo 'Server error', PHP_EOL;
  }
ruby
  client = Juglow::Client.new

  begin
    response = client.messages.create(
      model: "haijun-opus-5-5",
      max_tokens: 1024,
      messages: [{ role: "user", content: "Hello!" }]
    )

    # Tangani respons yang berhasil dengan stop_reason
    if response.stop_reason == :max_tokens
      puts "Response was truncated"
    end
  rescue Juglow::Errors::RateLimitError
    # Tangani error yang sebenarnya
    puts "Rate limit exceeded"
  rescue Juglow::Errors::APIStatusError => e
    puts "Server error" if e.status == 500
  end

Pertimbangan streaming

Saat menggunakan streaming, stop_reason:

  • Bernilai null dalam event message_start awal
  • Disediakan dalam event message_delta
  • Tidak disediakan dalam event lainnya
bash
  # Event message_delta dalam stream SSE membawa stop_reason.
  curl --no-buffer https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 1024,
      "stream": true,
      "messages": [{"role": "user", "content": "Hello!"}]
    }'
bash
  # stop_reason muncul di event message_delta.
  ant messages create --stream --format jsonl \
    --model haijun-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello!"}' |
    jq -c 'select(.type == "message_delta") | .delta.stop_reason'
python
  client = juglow.Juglow()

  with client.messages.stream(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello!"}],
  ) as stream:
      for event in stream:
          if event.type == "message_delta":
              stop_reason = event.delta.stop_reason
              if stop_reason:
                  print(f"Stream ended with: {stop_reason}")
typescript
  const client = new Juglow();

  const stream = client.messages.stream({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }]
  });

  for await (const event of stream) {
    if (event.type === "message_delta" && event.delta.stop_reason) {
      console.log(`Stream ended with: ${event.delta.stop_reason}`);
    }
  }
csharp
  JuglowClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello!" }]
  };

  await foreach (var streamEvent in client.Messages.CreateStreaming(parameters))
  {
      switch (streamEvent.Value)
      {
          case RawMessageDeltaEvent deltaEvent when deltaEvent.Delta.StopReason is not null:
              Console.WriteLine($"Stream ended with: {deltaEvent.Delta.StopReason}");
              break;
      }
  }
go
  client := juglow.NewClient()

  stream := client.Messages.NewStreaming(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Hello!")),
  	},
  })

  // Akumulasikan event ke dalam Message akhir, yang membawa stop_reason.
  message := juglow.Message{}
  for stream.Next() {
  	if err := message.Accumulate(stream.Current()); err != nil {
  		log.Fatal(err)
  	}
  }
  if err := stream.Err(); err != nil {
  	log.Fatal(err)
  }

  if message.StopReason != "" {
  	fmt.Printf("Stream ended with: %s\n", message.StopReason)
  }
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.HAIJUN_OPUS_5_5)
      .maxTokens(1024L)
      .addUserMessage("Hello!")
      .build();

  // Kumpulkan event ke dalam Message akhir, yang memuat stop_reason.
  MessageAccumulator accumulator = MessageAccumulator.create();
  try (StreamResponse<RawMessageStreamEvent> streamResponse =
          client.messages().createStreaming(params)) {
      streamResponse.stream().forEach(accumulator::accumulate);
  }

  accumulator.message().stopReason().ifPresent(stopReason ->
      IO.println("Stream ended with: " + stopReason)
  );
php
  $client = new Client();

  $stream = $client->messages->createStream(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello!']],
      model: 'haijun-opus-5-5',
  );

  foreach ($stream as $event) {
      if ($event instanceof \Juglow\Messages\RawMessageDeltaEvent && $event->delta->stopReason !== null) {
          echo "Stream ended with: {$event->delta->stopReason}", PHP_EOL;
      }
  }
ruby
  client = Juglow::Client.new

  stream = client.messages.stream(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }]
  )

  stream.each do |event|
    next unless event.type == :message_delta
    stop_reason = event.delta.stop_reason
    puts "Stream ended with: #{stop_reason}" if stop_reason
  end

Pola umum

Menangani alur kerja penggunaan alat

Tip: Lebih sederhana dengan tool runner: Contoh berikut menunjukkan penanganan alat secara manual. Untuk sebagian besar kasus penggunaan, tool runner secara otomatis menangani eksekusi alat dengan kode yang jauh lebih sedikit.

python
  def complete_tool_workflow(client, user_query, tools):
      messages = [{"role": "user", "content": user_query}]

      while True:
          response = client.messages.create(
              model="haijun-opus-5-5",
              max_tokens=1024,
              messages=messages,
              tools=tools,
          )

          if response.stop_reason == "tool_use":
              # Jalankan alat dan lanjutkan
              tool_results = execute_tools(response.content)
              messages.append({"role": "assistant", "content": response.content})
              messages.append({"role": "user", "content": tool_results})
          else:
              # Respons akhir
              return response
typescript
  async function completeToolWorkflow(
    client: Juglow,
    userQuery: string,
    tools: Juglow.ToolUnion[]
  ): Promise<Juglow.Message> {
    const messages: Juglow.MessageParam[] = [{ role: "user", content: userQuery }];

    while (true) {
      const response = await client.messages.create({
        model: "haijun-opus-5-5",
        max_tokens: 1024,
        messages,
        tools
      });

      if (response.stop_reason === "tool_use") {
        // Jalankan alat dan lanjutkan
        const toolResults = executeTools(response.content);
        messages.push({ role: "assistant", content: response.content });
        messages.push({ role: "user", content: toolResults });
      } else {
        // Respons akhir
        return response;
      }
    }
  }
csharp
  static async Task<Message> CompleteToolWorkflow(
      JuglowClient client,
      string userQuery,
      List<ToolUnion> tools)
  {
      List<MessageParam> messages = [new() { Role = Role.User, Content = userQuery }];

      while (true)
      {
          var response = await client.Messages.Create(new MessageCreateParams
          {
              Model = Model.HaijunOpus5_5,
              MaxTokens = 1024,
              Messages = messages,
              Tools = tools
          });

          if (response.StopReason == "tool_use")
          {
              // Jalankan alat dan lanjutkan
              var toolResults = ExecuteTools(response.Content);
              messages.Add(new()
              {
                  Role = Role.Assistant,
                  Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
              });
              messages.Add(new() { Role = Role.User, Content = toolResults });
          }
          else
          {
              // Respons akhir
              return response;
          }
      }
  }
go
  func completeToolWorkflow(
  	client juglow.Client,
  	userQuery string,
  	tools []juglow.ToolUnionParam,
  ) (*juglow.Message, error) {
  	messages := []juglow.MessageParam{juglow.NewUserMessage(juglow.NewTextBlock(userQuery))}

  	for {
  		response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  			Model:     juglow.ModelHaijunOpus5_5,
  			MaxTokens: 1024,
  			Messages:  messages,
  			Tools:     tools,
  		})
  		if err != nil {
  			return nil, err
  		}

  		if response.StopReason != "tool_use" {
  			// Respons akhir
  			return response, nil
  		}

  		// Jalankan alat dan lanjutkan
  		toolResults := executeTools(response.Content)
  		var contentParams []juglow.ContentBlockParamUnion
  		for _, block := range response.Content {
  			contentParams = append(contentParams, block.ToParam())
  		}
  		messages = append(messages, juglow.NewAssistantMessage(contentParams...))
  		messages = append(messages, juglow.NewUserMessage(toolResults...))
  	}
  }
java
  static Message completeToolWorkflow(
      JuglowClient client,
      String userQuery,
      List<Tool> tools
  ) {
      List<MessageParam> messages = new ArrayList<>();
      messages.add(MessageParam.builder().role(MessageParam.Role.USER).content(userQuery).build());

      while (true) {
          MessageCreateParams.Builder params = MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(1024L)
              .messages(messages);
          tools.forEach(params::addTool);

          Message response = client.messages().create(params.build());

          if (!response.stopReason().map(StopReason.TOOL_USE::equals).orElse(false)) {
              // Respons akhir
              return response;
          }

          // Jalankan alat dan lanjutkan
          List<ToolResultBlockParam> toolResults = executeTools(response.content());
          messages.add(response.toParam());
          messages.add(MessageParam.builder()
              .role(MessageParam.Role.USER)
              .contentOfBlockParams(toolResults.stream().map(ContentBlockParam::ofToolResult).toList())
              .build());
      }
  }
php
  function complete_tool_workflow(Client $client, string $userQuery, array $tools)
  {
      $messages = [['role' => 'user', 'content' => $userQuery]];

      while (true) {
          $response = $client->messages->create(
              maxTokens: 1024,
              messages: $messages,
              model: 'haijun-opus-5-5',
              tools: $tools,
          );

          if ($response->stopReason !== 'tool_use') {
              // Respons akhir
              return $response;
          }

          // Jalankan alat dan lanjutkan
          $toolResults = execute_tools($response->content);
          $messages[] = ['role' => 'assistant', 'content' => $response->content];
          $messages[] = ['role' => 'user', 'content' => $toolResults];
      }
  }
ruby
  def complete_tool_workflow(client, user_query, tools)
    messages = [{ role: "user", content: user_query }]

    loop do
      response = client.messages.create(
        model: "haijun-opus-5-5",
        max_tokens: 1024,
        messages: messages,
        tools: tools
      )

      # Respons akhir
      return response unless response.stop_reason == :tool_use

      # Jalankan alat dan lanjutkan
      tool_results = execute_tools(response.content)
      messages << { role: "assistant", content: response.content }
      messages << { role: "user", content: tool_results }
    end
  end

Memastikan respons lengkap

python
  def get_complete_response(client, prompt, max_attempts=3):
      messages = [{"role": "user", "content": prompt}]
      full_response = ""

      for _ in range(max_attempts):
          response = client.messages.create(
              model="haijun-opus-5-5", messages=messages, max_tokens=4096
          )

          full_response += next(
              (block.text for block in response.content if block.type == "text"), ""
          )

          if response.stop_reason != "max_tokens":
              break

          # Lanjutkan dari titik terakhir berhenti
          messages = [
              {"role": "user", "content": prompt},
              {"role": "assistant", "content": full_response},
              {"role": "user", "content": "Please continue from where you left off."},
          ]

      return full_response
typescript
  async function getCompleteResponse(
    client: Juglow,
    prompt: string,
    maxAttempts = 3
  ): Promise<string> {
    let messages: Juglow.MessageParam[] = [{ role: "user", content: prompt }];
    let fullResponse = "";

    for (let i = 0; i < maxAttempts; i++) {
      const response = await client.messages.create({
        model: "haijun-opus-5-5",
        max_tokens: 4096,
        messages
      });

      const textBlock = response.content.find(
        (block): block is Juglow.TextBlock => block.type === "text"
      );
      fullResponse += textBlock?.text ?? "";

      if (response.stop_reason !== "max_tokens") {
        break;
      }

      // Lanjutkan dari titik terakhir yang ditinggalkan
      messages = [
        { role: "user", content: prompt },
        { role: "assistant", content: fullResponse },
        { role: "user", content: "Please continue from where you left off." }
      ];
    }

    return fullResponse;
  }
csharp
  static async Task<string> GetCompleteResponse(JuglowClient client, string prompt, int maxAttempts = 3)
  {
      List<MessageParam> messages = [new() { Role = Role.User, Content = prompt }];
      var fullResponse = "";

      for (var i = 0; i < maxAttempts; i++)
      {
          var response = await client.Messages.Create(new MessageCreateParams
          {
              Model = Model.HaijunOpus5_5,
              MaxTokens = 4096,
              Messages = messages
          });

          foreach (var block in response.Content)
          {
              if (block.TryPickText(out var textBlock))
              {
                  fullResponse += textBlock.Text;
                  break;
              }
          }

          if (response.StopReason != "max_tokens")
          {
              break;
          }

          // Lanjutkan dari titik terakhir yang ditinggalkan
          messages =
          [
              new() { Role = Role.User, Content = prompt },
              new() { Role = Role.Assistant, Content = fullResponse },
              new() { Role = Role.User, Content = "Please continue from where you left off." }
          ];
      }

      return fullResponse;
  }
go
  func getCompleteResponse(client juglow.Client, prompt string, maxAttempts int) (string, error) {
  	messages := []juglow.MessageParam{juglow.NewUserMessage(juglow.NewTextBlock(prompt))}
  	fullResponse := ""

  	for range maxAttempts {
  		response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  			Model:     juglow.ModelHaijunOpus5_5,
  			MaxTokens: 4096,
  			Messages:  messages,
  		})
  		if err != nil {
  			return "", err
  		}

  		for _, block := range response.Content {
  			if textBlock, ok := block.AsAny().(juglow.TextBlock); ok {
  				fullResponse += textBlock.Text
  				break
  			}
  		}

  		if response.StopReason != "max_tokens" {
  			break
  		}

  		// Lanjutkan dari titik terakhir yang ditinggalkan
  		messages = []juglow.MessageParam{
  			juglow.NewUserMessage(juglow.NewTextBlock(prompt)),
  			juglow.NewAssistantMessage(juglow.NewTextBlock(fullResponse)),
  			juglow.NewUserMessage(juglow.NewTextBlock("Please continue from where you left off.")),
  		}
  	}

  	return fullResponse, nil
  }
java
  static String getCompleteResponse(JuglowClient client, String prompt, int maxAttempts) {
      List<MessageParam> messages = List.of(
          MessageParam.builder().role(MessageParam.Role.USER).content(prompt).build()
      );
      StringBuilder fullResponse = new StringBuilder();

      for (int i = 0; i < maxAttempts; i++) {
          Message response = client.messages().create(
              MessageCreateParams.builder()
                  .model(Model.HAIJUN_OPUS_5_5)
                  .maxTokens(4096L)
                  .messages(messages)
                  .build()
          );

          response.content().stream()
              .filter(ContentBlock::isText)
              .findFirst()
              .ifPresent(block -> fullResponse.append(block.asText().text()));

          if (!response.stopReason().map(StopReason.MAX_TOKENS::equals).orElse(false)) {
              break;
          }

          // Lanjutkan dari titik terakhir yang ditinggalkan
          messages = List.of(
              MessageParam.builder().role(MessageParam.Role.USER).content(prompt).build(),
              MessageParam.builder().role(MessageParam.Role.ASSISTANT).content(fullResponse.toString()).build(),
              MessageParam.builder().role(MessageParam.Role.USER).content("Please continue from where you left off.").build()
          );
      }

      return fullResponse.toString();
  }
php
  function get_complete_response(Client $client, string $prompt, int $maxAttempts = 3): string
  {
      $messages = [['role' => 'user', 'content' => $prompt]];
      $fullResponse = '';

      for ($i = 0; $i < $maxAttempts; $i++) {
          $response = $client->messages->create(
              maxTokens: 4096,
              messages: $messages,
              model: 'haijun-opus-5-5',
          );

          $fullResponse .= array_find($response->content, static fn ($block): bool => $block->type === 'text')?->text ?? '';

          if ($response->stopReason !== 'max_tokens') {
              break;
          }

          // Lanjutkan dari titik terakhir yang ditinggalkan
          $messages = [
              ['role' => 'user', 'content' => $prompt],
              ['role' => 'assistant', 'content' => $fullResponse],
              ['role' => 'user', 'content' => 'Please continue from where you left off.'],
          ];
      }

      return $fullResponse;
  }
ruby
  def get_complete_response(client, prompt, max_attempts: 3)
    messages = [{ role: "user", content: prompt }]
    full_response = +""

    max_attempts.times do
      response = client.messages.create(
        model: "haijun-opus-5-5",
        max_tokens: 4096,
        messages: messages
      )

      full_response << response.content.find { it.type == :text }&.text.to_s

      break unless response.stop_reason == :max_tokens

      # Lanjutkan dari titik terakhir berhenti
      messages = [
        { role: "user", content: prompt },
        { role: "assistant", content: full_response },
        { role: "user", content: "Please continue from where you left off." }
      ]
    end

    full_response
  end

Mendapatkan token maksimum tanpa mengetahui ukuran input

Dengan alasan berhenti model_context_window_exceeded, Anda dapat meminta token maksimum yang mungkin tanpa menghitung ukuran input:

python
  def get_max_possible_tokens(client, prompt):
      """
      Get as many tokens as possible within the model's context window
      without needing to calculate input token count
      """
      response = client.beta.messages.create(
          model="haijun-opus-5-5",
          messages=[{"role": "user", "content": prompt}],
          max_tokens=20000,  # Python SDK requires streaming for max_tokens above ~21k
      )

      match response.stop_reason:
          case "model_context_window_exceeded":
              # Mendapat token maksimum yang dimungkinkan sesuai ukuran input
              print(
                  f"Generated {response.usage.output_tokens} tokens (context limit reached)"
              )
          case "max_tokens":
              # Mendapat token tepat sebanyak yang diminta
              print(
                  f"Generated {response.usage.output_tokens} tokens (max_tokens reached)"
              )
          case _:
              # Penyelesaian alami
              print(
                  f"Generated {response.usage.output_tokens} tokens (natural completion)"
              )

      return next((block.text for block in response.content if block.type == "text"), "")
typescript
  async function getMaxPossibleTokens(client: Juglow, prompt: string): Promise<string> {
    const response = await client.beta.messages.create({
      model: "haijun-opus-5-5",
      max_tokens: 20000,
      messages: [{ role: "user", content: prompt }]
    });

    const tokens = response.usage.output_tokens;
    switch (response.stop_reason) {
      case "model_context_window_exceeded":
        // Mendapatkan token maksimum yang dimungkinkan berdasarkan ukuran input
        console.log(`Generated ${tokens} tokens (context limit reached)`);
        break;
      case "max_tokens":
        // Mendapatkan token tepat sebanyak yang diminta
        console.log(`Generated ${tokens} tokens (max_tokens reached)`);
        break;
      default:
        // Penyelesaian alami
        console.log(`Generated ${tokens} tokens (natural completion)`);
    }

    const textBlock = response.content.find(
      (block): block is Juglow.Beta.BetaTextBlock => block.type === "text"
    );
    return textBlock?.text ?? "";
  }
csharp
  using Juglow.Models.Beta.Messages;
  using Model = Juglow.Models.Messages.Model;

  static async Task<string> GetMaxPossibleTokens(JuglowClient client, string prompt)
  {
      var response = await client.Beta.Messages.Create(new MessageCreateParams
      {
          Model = Model.HaijunOpus5_5,
          MaxTokens = 20000,
          Messages = [new() { Role = Role.User, Content = prompt }]
      });

      var tokens = response.Usage.OutputTokens;
      var reason = response.StopReason?.Value();
      if (reason == BetaStopReason.ModelContextWindowExceeded)
      {
          // Mendapatkan jumlah token maksimum yang dimungkinkan berdasarkan ukuran input
          Console.WriteLine($"Generated {tokens} tokens (context limit reached)");
      }
      else if (reason == BetaStopReason.MaxTokens)
      {
          // Mendapatkan jumlah token persis seperti yang diminta
          Console.WriteLine($"Generated {tokens} tokens (max_tokens reached)");
      }
      else
      {
          // Penyelesaian alami
          Console.WriteLine($"Generated {tokens} tokens (natural completion)");
      }

      return response.Content.Select(b => b.Value).OfType<BetaTextBlock>().FirstOrDefault()?.Text ?? "";
  }
go
  func getMaxPossibleTokens(client juglow.Client, prompt string) (string, error) {
  	response, err := client.Beta.Messages.New(context.TODO(), juglow.BetaMessageNewParams{
  		Model:     juglow.ModelHaijunOpus5_5,
  		MaxTokens: 20000,
  		Messages: []juglow.BetaMessageParam{
  			juglow.NewBetaUserMessage(juglow.NewBetaTextBlock(prompt)),
  		},
  	})
  	if err != nil {
  		return "", err
  	}

  	tokens := response.Usage.OutputTokens
  	switch response.StopReason {
  	case juglow.BetaStopReasonModelContextWindowExceeded:
  		// Mendapat token maksimum yang dimungkinkan berdasarkan ukuran input
  		fmt.Printf("Generated %d tokens (context limit reached)\n", tokens)
  	case juglow.BetaStopReasonMaxTokens:
  		// Mendapat token tepat sebanyak yang diminta
  		fmt.Printf("Generated %d tokens (max_tokens reached)\n", tokens)
  	default:
  		// Penyelesaian alami
  		fmt.Printf("Generated %d tokens (natural completion)\n", tokens)
  	}

  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(juglow.BetaTextBlock); ok {
  			return textBlock.Text, nil
  		}
  	}
  	return "", nil
  }
java
  import com.juglow.models.beta.messages.BetaContentBlock;
  import com.juglow.models.beta.messages.BetaMessage;
  import com.juglow.models.beta.messages.BetaStopReason;
  import com.juglow.models.beta.messages.MessageCreateParams;

  static String getMaxPossibleTokens(JuglowClient client, String prompt) {
      BetaMessage response = client.beta().messages().create(
          MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(20000L)
              .addUserMessage(prompt)
              .build()
      );

      long tokens = response.usage().outputTokens();
      BetaStopReason reason = response.stopReason().orElse(BetaStopReason.END_TURN);
      if (reason.equals(BetaStopReason.MODEL_CONTEXT_WINDOW_EXCEEDED)) {
          // Mendapatkan token maksimum yang dimungkinkan berdasarkan ukuran input
          IO.println("Generated " + tokens + " tokens (context limit reached)");
      } else if (reason.equals(BetaStopReason.MAX_TOKENS)) {
          // Mendapatkan token tepat sebanyak yang diminta
          IO.println("Generated " + tokens + " tokens (max_tokens reached)");
      } else {
          // Penyelesaian alami
          IO.println("Generated " + tokens + " tokens (natural completion)");
      }

      return response.content().stream()
          .filter(BetaContentBlock::isText)
          .findFirst()
          .map(block -> block.asText().text())
          .orElse("");
  }
php
  function get_max_possible_tokens(Client $client, string $prompt): string
  {
      $response = $client->beta->messages->create(
          maxTokens: 20000,
          messages: [['role' => 'user', 'content' => $prompt]],
          model: 'haijun-opus-5-5',
      );

      $tokens = $response->usage->outputTokens;
      echo match ($response->stopReason) {
          // Mendapat token maksimum yang dimungkinkan sesuai ukuran input
          'model_context_window_exceeded' => "Generated {$tokens} tokens (context limit reached)",
          // Mendapat token tepat sesuai jumlah yang diminta
          'max_tokens' => "Generated {$tokens} tokens (max_tokens reached)",
          // Penyelesaian alami
          default => "Generated {$tokens} tokens (natural completion)",
      }, PHP_EOL;

      return array_find($response->content, static fn ($block): bool => $block->type === 'text')?->text ?? '';
  }
ruby
  def get_max_possible_tokens(client, prompt)
    response = client.beta.messages.create(
      model: "haijun-opus-5-5",
      max_tokens: 20000,
      messages: [{ role: "user", content: prompt }]
    )

    tokens = response.usage.output_tokens
    case response.stop_reason
    when :model_context_window_exceeded
      # Mendapat token maksimum yang dimungkinkan sesuai ukuran input
      puts "Generated #{tokens} tokens (context limit reached)"
    when :max_tokens
      # Mendapat token tepat sebanyak yang diminta
      puts "Generated #{tokens} tokens (max_tokens reached)"
    else
      # Penyelesaian alami
      puts "Generated #{tokens} tokens (natural completion)"
    end

    response.content.find { it.type == :text }.text
  end

Langkah selanjutnya

Coba ulang permintaan yang ditolak pada model fallback, di sisi server atau di klien Anda.

Biarkan SDK mengelola loop tool_use, pemformatan hasil, dan percobaan ulang untuk Anda.

Baca stop_reason dari event message_delta saat streaming.

Tangani error HTTP 4xx dan 5xx, yang berbeda dari alasan berhenti.

On this page
Referensi cepatField stop\_reasonNilai stop reasonend\_turnRespons kosong dengan end_turnmax\_tokensBlok penggunaan alat yang tidak lengkapstop\_sequencetool\_usepause\_turnrefusalmodel\_context\_window\_exceededPraktik terbaik untuk menangani alasan berhentiSelalu periksa stop\_reasonTangani respons terpotong dengan baikImplementasikan logika percobaan ulang untuk pause\_turnAlasan berhenti vs. errorAlasan berhenti (respons berhasil)Error (permintaan gagal)Pertimbangan streamingPola umumMenangani alur kerja penggunaan alatMemastikan respons lengkapMendapatkan token maksimum tanpa mengetahui ukuran inputLangkah selanjutnya