Skip to content

Commit d410df3

Browse files
authored
FEATURE: support GPT-5 (#34168)
Also adds mappings for Claude on AWS - ensures developer message is used - ensures we do the max token remapping needed - ensures reasoning effort is passed along
1 parent b0b7e83 commit d410df3

File tree

7 files changed

+75
-31
lines changed

7 files changed

+75
-31
lines changed

plugins/discourse-ai/config/locales/client.en.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ en:
580580

581581
model_description:
582582
none: "General settings that work for most language models"
583-
anthropic-claude-opus-4-0: "Anthropic's most intelligent model"
583+
anthropic-claude-opus-4-1: "Anthropic's most intelligent model"
584584
anthropic-claude-sonnet-4-0: "Optimal balance of speed and cost"
585585
anthropic-claude-3-7-sonnet-latest: "Optimal balance of speed and cost (previous generation)"
586586
anthropic-claude-3-5-haiku-latest: "Fast and cost-effective"
@@ -590,9 +590,9 @@ en:
590590
google-gemini-2-0-flash-lite: "Cost efficient and low latency model"
591591
open_ai-o3: "Open AI's most capable reasoning model"
592592
open_ai-o4-mini: "Advanced Cost-efficient reasoning model"
593-
open_ai-gpt-4-1: "Open AI's flagship model. It is well suited for problem solving across domains"
594-
open_ai-gpt-4-1-mini: "Provides a balance between intelligence, speed, and cost that makes it an attractive model for many use cases."
595-
open_ai-gpt-4-1-nano: "Fastest, most cost-effective GPT-4.1 model."
593+
open_ai-gpt-5: "Open AI's flagship model. It is well suited for problem solving across domains"
594+
open_ai-gpt-5-mini: "Provides a balance between intelligence, speed, and cost that makes it an attractive model for many use cases."
595+
open_ai-gpt-5-nano: "Fastest, most cost-effective GPT-5 model."
596596
samba_nova-Meta-Llama-3-1-8B-Instruct: "Efficient lightweight multilingual model"
597597
samba_nova-Meta-Llama-3-3-70B-Instruct": "Powerful multipurpose model"
598598
mistral-mistral-large-latest: "Mistral's most powerful model"

plugins/discourse-ai/lib/completions/dialects/chat_gpt.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ def tools_dialect
6969
# developer messages are preferred on recent reasoning models
7070
def supports_developer_messages?
7171
!legacy_reasoning_model? && llm_model.provider == "open_ai" &&
72-
(llm_model.name.start_with?("o1") || llm_model.name.start_with?("o3"))
72+
(
73+
llm_model.name.start_with?("o1") || llm_model.name.start_with?("o3") ||
74+
llm_model.name.start_with?("gpt-5")
75+
)
7376
end
7477

7578
def legacy_reasoning_model?

plugins/discourse-ai/lib/completions/endpoints/aws_bedrock.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,20 +70,28 @@ def bedrock_model_id
7070
case llm_model.name
7171
when "claude-2"
7272
"anthropic.claude-v2:1"
73-
when "claude-3-haiku"
73+
when "claude-3-haiku", "claude-3-haiku-20240307"
7474
"anthropic.claude-3-haiku-20240307-v1:0"
7575
when "claude-3-sonnet"
7676
"anthropic.claude-3-sonnet-20240229-v1:0"
7777
when "claude-instant-1"
7878
"anthropic.claude-instant-v1"
7979
when "claude-3-opus"
8080
"anthropic.claude-3-opus-20240229-v1:0"
81-
when "claude-3-5-sonnet"
81+
when "claude-3-5-sonnet", "claude-3-5-sonnet-20241022", "claude-3-5-sonnet-latest"
8282
"anthropic.claude-3-5-sonnet-20241022-v2:0"
83-
when "claude-3-5-haiku"
83+
when "claude-3-5-sonnet-20240620"
84+
"anthropic.claude-3-5-sonnet-20240620-v1:0"
85+
when "claude-3-5-haiku", "claude-3-5-haiku-20241022", "claude-3-5-haiku-latest"
8486
"anthropic.claude-3-5-haiku-20241022-v1:0"
85-
when "claude-3-7-sonnet"
87+
when "claude-3-7-sonnet", "claude-3-7-sonnet-20250219", "claude-3-7-sonnet-latest"
8688
"anthropic.claude-3-7-sonnet-20250219-v1:0"
89+
when "claude-opus-4-1", "claude-opus-4-1-20250805"
90+
"anthropic.claude-opus-4-1-20250805-v1:0"
91+
when "claude-opus-4", "claude-opus-4-20250514"
92+
"anthropic.claude-opus-4-20250514-v1:0"
93+
when "claude-sonnet-4", "claude-sonnet-4-20250514"
94+
"anthropic.claude-sonnet-4-20250514-v1:0"
8795
else
8896
llm_model.name
8997
end

plugins/discourse-ai/lib/completions/endpoints/open_ai.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def normalize_model_params(model_params)
1414
# max_tokens is deprecated however we still need to support it
1515
# on older OpenAI models and older Azure models, so we will only normalize
1616
# if our model name starts with o (to denote all the reasoning models)
17-
if llm_model.name.starts_with?("o")
17+
if llm_model.name.starts_with?("o") | llm_model.name.starts_with?("gpt-5")
1818
max_tokens = model_params.delete(:max_tokens)
1919
model_params[:max_completion_tokens] = max_tokens if max_tokens
2020
end

plugins/discourse-ai/lib/completions/llm.rb

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ def presets
5151
output_cost: 4,
5252
},
5353
{
54-
name: "claude-opus-4-0",
54+
name: "claude-opus-4-1",
5555
tokens: 200_000,
56-
display_name: "Claude 4 Opus",
56+
display_name: "Claude 4.1 Opus",
5757
input_cost: 15,
5858
cached_input_cost: 1.50,
5959
output_cost: 75,
@@ -126,27 +126,27 @@ def presets
126126
output_cost: 8,
127127
},
128128
{
129-
name: "gpt-4.1",
130-
tokens: 800_000,
131-
display_name: "GPT-4.1",
132-
input_cost: 2,
133-
cached_input_cost: 0.5,
134-
output_cost: 8,
129+
name: "gpt-5",
130+
tokens: 400_000,
131+
display_name: "GPT-5",
132+
input_cost: 1.25,
133+
cached_input_cost: 0.124,
134+
output_cost: 10,
135135
},
136136
{
137-
name: "gpt-4.1-mini",
138-
tokens: 800_000,
139-
display_name: "GPT-4.1 Mini",
140-
input_cost: 0.40,
141-
cached_input_cost: 0.10,
142-
output_cost: 1.60,
137+
name: "gpt-5-mini",
138+
tokens: 400_000,
139+
display_name: "GPT-5 Mini",
140+
input_cost: 0.25,
141+
cached_input_cost: 0.025,
142+
output_cost: 2.0,
143143
},
144144
{
145-
name: "gpt-4.1-nano",
146-
tokens: 800_000,
145+
name: "gpt-5-nano",
146+
tokens: 400_000,
147147
display_name: "GPT-4.1 Nano",
148-
input_cost: 0.10,
149-
cached_input_cost: 0.025,
148+
input_cost: 0.05,
149+
cached_input_cost: 0.005,
150150
output_cost: 0.40,
151151
},
152152
],

