0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-09 18:01:48 +08:00
discourse/plugins/discourse-ai/lib/completions/llm_presets.rb
Sam e1caf1d1d3
FEATURE: Support OpenAI GPT-5.6 models (#41832)
Refresh the OpenAI presets for the GPT-5.6 family, including cache-write
pricing and usage accounting. Add reasoning mode and max-effort support
while preserving compatibility with models limited to xhigh.
2026-07-20 19:19:11 +10:00

316 lines
10 KiB
Ruby
Vendored

# frozen_string_literal: true
module DiscourseAi
module Completions
class LlmPresets
class << self
def all
@all ||= build_presets
end
def find_provider(provider_id)
all.find { |preset| preset[:id] == provider_id }
end
def find_model(provider_id, model_name)
provider = find_provider(provider_id)
return nil unless provider
provider[:models].find { |m| m[:name] == model_name }
end
def reset_cache!
@all = nil
end
private
def build_presets
[
anthropic_preset,
google_preset,
google_vertex_ai_preset,
open_ai_preset,
open_router_preset,
].freeze
end
def anthropic_preset
{
id: "anthropic",
models: [
model(
name: "claude-opus-4-8",
tokens: 1_000_000,
display_name: "Claude Opus 4.8",
max_output_tokens: 128_000,
input_cost: 5.0,
cached_input_cost: 0.50,
cache_write_cost: 6.25,
output_cost: 25.0,
vision_enabled: true,
# Opus 4.7+ rejects the classic thinking.type=enabled request
# and requires thinking.type=adaptive instead.
provider_params: {
enable_reasoning: true,
adaptive_thinking: true,
},
),
model(
name: "claude-opus-4-7",
tokens: 1_000_000,
display_name: "Claude Opus 4.7",
max_output_tokens: 128_000,
input_cost: 5.0,
cached_input_cost: 0.50,
cache_write_cost: 6.25,
output_cost: 25.0,
vision_enabled: true,
provider_params: {
enable_reasoning: true,
adaptive_thinking: true,
},
),
model(
name: "claude-sonnet-5",
tokens: 1_000_000,
display_name: "Claude Sonnet 5",
max_output_tokens: 64_000,
input_cost: 3.0,
cached_input_cost: 0.30,
cache_write_cost: 3.75,
output_cost: 15.0,
vision_enabled: true,
# Sonnet 5 also requires adaptive thinking, same as Opus 4.7+.
provider_params: {
enable_reasoning: true,
adaptive_thinking: true,
},
),
model(
name: "claude-sonnet-4-6",
tokens: 1_000_000,
display_name: "Claude Sonnet 4.6",
max_output_tokens: 64_000,
input_cost: 3.0,
cached_input_cost: 0.30,
cache_write_cost: 3.75,
output_cost: 15.0,
vision_enabled: true,
),
model(
name: "claude-haiku-4-5",
tokens: 200_000,
display_name: "Claude Haiku 4.5",
max_output_tokens: 64_000,
input_cost: 1.0,
cached_input_cost: 0.10,
cache_write_cost: 1.25,
output_cost: 5.0,
vision_enabled: true,
),
],
tokenizer: DiscourseAi::Tokenizer::AnthropicTokenizer,
endpoint: "https://api.anthropic.com/v1/messages",
provider: "anthropic",
}
end
def google_preset
{
id: "google",
models: [
model(
name: "gemini-3.1-pro",
tokens: 1_000_000,
display_name: "Gemini 3.1 Pro",
max_output_tokens: 65_000,
input_cost: 2.0,
cached_input_cost: 0.20,
output_cost: 12.0,
vision_enabled: true,
endpoint:
"https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-pro-preview",
),
model(
name: "gemini-3-flash",
tokens: 1_000_000,
display_name: "Gemini 3 Flash",
max_output_tokens: 65_000,
input_cost: 0.50,
cached_input_cost: 0.05,
output_cost: 3.0,
vision_enabled: true,
endpoint:
"https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview",
),
],
tokenizer: DiscourseAi::Tokenizer::GeminiTokenizer,
provider: "google",
}
end
def google_vertex_ai_preset
{
id: "google_vertex_ai",
models: [
model(
name: "google/gemini-3-flash",
tokens: 1_000_000,
display_name: "Gemini 3 Flash (Vertex)",
max_output_tokens: 65_000,
input_cost: 0.50,
cached_input_cost: 0.05,
output_cost: 3.0,
vision_enabled: true,
provider_params: {
region: "global",
},
),
],
tokenizer: DiscourseAi::Tokenizer::GeminiTokenizer,
provider: "google_vertex_ai",
}
end
def open_ai_preset
{
id: "open_ai",
models: [
model(
name: "gpt-5.6-sol",
tokens: 1_050_000,
display_name: "GPT-5.6 Sol",
max_output_tokens: 128_000,
input_cost: 5.0,
cached_input_cost: 0.50,
cache_write_cost: 6.25,
output_cost: 30.0,
vision_enabled: true,
endpoint: "https://api.openai.com/v1/responses",
),
model(
name: "gpt-5.6-terra",
tokens: 1_050_000,
display_name: "GPT-5.6 Terra",
max_output_tokens: 128_000,
input_cost: 2.50,
cached_input_cost: 0.25,
cache_write_cost: 3.125,
output_cost: 15.0,
vision_enabled: true,
endpoint: "https://api.openai.com/v1/responses",
),
model(
name: "gpt-5.6-luna",
tokens: 1_050_000,
display_name: "GPT-5.6 Luna",
max_output_tokens: 128_000,
input_cost: 1.0,
cached_input_cost: 0.10,
cache_write_cost: 1.25,
output_cost: 6.0,
vision_enabled: true,
endpoint: "https://api.openai.com/v1/responses",
),
],
tokenizer: DiscourseAi::Tokenizer::OpenAiTokenizer,
endpoint: "https://api.openai.com/v1/chat/completions",
provider: "open_ai",
}
end
def open_router_preset
{
id: "open_router",
models: [
model(
name: "moonshotai/kimi-k2.6",
tokens: 262_144,
display_name: "Moonshot Kimi K2.6",
max_output_tokens: 131_072,
input_cost: 0.74,
cached_input_cost: 0.14,
output_cost: 3.49,
vision_enabled: true,
),
model(
name: "deepseek/deepseek-v4-flash",
tokens: 1_048_576,
display_name: "DeepSeek V4 Flash",
max_output_tokens: 192_000,
input_cost: 0.14,
cached_input_cost: 0.0028,
output_cost: 0.28,
),
model(
name: "minimax/minimax-m2.7",
tokens: 196_608,
display_name: "MiniMax M2.7",
max_output_tokens: 65_536,
input_cost: 0.30,
cached_input_cost: 0.06,
output_cost: 1.20,
),
model(
name: "x-ai/grok-4.1-fast",
tokens: 2_000_000,
display_name: "xAI Grok 4.1 Fast",
max_output_tokens: 30_000,
input_cost: 0.20,
cached_input_cost: 0.05,
output_cost: 0.50,
vision_enabled: true,
),
model(
name: "z-ai/glm-5.1",
tokens: 202_752,
display_name: "Z-AI GLM-5.1",
max_output_tokens: 65_536,
input_cost: 1.05,
cached_input_cost: 0.525,
output_cost: 3.50,
),
model(
name: "qwen/qwen3.6-plus",
tokens: 1_000_000,
display_name: "Qwen3.6 Plus",
max_output_tokens: 65_536,
input_cost: 0.325,
output_cost: 1.95,
vision_enabled: true,
),
],
tokenizer: DiscourseAi::Tokenizer::OpenAiTokenizer,
endpoint: "https://openrouter.ai/api/v1/chat/completions",
provider: "open_router",
}
end
def model(
name:,
tokens:,
display_name:,
max_output_tokens: nil,
input_cost: nil,
cached_input_cost: nil,
cache_write_cost: nil,
output_cost: nil,
vision_enabled: false,
endpoint: nil,
provider_params: nil
)
result = { name: name, tokens: tokens, display_name: display_name }
result[:max_output_tokens] = max_output_tokens if max_output_tokens
result[:input_cost] = input_cost if input_cost
result[:cached_input_cost] = cached_input_cost if cached_input_cost
result[:cache_write_cost] = cache_write_cost if cache_write_cost
result[:output_cost] = output_cost if output_cost
result[:vision_enabled] = vision_enabled if vision_enabled
result[:endpoint] = endpoint if endpoint
result[:provider_params] = provider_params if provider_params
result
end
end
end
end
end