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: the gpt-5.6 family documents the full none..max reasoning.effort 021 // range (latest-model guide, 2026-07-18); gpt-5.4 and gpt-5.5 document low..xhigh; mini/nano 022 // variants and o-series models are documented for low..high only, so higher tiers clamp. 023 // gpt-5.6-terra and gpt-5.6-luna share the gpt-5.6 family limits per the latest-model guide; 024 // adjust when OpenAI publishes per-variant pages. 025 GPT_5_6("gpt-5.6", 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.STANDARD), 031 GPT_5_4_NANO("gpt-5.4-nano", 400_000, 128_000, EffortSupport.STANDARD), 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 /** {@code low} / {@code medium} / {@code high} only; higher Helios tiers clamp to high. */ 047 STANDARD, 048 049 /** Adds {@code xhigh}; {@code ThinkingLevel.MAX} clamps to xhigh. */ 050 EXTENDED, 051 052 /** 053 * Full range {@code none} / {@code low} / {@code medium} / {@code high} / {@code xhigh} / 054 * {@code max} (gpt-5.6 family). 055 */ 056 FULL 057 } 058 059 private final String id; 060 private final int contextWindow; 061 private final int maxOutputTokens; 062 private final EffortSupport effortSupport; 063 064 OpenAIModelId(String id, int contextWindow, int maxOutputTokens, EffortSupport effortSupport) { 065 this.id = id; 066 this.contextWindow = contextWindow; 067 this.maxOutputTokens = maxOutputTokens; 068 this.effortSupport = effortSupport; 069 } 070 071 /** 072 * The {@code reasoning.effort} range this model accepts. Drives the {@code ThinkingLevel} → wire 073 * mapping in {@code OpenAIModel}: tiers above the model's ceiling clamp down so requests stay 074 * valid. 075 * 076 * @return the effort-support tier 077 */ 078 public EffortSupport effortSupport() { 079 return effortSupport; 080 } 081 082 /** 083 * Whether this model accepts {@code reasoning.effort=xhigh}. 084 * 085 * @return true for {@link EffortSupport#EXTENDED} and {@link EffortSupport#FULL} models 086 * @deprecated use {@link #effortSupport()}; the boolean cannot express the gpt-5.6 family's 087 * {@code none}/{@code max} support 088 */ 089 @Deprecated(since = "2.8.0") 090 public boolean supportsXhighEffort() { 091 return effortSupport != EffortSupport.STANDARD; 092 } 093 094 /** 095 * Returns the API model identifier string. 096 * 097 * @return the model ID used in API requests 098 */ 099 public String id() { 100 return id; 101 } 102 103 /** 104 * Returns the context window size in tokens. 105 * 106 * @return the context window size 107 */ 108 public int contextWindow() { 109 return contextWindow; 110 } 111 112 /** 113 * Returns the maximum output tokens this model can generate in a single response. Used as the 114 * fallback when {@code ModelConfig.maxOutputTokens()} is unset. 115 * 116 * @return the per-model output ceiling 117 */ 118 public int maxOutputTokens() { 119 return maxOutputTokens; 120 } 121 122 /** 123 * Finds an OpenAIModelId by its string identifier. 124 * 125 * @param id the model identifier string 126 * @return the matching OpenAIModelId, or null if not found 127 */ 128 public static OpenAIModelId fromId(String id) { 129 if (Strings.isBlank(id)) { 130 return null; 131 } 132 for (var model : values()) { 133 if (model.id.equals(id)) { 134 return model; 135 } 136 } 137 return null; 138 } 139 140 /** 141 * Checks if the given model ID is supported. 142 * 143 * @param id the model identifier string 144 * @return true if the model is supported 145 */ 146 public static boolean isSupported(String id) { 147 return fromId(id) != null; 148 } 149}