001/* 002 * Copyright (c) 2026 Singular 003 * SPDX-License-Identifier: MIT 004 */ 005 006package ai.singlr.openai; 007 008import ai.singlr.core.common.Strings; 009 010/** 011 * Supported OpenAI model identifiers. 012 * 013 * <p>Each enum constant maps to a specific model available through the Responses API. 014 */ 015public enum OpenAIModelId { 016 // maxOutputTokens reflects the documented per-model output ceiling at time of writing — 017 // operators can override per-call via ModelConfig.Builder.withMaxOutputTokens. Reasoning models 018 // (o3, o4-mini) carry higher caps because their output includes reasoning tokens. 019 // 020 // EffortSupport per model (model pages, 2026-08-21): the gpt-5.6 family (incl. sol/terra/luna) 021 // documents none/low/medium/high/xhigh/max; gpt-5.5, gpt-5.4, gpt-5.4-mini and gpt-5.4-nano 022 // document none/low/medium/high/xhigh; o-series and non-reasoning GPT-4.x models are STANDARD 023 // (low..high, no explicit none), so higher tiers clamp. 024 GPT_5_6("gpt-5.6", 1_050_000, 128_000, EffortSupport.FULL), 025 GPT_5_6_SOL("gpt-5.6-sol", 1_050_000, 128_000, EffortSupport.FULL), 026 GPT_5_6_TERRA("gpt-5.6-terra", 1_050_000, 128_000, EffortSupport.FULL), 027 GPT_5_6_LUNA("gpt-5.6-luna", 1_050_000, 128_000, EffortSupport.FULL), 028 GPT_5_5("gpt-5.5", 1_050_000, 128_000, EffortSupport.EXTENDED), 029 GPT_5_4("gpt-5.4", 1_050_000, 128_000, EffortSupport.EXTENDED), 030 GPT_5_4_MINI("gpt-5.4-mini", 400_000, 128_000, EffortSupport.EXTENDED), 031 GPT_5_4_NANO("gpt-5.4-nano", 400_000, 128_000, EffortSupport.EXTENDED), 032 GPT_4_1("gpt-4.1", 1_000_000, 32_000, EffortSupport.STANDARD), 033 GPT_4_1_MINI("gpt-4.1-mini", 1_000_000, 32_000, EffortSupport.STANDARD), 034 GPT_4_1_NANO("gpt-4.1-nano", 1_000_000, 16_000, EffortSupport.STANDARD), 035 GPT_4O("gpt-4o", 128_000, 16_384, EffortSupport.STANDARD), 036 GPT_4O_MINI("gpt-4o-mini", 128_000, 16_384, EffortSupport.STANDARD), 037 O3("o3", 200_000, 100_000, EffortSupport.STANDARD), 038 O4_MINI("o4-mini", 200_000, 100_000, EffortSupport.STANDARD); 039 040 /** 041 * The {@code reasoning.effort} value range a model accepts on the Responses API. Wider tiers are 042 * supersets: {@link #FULL} accepts everything {@link #EXTENDED} does plus {@code none} and {@code 043 * max}. 044 */ 045 public enum EffortSupport { 046 /** 047 * {@code low} / {@code medium} / {@code high} only; higher Helios tiers clamp to high and 048 * {@code ThinkingLevel.NONE} omits the {@code reasoning} config. 049 */ 050 STANDARD, 051 052 /** 053 * {@code none} / {@code low} / {@code medium} / {@code high} / {@code xhigh} (gpt-5.4 and 054 * gpt-5.5 families); {@code ThinkingLevel.MAX} clamps to xhigh. 055 */ 056 EXTENDED, 057 058 /** 059 * Full range {@code none} / {@code low} / {@code medium} / {@code high} / {@code xhigh} / 060 * {@code max} (gpt-5.6 family). 061 */ 062 FULL 063 } 064 065 private final String id; 066 private final int contextWindow; 067 private final int maxOutputTokens; 068 private final EffortSupport effortSupport; 069 070 OpenAIModelId(String id, int contextWindow, int maxOutputTokens, EffortSupport effortSupport) { 071 this.id = id; 072 this.contextWindow = contextWindow; 073 this.maxOutputTokens = maxOutputTokens; 074 this.effortSupport = effortSupport; 075 } 076 077 /** 078 * The {@code reasoning.effort} range this model accepts. Drives the {@code ThinkingLevel} → wire 079 * mapping in {@code OpenAIModel}: tiers above the model's ceiling clamp down so requests stay 080 * valid. 081 * 082 * @return the effort-support tier 083 */ 084 public EffortSupport effortSupport() { 085 return effortSupport; 086 } 087 088 /** 089 * Whether this model accepts {@code reasoning.effort=xhigh}. 090 * 091 * @return true for {@link EffortSupport#EXTENDED} and {@link EffortSupport#FULL} models 092 * @deprecated use {@link #effortSupport()}; the boolean cannot express the gpt-5.6 family's 093 * {@code none}/{@code max} support 094 */ 095 @Deprecated(since = "2.8.0") 096 public boolean supportsXhighEffort() { 097 return effortSupport != EffortSupport.STANDARD; 098 } 099 100 /** 101 * Returns the API model identifier string. 102 * 103 * @return the model ID used in API requests 104 */ 105 public String id() { 106 return id; 107 } 108 109 /** 110 * Returns the context window size in tokens. 111 * 112 * @return the context window size 113 */ 114 public int contextWindow() { 115 return contextWindow; 116 } 117 118 /** 119 * Returns the maximum output tokens this model can generate in a single response. Used as the 120 * fallback when {@code ModelConfig.maxOutputTokens()} is unset. 121 * 122 * @return the per-model output ceiling 123 */ 124 public int maxOutputTokens() { 125 return maxOutputTokens; 126 } 127 128 /** 129 * Finds an OpenAIModelId by its string identifier. 130 * 131 * @param id the model identifier string 132 * @return the matching OpenAIModelId, or null if not found 133 */ 134 public static OpenAIModelId fromId(String id) { 135 if (Strings.isBlank(id)) { 136 return null; 137 } 138 for (var model : values()) { 139 if (model.id.equals(id)) { 140 return model; 141 } 142 } 143 return null; 144 } 145 146 /** 147 * Checks if the given model ID is supported. 148 * 149 * @param id the model identifier string 150 * @return true if the model is supported 151 */ 152 public static boolean isSupported(String id) { 153 return fromId(id) != null; 154 } 155}