package ai.nomyo; import ai.nomyo.errors.*; import lombok.Getter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ExecutionException; /** * High-level OpenAI-compatible entrypoint with automatic hybrid encryption (AES-256-GCM + RSA-4096). */ @Getter public class SecureChatCompletion { private final SecureCompletionClient client; private final String apiKey; private final String keyDir; /** * Default settings: {@code https://api.nomyo.ai}, HTTPS-only, secure memory, ephemeral keys, 2 retries. */ public SecureChatCompletion() { this(Constants.DEFAULT_BASE_URL, false, null, true, null, Constants.DEFAULT_MAX_RETRIES); } public SecureChatCompletion(String baseUrl, String apiKey) { this(baseUrl, false, apiKey, true, null, Constants.DEFAULT_MAX_RETRIES); } public SecureChatCompletion(String baseUrl, String apiKey, boolean allowHttp) { this(baseUrl, allowHttp, apiKey, true, null, Constants.DEFAULT_MAX_RETRIES); } public SecureChatCompletion(String baseUrl) { this(baseUrl, false, null, true, null, Constants.DEFAULT_MAX_RETRIES); } /** * @param baseUrl NOMYO Router base URL (HTTPS enforced unless {@code allowHttp}) * @param allowHttp permit {@code http://} URLs (development only) * @param apiKey Bearer token (can also be passed per-call via {@link #create}) * @param secureMemory enable memory locking/zeroing * @param keyDir RSA key directory; {@code null} = ephemeral * @param maxRetries retries on 429/500/502/503/504 + network errors (exponential backoff) */ public SecureChatCompletion(String baseUrl, boolean allowHttp, String apiKey, boolean secureMemory, String keyDir, int maxRetries) { this.client = new SecureCompletionClient(baseUrl, allowHttp, secureMemory, maxRetries); this.apiKey = apiKey; this.keyDir = keyDir; } /** * Main entrypoint — same signature as {@code openai.ChatCompletion.create()}. * All kwargs are passed through to the OpenAI-compatible API. *

Streaming is not supported (server rejects with HTTP 400). * Security tiers: "standard", "high", "maximum". * * @param model model identifier (required) * @param messages OpenAI-format message list (required) * @param kwargs additional OpenAI-compatible params (temperature, maxTokens, etc.) * @return decrypted response map * @throws SecurityError encryption/decryption failure * @throws APIConnectionError network error * @throws InvalidRequestError HTTP 400 * @throws AuthenticationError HTTP 401 * @throws ForbiddenError HTTP 403 * @throws RateLimitError HTTP 429 * @throws ServerError HTTP 500 * @throws ServiceUnavailableError HTTP 503 * @throws APIError other errors */ @SuppressWarnings({"JavadocDeclaration"}) public Map create(String model, List> messages, Map kwargs) { // Validate required parameters if (model == null || model.isEmpty()) { throw new IllegalArgumentException("model is required"); } if (messages == null || messages.isEmpty()) { throw new IllegalArgumentException("messages is required and cannot be empty"); } // Build payload Map payload = new HashMap<>(); payload.put("model", model); payload.put("messages", messages); // Add kwargs if (kwargs != null) { // Check for stream parameter if (kwargs.containsKey("stream")) { Object streamValue = kwargs.get("stream"); boolean stream = streamValue instanceof Boolean ? (Boolean) streamValue : Boolean.parseBoolean(streamValue.toString()); if (stream) { throw new IllegalArgumentException("Streaming is not supported"); } } // Check for security_tier if (kwargs.containsKey("security_tier")) { Object tier = kwargs.get("security_tier"); if (tier != null && !Constants.VALID_SECURITY_TIERS.contains(tier.toString())) { throw new IllegalArgumentException( "Invalid security_tier: '" + tier + "'. Must be one of: " + Constants.VALID_SECURITY_TIERS); } } payload.putAll(kwargs); } // Determine API key (per-call override or instance key) String apiKey = this.apiKey; if (kwargs != null && kwargs.containsKey("api_key")) { Object key = kwargs.get("api_key"); if (key != null) { apiKey = key.toString(); } } // Determine security tier String securityTier = null; if (kwargs != null && kwargs.containsKey("security_tier")) { securityTier = kwargs.get("security_tier").toString(); } // Generate payload ID String payloadId = UUID.randomUUID().toString(); // Send secure request try { return client.sendSecureRequest(payload, payloadId, apiKey, securityTier).get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Request interrupted", e); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause instanceof SecureCompletionClient.ValueError) { throw new IllegalArgumentException(cause); } throw new RuntimeException(cause); } } /** * Convenience variant with no additional parameters. */ @SuppressWarnings("UnusedReturnValue") public Map create(String model, List> messages) { return create(model, messages, null); } /** * Delegates to {@link SecureCompletionClient#close()}. */ public void close() { client.close(); } }