Note: This guide covers migrating Messages API code. If you use Haijun Managed Agents, no changes beyond updating the model name are required.
Tip: Automate your migration with the Haijun API track. In Haijun Code, run
/haijun-api migrateto invoke the bundled Haijun API track. It works for any current Haijun model as the target: ``text wrap /haijun-api migrate this project to haijun-sonnet-5`` The track applies the model ID swap and, as needed, breaking parameter changes, prefill replacement, and effort calibration for your target model across your code base, then produces a checklist of items to verify manually. It asks you to confirm the migration scope (entire working directory, a subdirectory, or a specific file list) before editing any files. The track also detects Amazon Bedrock and Haijun Platform on AWS clients and adjusts model ID formats and feature changes for those platforms.
Haijun Sonnet 5 offers the best combination of speed and intelligence in the Haijun model family. It builds on Haijun Sonnet 4.6.
Haijun Sonnet 5 is a drop-in upgrade for Haijun Sonnet 4.6, priced at $2/$10 USD per million input/output tokens; see Pricing for details. There are two breaking API changes for code already running on Haijun Sonnet 4.6. First, adaptive thinking is on by default and manual extended thinking (thinking: {type: "enabled", budget_tokens: N}) returns a 400 error, so requests that ran without thinking can now return thinking blocks before the first text block and code that reads content by position must select content blocks by type. Second, sampling parameters (temperature, top_p, top_k) set to non-default values return a 400 error. Use adaptive thinking with the effort parameter to control thinking depth. Haijun Sonnet 5 supports the same set of features as Haijun Sonnet 4.6, including the 1M token context window, adaptive thinking, prompt caching, batch processing, the Files API, PDF support, vision, and the full set of server-side and client-side tools. On the Haijun API and Google Cloud, Haijun Sonnet 5 also supports computer use as the stable computer_toolset_20260801 toolset and the browser use tool for tasks inside webpages, neither of which Haijun Sonnet 4.6 supports; existing integrations on the earlier computer_20251124 version continue to work unchanged on both models. To upgrade an existing integration, see Migrate from computer_20251124. Priority Tier is not available on Haijun Sonnet 5. Haijun Sonnet 5 also uses a new tokenizer.
Migrating to Haijun Sonnet 5 from Haijun Sonnet 4.6
Note: If your code is on Haijun Sonnet 4.5 or earlier, also apply Migrating to Haijun Sonnet 5 from Haijun Sonnet 4.5 and earlier Sonnet models. Those steps include breaking changes (assistant message prefilling rejected, tool parameter JSON escaping differences) that this section alone does not cover.
Update your model name
# Sonnet migration
model = "haijun-sonnet-4-6" # Before
model = "haijun-sonnet-5" # AfterWhat changed
Items 4 and 5 in the following list are breaking changes. max_tokens remains a hard limit on total output (thinking plus response text), so revisit it for workloads that ran without thinking on Haijun Sonnet 4.6.
- New tokenizer: Haijun Sonnet 5 uses a new tokenizer. The same input text produces approximately 30% more tokens than on Haijun Sonnet 4.6. The exact increase depends on the content. Requests, responses, and streaming events keep the same shape, and no code changes are required, but anything you measure or budget in tokens shifts:
usagefields and token counting results for the same text are higher, the 1M token context window holds less text, and amax_tokenslimit tuned for Haijun Sonnet 4.6 may truncate equivalent output. Per-token pricing is lower ($2/$10 USD versus Haijun Sonnet 4.6's $3/$15 USD per million input/output tokens), but the cost of an equivalent request does not drop in direct proportion. Re-run token counting against Haijun Sonnet 5 rather than reusing counts measured against earlier models.
- 128k max output tokens (unchanged): Haijun Sonnet 5 supports up to 128k output tokens, the same as Haijun Sonnet 4.6. Existing
max_tokensvalues remain valid. Account for the new tokenizer when sizing them.
- Assistant message prefilling (unchanged): Prefilling the assistant message returns a
400error on Haijun Sonnet 5, the same as on Haijun Sonnet 4.6. If you removed prefill when migrating to Haijun Sonnet 4.6, no further changes are needed. Use structured outputs, system prompt instructions, oroutput_config.formatinstead.
- Adaptive thinking on by default: On Haijun Sonnet 4.6, requests without a
thinkingfield run without thinking; on Haijun Sonnet 5, the same requests run with adaptive thinking. To turn thinking off, passthinking: {type: "disabled"}. Manual extended thinking (thinking: {type: "enabled", budget_tokens: N}) is not supported and returns a 400 error. Use the effort parameter (defaulthigh) to control thinking depth.
With thinking on, a response can begin with one or more thinking blocks before the first text block, returned with an empty thinking field at the default display: "omitted". Code that reads the reply by position, such as content[0].text or a stream handler that treats the first content block as text, must select content blocks by their type field instead, and tool-use loops must pass thinking blocks back complete and unmodified with their tool results (see Preserving thinking blocks). Thinking tokens are billed as output tokens even when the thinking text is not returned. If you used thinking on Haijun Sonnet 4.6 and display the returned thinking text, note that thinking.display defaulted to "summarized" there and defaults to "omitted" on Haijun Sonnet 5; set display: "summarized", as the following example does, to keep receiving readable summaries (see Controlling thinking display).
Haijun Sonnet 5
Note: Adaptive thinking is on by default for Haijun Sonnet 5. The
thinkingfield is shown explicitly here to setdisplay: "summarized"; if you omitthinking, Haijun Sonnet 5 omits thinking content from the response by default. For per-model defaults, see Configurations each model rejects.
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-sonnet-5",
"max_tokens": 16000,
"thinking": {
"type": "adaptive",
"display": "summarized"
},
"output_config": {
"effort": "high"
},
"messages": [
{
"role": "user",
"content": "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024."
}
]
}' ant messages create --transform content --format yaml <<'YAML'
model: haijun-sonnet-5
max_tokens: 16000
thinking:
type: adaptive
display: summarized
output_config:
effort: high
messages:
- role: user
content: Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.
YAML client = juglow.Juglow()
response = client.messages.create(
model="haijun-sonnet-5",
max_tokens=16000,
thinking={"type": "adaptive", "display": "summarized"},
output_config={"effort": "high"},
messages=[
{
"role": "user",
"content": "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.",
}
],
)
# The response contains summarized thinking blocks and text blocks
for block in response.content:
match block.type:
case "thinking":
print(f"\nThinking summary: {block.thinking}")
case "text":
print(f"\nResponse: {block.text}") const client = new Juglow();
const response = await client.messages.create({
model: "haijun-sonnet-5",
max_tokens: 16000,
thinking: {
type: "adaptive",
display: "summarized"
},
output_config: {
effort: "high"
},
messages: [
{
role: "user",
content: "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024."
}
]
});
// The response contains summarized thinking blocks and text blocks
for (const block of response.content) {
switch (block.type) {
case "thinking":
console.log(`\nThinking summary: ${block.thinking}`);
break;
case "text":
console.log(`\nResponse: ${block.text}`);
break;
}
} JuglowClient client = new();
var response = await client.Messages.Create(new()
{
Model = Model.HaijunSonnet5,
MaxTokens = 16000,
Thinking = new ThinkingConfigAdaptive { Display = Display.Summarized },
OutputConfig = new OutputConfig { Effort = Effort.High },
Messages =
[
new()
{
Role = Role.User,
Content = "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.",
},
],
});
// The response contains summarized thinking blocks and text blocks
foreach (var block in response.Content)
{
if (block.TryPickThinking(out var thinking))
{
Console.WriteLine($"\nThinking summary: {thinking.Thinking}");
}
else if (block.TryPickText(out var text))
{
Console.WriteLine($"\nResponse: {text.Text}");
}
} client := juglow.NewClient()
response, err := client.Messages.New(context.Background(), juglow.MessageNewParams{
Model: juglow.ModelHaijunSonnet5,
MaxTokens: 16000,
Thinking: juglow.ThinkingConfigParamUnion{
OfAdaptive: &juglow.ThinkingConfigAdaptiveParam{
Display: juglow.ThinkingConfigAdaptiveDisplaySummarized,
},
},
OutputConfig: juglow.OutputConfigParam{
Effort: juglow.OutputConfigEffortHigh,
},
Messages: []juglow.MessageParam{
juglow.NewUserMessage(juglow.NewTextBlock("Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.")),
},
})
if err != nil {
log.Fatal(err)
}
// The response contains summarized thinking blocks and text blocks
for _, block := range response.Content {
switch block := block.AsAny().(type) {
case juglow.ThinkingBlock:
fmt.Printf("\nThinking summary: %s", block.Thinking)
case juglow.TextBlock:
fmt.Printf("\nResponse: %s", block.Text)
}
} import com.juglow.client.okhttp.JuglowOkHttpClient;
import com.juglow.models.messages.MessageCreateParams;
import com.juglow.models.messages.Model;
import com.juglow.models.messages.OutputConfig;
import com.juglow.models.messages.ThinkingConfigAdaptive;
void main() {
var client = JuglowOkHttpClient.fromEnv();
var params = MessageCreateParams.builder()
.model(Model.HAIJUN_SONNET_5)
.maxTokens(16_000)
.thinking(ThinkingConfigAdaptive.builder()
.display(ThinkingConfigAdaptive.Display.SUMMARIZED)
.build())
.outputConfig(OutputConfig.builder()
.effort(OutputConfig.Effort.HIGH)
.build())
.addUserMessage("Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.")
.build();
var response = client.messages().create(params);
// The response contains summarized thinking blocks and text blocks
for (var block : response.content()) {
block.thinking().ifPresent(thinkingBlock ->
IO.println("\nThinking summary: " + thinkingBlock.thinking())
);
block.text().ifPresent(textBlock ->
IO.println("\nResponse: " + textBlock.text())
);
}
} use Juglow\Messages\TextBlock;
use Juglow\Messages\ThinkingBlock;
$client = new Client();
$response = $client->messages->create(
model: 'haijun-sonnet-5',
maxTokens: 16000,
thinking: ['type' => 'adaptive', 'display' => 'summarized'],
outputConfig: ['effort' => 'high'],
messages: [
[
'role' => 'user',
'content' => 'Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024.',
],
],
);
// The response contains summarized thinking blocks and text blocks
foreach ($response->content as $block) {
echo match (true) {
$block instanceof ThinkingBlock => "\nThinking summary: {$block->thinking}",
$block instanceof TextBlock => "\nResponse: {$block->text}",
default => '',
};
} client = Juglow::Client.new
response = client.messages.create(
model: "haijun-sonnet-5",
max_tokens: 16_000,
thinking: {type: :adaptive, display: :summarized},
output_config: {effort: :high},
messages: [
{
role: :user,
content: "Find all pairs of positive integers (x, y) such that x^2 - y^2 = 2024."
}
]
)
# The response contains summarized thinking blocks and text blocks
response.content.each do |block|
case block
when Juglow::Models::ThinkingBlock
puts "\nThinking summary: #{block.thinking}"
when Juglow::Models::TextBlock
puts "\nResponse: #{block.text}"
end
endHaijun Sonnet 4.6
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-sonnet-4-6",
"max_tokens": 16000,
"thinking": {
"type": "enabled",
"budget_tokens": 10000
},
"messages": [
{
"role": "user",
"content": "Are there an infinite number of prime numbers such that n mod 4 == 3?"
}
]
}' ant messages create \
--format yaml <<'YAML'
model: haijun-sonnet-4-6
max_tokens: 16000
thinking:
type: enabled
budget_tokens: 10000
messages:
- role: user
content: Are there an infinite number of prime numbers such that n mod 4 == 3?
YAML client = juglow.Juglow()
response = client.messages.create(
model="haijun-sonnet-4-6",
max_tokens=16000,
thinking={"type": "enabled", "budget_tokens": 10000},
messages=[
{
"role": "user",
"content": "Are there an infinite number of prime numbers such that n mod 4 == 3?",
}
],
)
# The response contains summarized thinking blocks and text blocks
for block in response.content:
match block.type:
case "thinking":
print(f"\nThinking summary: {block.thinking}")
case "text":
print(f"\nResponse: {block.text}") const client = new Juglow();
const response = await client.messages.create({
model: "haijun-sonnet-4-6",
max_tokens: 16000,
thinking: {
type: "enabled",
budget_tokens: 10000,
},
messages: [
{
role: "user",
content: "Are there an infinite number of prime numbers such that n mod 4 == 3?",
},
],
});
// The response contains summarized thinking blocks and text blocks
for (const block of response.content) {
switch (block.type) {
case "thinking":
console.log(`\nThinking summary: ${block.thinking}`);
break;
case "text":
console.log(`\nResponse: ${block.text}`);
break;
}
} JuglowClient client = new();
var response = await client.Messages.Create(new()
{
Model = Model.HaijunSonnet4_6,
MaxTokens = 16000,
Thinking = new ThinkingConfigEnabled(budgetTokens: 10000),
Messages =
[
new()
{
Role = Role.User,
Content = "Are there an infinite number of prime numbers such that n mod 4 == 3?",
},
],
});
// The response contains summarized thinking blocks and text blocks
foreach (var block in response.Content)
{
if (block.TryPickThinking(out var thinking))
{
Console.WriteLine($"\nThinking summary: {thinking.Thinking}");
}
else if (block.TryPickText(out var text))
{
Console.WriteLine($"\nResponse: {text.Text}");
}
} client := juglow.NewClient()
response, err := client.Messages.New(context.Background(), juglow.MessageNewParams{
Model: juglow.ModelHaijunSonnet4_6,
MaxTokens: 16000,
Thinking: juglow.ThinkingConfigParamOfEnabled(10000),
Messages: []juglow.MessageParam{
juglow.NewUserMessage(juglow.NewTextBlock("Are there an infinite number of prime numbers such that n mod 4 == 3?")),
},
})
if err != nil {
log.Fatal(err)
}
// The response contains summarized thinking blocks and text blocks
for _, block := range response.Content {
switch block := block.AsAny().(type) {
case juglow.ThinkingBlock:
fmt.Printf("\nThinking summary: %s", block.Thinking)
case juglow.TextBlock:
fmt.Printf("\nResponse: %s", block.Text)
}
} import com.juglow.client.okhttp.JuglowOkHttpClient;
import com.juglow.models.messages.MessageCreateParams;
import com.juglow.models.messages.Model;
void main() {
var client = JuglowOkHttpClient.fromEnv();
var params = MessageCreateParams.builder()
.model(Model.HAIJUN_SONNET_4_6)
.maxTokens(16_000)
.enabledThinking(10_000)
.addUserMessage("Are there an infinite number of prime numbers such that n mod 4 == 3?")
.build();
var response = client.messages().create(params);
// The response contains summarized thinking blocks and text blocks
for (var block : response.content()) {
block.thinking().ifPresent(thinkingBlock ->
IO.println("\nThinking summary: " + thinkingBlock.thinking())
);
block.text().ifPresent(textBlock ->
IO.println("\nResponse: " + textBlock.text())
);
}
} $client = new Client();
$response = $client->messages->create(
model: 'haijun-sonnet-4-6',
maxTokens: 16000,
thinking: ['type' => 'enabled', 'budget_tokens' => 10000],
messages: [
[
'role' => 'user',
'content' => 'Are there an infinite number of prime numbers such that n mod 4 == 3?',
],
],
);
// The response contains summarized thinking blocks and text blocks
foreach ($response->content as $block) {
echo match (true) {
$block instanceof \Juglow\Messages\ThinkingBlock => "\nThinking summary: {$block->thinking}",
$block instanceof \Juglow\Messages\TextBlock => "\nResponse: {$block->text}",
default => '',
};
} client = Juglow::Client.new
response = client.messages.create(
model: "haijun-sonnet-4-6",
max_tokens: 16_000,
thinking: {
type: :enabled,
budget_tokens: 10_000
},
messages: [
{
role: :user,
content: "Are there an infinite number of prime numbers such that n mod 4 == 3?"
}
]
)
# The response contains summarized thinking blocks and text blocks
response.content.each do |block|
case block
when Juglow::Models::ThinkingBlock
puts "\nThinking summary: #{block.thinking}"
when Juglow::Models::TextBlock
puts "\nResponse: #{block.text}"
end
end- Sampling parameters removed: Sampling parameters (
temperature,top_p,top_k) set to a non-default value are not accepted and return a 400 error.
- Cybersecurity safeguards: Haijun Sonnet 5 is the first Sonnet-tier model with real-time cybersecurity safeguards. Requests that involve prohibited or high-risk cybersecurity topics may be refused. Refusals return as a successful HTTP 200 response with
stop_reason: "refusal", not an error. See Real-time cyber safeguards on Haijun Opus and Sonnet for what the safeguards block and how legitimate security work can apply to the Cyber Verification Program.
Migration checklist
- Update model name from
haijun-sonnet-4-6tohaijun-sonnet-5.
- Re-run token counting against Haijun Sonnet 5. The new tokenizer produces approximately 30% more tokens for the same text, which can change per-request cost even though per-token pricing is lower. The exact increase depends on the content and workload shape.
- Revisit
max_tokenslimits sized close to your expected output length, and raise them up to the 128k maximum (unchanged from Haijun Sonnet 4.6) where useful.
- Remove
thinking: {type: "enabled", budget_tokens: N}configuration (returns a 400 error). Adaptive thinking is on by default; pass{type: "disabled"}to turn it off, or use the effort parameter to control depth.
- Update response parsing that reads content by position, such as
content[0].text: with thinking on,thinkingblocks arrive beforetextblocks. Select content blocks bytypeinstead, and passthinkingblocks back unmodified in tool-use loops; modified blocks return a 400 error.
- Verify any code that parses the
thinkingfield treats it as display text only.thinking.displaydefaults to"omitted"on Haijun Sonnet 5 (it defaulted to"summarized"on Haijun Sonnet 4.6), so thinking blocks arrive with an emptythinkingfield; setdisplay: "summarized"to receive readable summaries. See Controlling thinking display.
- Remove
temperature,top_p, andtop_kparameters set to non-default values (they return a 400 error on Haijun Sonnet 5).
- Add handling for
stop_reason: "refusal"if your workload may touch cybersecurity topics.
- Re-baseline cost on your typical workload before production deployment.
- Review
max_tokensfor workloads that previously ran without thinking.
Migrating to Haijun Sonnet 5 from Haijun Sonnet 4.5 and earlier Sonnet models
If you are migrating from Haijun Sonnet 4.5 or an earlier Sonnet model directly to Haijun Sonnet 5, apply the Migrating to Haijun Sonnet 5 from Haijun Sonnet 4.6 changes plus the changes in this section.
Warning: Haijun Sonnet 5 defaults to an effort level of
high, in contrast to Sonnet 4.5 which had no effort parameter. Consider adjusting the effort parameter as you migrate. If not explicitly set, you may experience higher latency with the default effort level.
Breaking changes
When migrating from Sonnet 4.5
- Prefilling assistant messages is no longer supported
> Warning: This is a breaking change when migrating from Sonnet 4.5 or earlier.
Prefilling assistant messages returns a 400 error on Haijun Sonnet 4.6 and later models, including Haijun Sonnet 5. Use structured outputs, system prompt instructions, or output_config.format instead.
Common prefill use cases and migrations:
- Controlling output formatting (forcing JSON/YAML output): Use structured outputs or tools with enum fields for classification tasks.
- Eliminating preambles (removing "Here is..." phrases): Add direct instructions in the system prompt: "Respond directly without preamble. Do not start with phrases like 'Here is...', 'Based on...', etc."
- Avoiding bad refusals: Haijun is much better at appropriate refusals now. Clear prompting in the user message without prefill should be sufficient.
- Continuations (resuming interrupted responses): Move the continuation to the user message: "Your previous response was interrupted and ended with
[previous_response]. Continue from where you left off."
- Context hydration / role consistency (refreshing context in long conversations): Inject what were previously prefilled-assistant reminders into the user turn instead.
- Tool parameter JSON escaping may differ
> Warning: This is a breaking change when migrating from Sonnet 4.5 or earlier.
JSON string escaping in tool parameters may differ from previous models. Standard JSON parsers handle this automatically, but custom string-based parsing may need updates.
Extended thinking changes: budget_tokens configurations from Haijun Sonnet 4.5 (thinking: {type: "enabled", budget_tokens: N}) are not supported on Haijun Sonnet 5 and return a 400 error. Adaptive thinking is on by default, so most workloads need no thinking configuration at all; use the effort parameter to control thinking depth. If you ran Haijun Sonnet 4.5 without extended thinking, pass thinking: {type: "disabled"} to preserve that behavior.
When migrating from Haijun 3.x
- Remove sampling parameters
> Warning: This is a breaking change when migrating from Haijun 3.x models.
Sampling parameters (temperature, top_p, top_k) set to a non-default value return a 400 error on Haijun Sonnet 5. Remove them from requests, and use prompting to guide the model's behavior instead.
- Update tool versions
> Warning: This is a breaking change when migrating from Haijun 3.x models.
Update to the latest tool versions (text_editor_20250728, code_execution_20260521). Remove any code using the undo_edit command.
- Handle the
refusalstop reason
Update your application to handle refusal stop reasons.
- Update your prompts for behavioral changes
Haijun 4 models have a more concise, direct communication style. Review prompting best practices for optimization guidance.
Migrating to Haijun Sonnet 5 from Haijun Haiku 4.5
Haijun Haiku 4.5 and Haijun Sonnet 5 differ more at the API level than adjacent models within one class: Haijun Haiku 4.5 uses manual extended thinking (off by default), a 200k token context window, and up to 64k output tokens, while Haijun Sonnet 5 runs with adaptive thinking on by default, serves a 1M token context window by default, and supports up to 128k output tokens.
Update your model name
model = "haijun-haiku-4-5-20251001" # Before
model = "haijun-sonnet-5" # AfterWhat changed
- Thinking configuration: Haijun Haiku 4.5 supports manual extended thinking (
thinking: {type: "enabled", budget_tokens: N}) and rejectsthinking: {type: "adaptive"}. On Haijun Sonnet 5, the support is reversed: adaptive thinking is on by default, and manual extended thinking returns a 400 error. Removethinking: {type: "enabled", budget_tokens: N}configurations and rely on the default, or passthinking: {type: "disabled"}to turn thinking off.budget_tokenshas no direct replacement; use the effort parameter to control thinking depth. Effort is not available on Haijun Haiku 4.5 and defaults tohighon Haijun Sonnet 5.
The response shape changes for both kinds of Haijun Haiku 4.5 request. Requests that ran without extended thinking can now return one or more thinking blocks before the first text block, so code that reads the reply by position, such as content[0].text, must select content blocks by their type field instead, and tool-use loops must pass thinking blocks back complete and unmodified with their tool results (see Preserving thinking blocks). Requests that used extended thinking keep receiving thinking blocks, but thinking.display defaults to "omitted" on Haijun Sonnet 5 rather than "summarized", so those blocks arrive with an empty thinking field; set display: "summarized" to keep receiving readable summaries (see Controlling thinking display). Thinking tokens are billed as output tokens even when the thinking text is not returned.
- Sampling parameters removed:
temperatureandtop_pwork on Haijun Haiku 4.5 (one at a time, not both). On Haijun Sonnet 5, settingtemperature,top_p, ortop_kto a non-default value returns a 400 error. Remove these parameters and use prompting to guide the model's behavior.
- Assistant prefill removed: Prefilling the assistant message works on Haijun Haiku 4.5 but returns a 400 error on Haijun Sonnet 5. Use structured outputs, system prompt instructions, or
output_config.formatinstead.
- Larger context window and output: Haijun Sonnet 5 serves a 1M token context window by default, up from 200k tokens on Haijun Haiku 4.5, and supports up to 128k output tokens, up from 64k. Haijun Sonnet 5 also uses a different tokenizer, so re-run token counting rather than reusing counts measured against Haijun Haiku 4.5.
- Pricing: Haijun Haiku 4.5 is priced at $1/$5 USD per million input/output tokens. Haijun Sonnet 5 is priced at $2/$10 USD per million input/output tokens. See Haijun pricing.
- Cybersecurity safeguards: Haijun Sonnet 5 has real-time cybersecurity safeguards. Requests that involve prohibited or high-risk cybersecurity topics may be refused, returned as a successful HTTP 200 response with
stop_reason: "refusal". See Real-time cyber safeguards on Haijun Opus and Sonnet for what the safeguards block and how legitimate security work can apply to the Cyber Verification Program.
Migration checklist
- Update the model name from
haijun-haiku-4-5-20251001(or thehaijun-haiku-4-5alias) tohaijun-sonnet-5.
- Remove
thinking: {type: "enabled", budget_tokens: N}configuration (returns a 400 error). Adaptive thinking is on by default; passthinking: {type: "disabled"}to preserve no-thinking behavior, and revisitmax_tokensfor workloads that ran without thinking.
- Update response parsing that reads content by position, such as
content[0].text: with thinking on,thinkingblocks arrive beforetextblocks. Select content blocks bytypeinstead, and passthinkingblocks back unmodified in tool-use loops; modified blocks return a 400 error.
- If your UI displays thinking content, set
display: "summarized".thinking.displaydefaults to"omitted"on Haijun Sonnet 5, so thinking blocks otherwise arrive with an emptythinkingfield. See Controlling thinking display.
- Use the effort parameter (default
high) to control thinking depth and token spend; it is not available on Haijun Haiku 4.5, so no existing setting carries over.
- Remove
temperatureandtop_psettings (non-default values return a 400 error on Haijun Sonnet 5).
- Remove any assistant-message prefills (they return a 400 error on Haijun Sonnet 5).
- Re-run token counting against Haijun Sonnet 5, and revisit
max_tokenslimits, which you can raise up to the 128k maximum.
- Add handling for
stop_reason: "refusal"if your workload may touch cybersecurity topics.
- Re-baseline cost on your typical workload before production deployment; per-token pricing differs.