001/* 002 * Copyright (c) 2026 Singular 003 * SPDX-License-Identifier: MIT 004 */ 005 006package ai.singlr.openai; 007 008import ai.singlr.core.common.Strings; 009import ai.singlr.core.model.Model; 010import ai.singlr.core.model.ModelConfig; 011import ai.singlr.core.model.ModelProvider; 012 013/** 014 * ModelProvider implementation for OpenAI's Responses API. 015 * 016 * <p>Supports every {@link OpenAIModelId} out of the box. When {@link ModelConfig#baseUrl()} is set 017 * — pointing at Azure OpenAI, an OpenAI-compatible proxy (LiteLLM, vLLM, Ollama), or Vertex AI — 018 * any non-blank {@code modelId} is accepted. The string is used verbatim as the {@code model} field 019 * in the request body, which Azure OpenAI maps to the deployment name. Context-window and 020 * max-output-tokens metadata default to {@code 0} ("unknown") for unrecognised ids; callers can 021 * override output tokens via {@link ModelConfig.Builder#withMaxOutputTokens(Integer)}. 022 */ 023public class OpenAIProvider implements ModelProvider { 024 025 private static final String PROVIDER_NAME = "openai"; 026 027 @Override 028 public String name() { 029 return PROVIDER_NAME; 030 } 031 032 @Override 033 public Model create(String modelId, ModelConfig config) { 034 var known = OpenAIModelId.fromId(modelId); 035 if (known != null) { 036 return new OpenAIModel(known, config); 037 } 038 if (!Strings.isBlank(config.baseUrl())) { 039 return new OpenAIModel(modelId, config); 040 } 041 throw new IllegalArgumentException( 042 "Unsupported model: " 043 + modelId 044 + ". Set ModelConfig.baseUrl for custom endpoints (Azure, proxy, Vertex)."); 045 } 046 047 @Override 048 public boolean supports(String modelId) { 049 return OpenAIModelId.isSupported(modelId); 050 } 051}