plugins/discourse-ai/spec/lib/completions/endpoints/open_ai_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,39 @@ def request_body(prompt, stream: false, tool_call: false)
738738
end
739739
end
740740

741+
it "supports gpt-5, remaps max_tokens, passes reasoning effort, and uses developer message" do
742+
model.update!(name: "gpt-5")
743+
744+
prompt =
745+
DiscourseAi::Completions::Prompt.new(
746+
"You are a bot",
747+
messages: [type: :user, content: "hello"],
748+
)
749+
dialect = compliance.dialect(prompt: prompt)
750+
751+
body_parsed = nil
752+
stub_request(:post, "https://api.openai.com/v1/chat/completions").with(
753+
body:
754+
proc do |body|
755+
body_parsed = JSON.parse(body)
756+
true
757+
end,
758+
).to_return(status: 200, body: { choices: [{ message: { content: "ok" } }] }.to_json)
759+
760+
endpoint.perform_completion!(
761+
dialect,
762+
user,
763+
{ max_tokens: 321, reasoning: { effort: "low" } },
764+
)
765+
766+
expect(body_parsed["model"]).to eq("gpt-5")
767+
expect(body_parsed["max_completion_tokens"]).to eq(321)
768+
expect(body_parsed["max_tokens"]).to be_nil
769+
expect(body_parsed["reasoning"]).to eq({ "effort" => "low" })
770+
expect(body_parsed["messages"].first["role"]).to eq("developer")
771+
expect(body_parsed["messages"].first["content"]).to eq("You are a bot")
772+
end
773+
741774
context "with tools" do
742775
it "returns a function invocation" do
743776
compliance.streaming_mode_tools(open_ai_mock)

plugins/discourse-ai/spec/system/llms/ai_llm_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
it "correctly sets defaults" do
1616
visit "/admin/plugins/discourse-ai/ai-llms"
1717

18-
find("[data-llm-id='anthropic-claude-opus-4-0'] button").click()
18+
find("[data-llm-id='anthropic-claude-opus-4-1'] button").click()
1919
form.field("api_key").fill_in("abcd")
2020
form.field("enabled_chat_bot").toggle
2121
form.submit
@@ -27,9 +27,9 @@
2727
expect(llm.api_key).to eq("abcd")
2828

2929
preset = DiscourseAi::Completions::Llm.presets.find { |p| p[:id] == "anthropic" }
30-
model_preset = preset[:models].find { |m| m[:name] == "claude-opus-4-0" }
30+
model_preset = preset[:models].find { |m| m[:name] == "claude-opus-4-1" }
3131

32-
expect(llm.name).to eq("claude-opus-4-0")
32+
expect(llm.name).to eq("claude-opus-4-1")
3333
expect(llm.url).to eq(preset[:endpoint])
3434
expect(llm.tokenizer).to eq(preset[:tokenizer].to_s)
3535
expect(llm.max_prompt_tokens.to_i).to eq(model_preset[:tokens])

0 commit comments

Comments
 (0)