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_SOL("gpt-5.6-sol", 1_050_000, 128_000, EffortSupport.FULL), 027 GPT_5_6_TERRA("gpt-5.6-terra", 1_050_000, 128_000, EffortSupport.FULL), 028 GPT_5_6_LUNA("gpt-5.6-luna", 1_050_000, 128_000, EffortSupport.FULL), 029 GPT_5_5("gpt-5.5", 1_050_000, 128_000, EffortSupport.EXTENDED), 030 GPT_5_4("gpt-5.4", 1_050_000, 128_000, EffortSupport.EXTENDED), 031 GPT_5_4_MINI("gpt-5.4-mini", 400_000, 128_000, EffortSupport.STANDARD), 032 GPT_5_4_NANO("gpt-5.4-nano", 400_000, 128_000, EffortSupport.STANDARD), 033 GPT_4_1("gpt-4.1", 1_000_000, 32_000, EffortSupport.STANDARD), 034 GPT_4_1_MINI("gpt-4.1-mini", 1_000_000, 32_000, EffortSupport.STANDARD), 035 GPT_4_1_NANO("gpt-4.1-nano", 1_000_000, 16_000, EffortSupport.STANDARD), 036 GPT_4O("gpt-4o", 128_000, 16_384, EffortSupport.STANDARD), 037 GPT_4O_MINI("gpt-4o-mini", 128_000, 16_384, EffortSupport.STANDARD), 038 O3("o3", 200_000, 100_000, EffortSupport.STANDARD), 039 O4_MINI("o4-mini", 200_000, 100_000, EffortSupport.STANDARD); 040 041 /** 042 * The {@code reasoning.effort} value range a model accepts on the Responses API. Wider tiers are 043 * supersets: {@link #FULL} accepts everything {@link #EXTENDED} does plus {@code none} and {@code 044 * max}. 045 */ 046 public enum EffortSupport { 047 /** {@code low} / {@code medium} / {@code high} only; higher Helios tiers clamp to high. */ 048 STANDARD, 049 050 /** Adds {@code xhigh}; {@code ThinkingLevel.MAX} clamps to xhigh. */ 051 EXTENDED, 052 053 /** 054 * Full range {@code none} / {@code low} / {@code medium} / {@code high} / {@code xhigh} / 055 * {@code max} (gpt-5.6 family). 056 */ 057 FULL 058 } 059 060 private final String id; 061 private final int contextWindow; 062 private final int maxOutputTokens; 063 private final EffortSupport effortSupport; 064 065 OpenAIModelId(String id, int contextWindow, int maxOutputTokens, EffortSupport effortSupport) { 066 this.id = id; 067 this.contextWindow = contextWindow; 068 this.maxOutputTokens = maxOutputTokens; 069 this.effortSupport = effortSupport; 070 } 071 072 /** 073 * The {@code reasoning.effort} range this model accepts. Drives the {@code ThinkingLevel} → wire 074 * mapping in {@code OpenAIModel}: tiers above the model's ceiling clamp down so requests stay 075 * valid. 076 * 077 * @return the effort-support tier 078 */ 079 public EffortSupport effortSupport() { 080 return effortSupport; 081 } 082 083 /** 084 * Whether this model accepts {@code reasoning.effort=xhigh}. 085 * 086 * @return true for {@link EffortSupport#EXTENDED} and {@link EffortSupport#FULL} models 087 * @deprecated use {@link #effortSupport()}; the boolean cannot express the gpt-5.6 family's 088 * {@code none}/{@code max} support 089 */ 090 @Deprecated(since = "2.8.0") 091 public boolean supportsXhighEffort() { 092 return effortSupport != EffortSupport.STANDARD; 093 } 094 095 /** 096 * Returns the API model identifier string. 097 * 098 * @return the model ID used in API requests 099 */ 100 public String id() { 101 return id; 102 } 103 104 /** 105 * Returns the context window size in tokens. 106 * 107 * @return the context window size 108 */ 109 public int contextWindow() { 110 return contextWindow; 111 } 112 113 /** 114 * Returns the maximum output tokens this model can generate in a single response. Used as the 115 * fallback when {@code ModelConfig.maxOutputTokens()} is unset. 116 * 117 * @return the per-model output ceiling 118 */ 119 public int maxOutputTokens() { 120 return maxOutputTokens; 121 } 122 123 /** 124 * Finds an OpenAIModelId by its string identifier. 125 * 126 * @param id the model identifier string 127 * @return the matching OpenAIModelId, or null if not found 128 */ 129 public static OpenAIModelId fromId(String id) { 130 if (Strings.isBlank(id)) { 131 return null; 132 } 133 for (var model : values()) { 134 if (model.id.equals(id)) { 135 return model; 136 } 137 } 138 return null; 139 } 140 141 /** 142 * Checks if the given model ID is supported. 143 * 144 * @param id the model identifier string 145 * @return true if the model is supported 146 */ 147 public static boolean isSupported(String id) { 148 return fromId(id) != null; 149 } 150}