diff --git a/docs/content/docs/reference/components/nano-gpt_v1.mdx b/docs/content/docs/reference/components/nano-gpt_v1.mdx
index 1308e1d2043..1a9b7575f97 100644
--- a/docs/content/docs/reference/components/nano-gpt_v1.mdx
+++ b/docs/content/docs/reference/components/nano-gpt_v1.mdx
@@ -29,6 +29,21 @@ Version: 1
+## Connection Setup
+
+1. Navigate to [NanoGPT console](https://nano-gpt.com/conversation/new).
+2. Click on **API**.
+3. Click on **Create API Key**.
+4. Enter name of your new key.
+5. Click on **Create**.
+6. Here is your new API key.
+7. Done 🚀.
+
+
+
+
+
+
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateSpeechAction.java b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateSpeechAction.java
index 15a8cadda80..7db9f05a787 100644
--- a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateSpeechAction.java
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateSpeechAction.java
@@ -54,11 +54,22 @@
*/
public class NanoGptCreateSpeechAction {
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
- private static final int MAX_POLL_ATTEMPTS = 40;
- private static final int POLL_INTERVAL_MS = 3000;
+ protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+ protected static final int MAX_POLL_ATTEMPTS = 40;
+ protected static final int POLL_INTERVAL_MS = 3000;
- private record AudioFetchResult(byte[] bytes, String extension, String audioUrl) {
+ protected record AudioFetchResult(byte[] bytes, String extension, String audioUrl) {
+
+ protected AudioFetchResult(byte[] bytes, String extension, String audioUrl) {
+ this.bytes = bytes == null ? null : bytes.clone();
+ this.extension = extension;
+ this.audioUrl = audioUrl;
+ }
+
+ @Override
+ public byte[] bytes() {
+ return bytes == null ? null : bytes.clone();
+ }
}
public static final ModifiableActionDefinition ACTION_DEFINITION = action(CREATE_SPEECH)
@@ -111,12 +122,12 @@ public static Object perform(Parameters inputParameters, Parameters connectionPa
Map requestBody = new HashMap<>();
requestBody.put("text", inputParameters.getRequiredString(INPUT));
- requestBody.put("model", inputParameters.getRequiredString(MODEL));
+ requestBody.put(MODEL, inputParameters.getRequiredString(MODEL));
String voice = inputParameters.getString(VOICE);
if (voice != null && !voice.isBlank()) {
- requestBody.put("voice", voice);
+ requestBody.put(VOICE, voice);
}
String responseFormat = inputParameters.getString(RESPONSE_FORMAT, "mp3");
@@ -126,7 +137,7 @@ public static Object perform(Parameters inputParameters, Parameters connectionPa
Double speed = inputParameters.getDouble(SPEED);
if (speed != null) {
- requestBody.put("speed", speed);
+ requestBody.put(SPEED, speed);
}
RestClient restClient = ModelUtils.getRestClientBuilder()
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateTranscriptionAction.java b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateTranscriptionAction.java
index db91a011985..efe69ac8d30 100644
--- a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateTranscriptionAction.java
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateTranscriptionAction.java
@@ -55,8 +55,7 @@ public class NanoGptCreateTranscriptionAction {
TRANSCRIPTION_MODEL_PROPERTY,
fileEntry(FILE)
.label("File")
- .description(
- "The audio file to transcribe. Supported formats: MP3, WAV, M4A, OGG, AAC (max 3MB).")
+ .description("The audio file to transcribe. Supported formats: MP3, WAV, M4A, OGG, AAC (max 3MB).")
.required(true),
TRANSCRIPTION_LANGUAGE_PROPERTY)
.output(outputSchema(string()))
@@ -80,12 +79,12 @@ public String getFilename() {
}
});
- formData.add("model", inputParameters.getRequiredString(MODEL));
+ formData.add(MODEL, inputParameters.getRequiredString(MODEL));
String language = inputParameters.getString(LANGUAGE);
if (language != null) {
- formData.add("language", language);
+ formData.add(LANGUAGE, language);
}
RestClient restClient = ModelUtils.getRestClientBuilder()
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/connection/NanoGptConnection.java b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/connection/NanoGptConnection.java
index 378aac8ab4f..c65c7fc2684 100644
--- a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/connection/NanoGptConnection.java
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/connection/NanoGptConnection.java
@@ -27,7 +27,9 @@
public final class NanoGptConnection {
public static final ModifiableConnectionDefinition CONNECTION_DEFINITION =
- RouterConnection.connectionDefinition(BASE_URL);
+ RouterConnection.connectionDefinition(BASE_URL)
+ .version(1)
+ .help("", "https://docs.bytechef.io/reference/components/nano-gpt_v1#connection-setup");
private NanoGptConnection() {
}
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/model/NanoGptChatModel.java b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/model/NanoGptChatModel.java
index 3a4f4d49bc2..174948dad90 100644
--- a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/model/NanoGptChatModel.java
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/model/NanoGptChatModel.java
@@ -96,6 +96,42 @@ protected void addProviderSpecificParams(Map body) {
}
}
+ public Double getMinP() {
+ return minP;
+ }
+
+ public Integer getMinTokens() {
+ return minTokens;
+ }
+
+ public Integer getMirostatMode() {
+ return mirostatMode;
+ }
+
+ public Double getMirostatTau() {
+ return mirostatTau;
+ }
+
+ public Double getMirostatEta() {
+ return mirostatEta;
+ }
+
+ public Double getRepetitionPenalty() {
+ return repetitionPenalty;
+ }
+
+ public Double getTfs() {
+ return tfs;
+ }
+
+ public Double getTopA() {
+ return topA;
+ }
+
+ public Double getTypicalP() {
+ return typicalP;
+ }
+
public static class Builder extends RouterChatModel.Builder {
private Double minP;
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/model/NanoGptImageModel.java b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/model/NanoGptImageModel.java
index eedda3ff7e4..d446cda6cde 100644
--- a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/model/NanoGptImageModel.java
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/java/com/bytechef/component/ai/llm/router/nano/gpt/model/NanoGptImageModel.java
@@ -49,6 +49,42 @@ public class NanoGptImageModel implements ImageModel {
private final Double strength;
private final Integer numInferenceSteps;
+ public String getModel() {
+ return model;
+ }
+
+ public String getSize() {
+ return size;
+ }
+
+ public String getResponseFormat() {
+ return responseFormat;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public Integer getN() {
+ return n;
+ }
+
+ public Integer getSeed() {
+ return seed;
+ }
+
+ public Double getGuidanceScale() {
+ return guidanceScale;
+ }
+
+ public Double getStrength() {
+ return strength;
+ }
+
+ public Integer getNumInferenceSteps() {
+ return numInferenceSteps;
+ }
+
private NanoGptImageModel(Builder builder) {
this.restClient = ModelUtils.getRestClientBuilder()
.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + builder.apiKey)
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/resources/connection.mdx b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/resources/connection.mdx
new file mode 100644
index 00000000000..ea0a91a9379
--- /dev/null
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/main/resources/connection.mdx
@@ -0,0 +1,15 @@
+## Connection Setup
+
+1. Navigate to [NanoGPT console](https://nano-gpt.com/conversation/new).
+2. Click on **API**.
+3. Click on **Create API Key**.
+4. Enter name of your new key.
+5. Click on **Create**.
+6. Here is your new API key.
+7. Done 🚀.
+
+
+
+
+
+
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptChatActionTest.java b/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptChatActionTest.java
new file mode 100644
index 00000000000..09717ecbe2b
--- /dev/null
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptChatActionTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2025 ByteChef
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.bytechef.component.ai.llm.router.nano.gpt.action;
+
+import static com.bytechef.component.ai.llm.constant.LLMConstants.FREQUENCY_PENALTY;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.LOGIT_BIAS;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.MAX_TOKENS;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.MODEL;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.PRESENCE_PENALTY;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.REASONING;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.SEED;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.STOP;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.TEMPERATURE;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.TOP_K;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.TOP_P;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.USER;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.VERBOSITY;
+import static com.bytechef.component.ai.llm.router.constant.RouterConstants.LOGPROBS;
+import static com.bytechef.component.ai.llm.router.constant.RouterConstants.MAX_COMPLETION_TOKENS;
+import static com.bytechef.component.ai.llm.router.constant.RouterConstants.TOP_LOGPROBS;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.MIN_P;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.MIN_TOKENS;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.MIROSTAT_ETA;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.MIROSTAT_MODE;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.MIROSTAT_TAU;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.REPETITION_PENALTY;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.TFS;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.TOP_A;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.TYPICAL_P;
+import static com.bytechef.component.definition.Authorization.TOKEN;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import com.bytechef.component.ai.llm.router.nano.gpt.model.NanoGptChatModel;
+import com.bytechef.component.definition.Parameters;
+import com.bytechef.component.test.definition.MockParametersFactory;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Nikolina Spehar
+ */
+class NanoGptChatActionTest {
+
+ private final Parameters mockedParameters = MockParametersFactory.create(
+ Map.ofEntries(
+ Map.entry(TOKEN, "token"), Map.entry(MODEL, "model"),
+ Map.entry(FREQUENCY_PENALTY, 1.0), Map.entry(LOGIT_BIAS, Map.of()),
+ Map.entry(LOGPROBS, false), Map.entry(MAX_COMPLETION_TOKENS, 1),
+ Map.entry(MAX_TOKENS, 1), Map.entry(PRESENCE_PENALTY, 0.0),
+ Map.entry(REASONING, "reasoning"), Map.entry(SEED, 4),
+ Map.entry(STOP, List.of()), Map.entry(TEMPERATURE, 0.0),
+ Map.entry(TOP_K, 0.0), Map.entry(TOP_LOGPROBS, 1), Map.entry(TOP_P, 0.0),
+ Map.entry(VERBOSITY, "verbosity"), Map.entry(USER, "user"),
+ Map.entry(MIN_P, 0.0), Map.entry(MIN_TOKENS, 1), Map.entry(MIROSTAT_MODE, 1),
+ Map.entry(MIROSTAT_TAU, 0.0), Map.entry(MIROSTAT_ETA, 0.0), Map.entry(REPETITION_PENALTY, 0.0),
+ Map.entry(TFS, 0.0), Map.entry(TOP_A, 0.0), Map.entry(TYPICAL_P, 0.0)));
+
+ @Test
+ void testPerform() {
+ NanoGptChatModel nanoGptChatModel = (NanoGptChatModel) NanoGptChatAction.CHAT_MODEL.createChatModel(
+ mockedParameters, mockedParameters, false);
+
+ assertNotNull(nanoGptChatModel);
+
+ assertEquals("model", nanoGptChatModel.getModel());
+ assertEquals(1.0, nanoGptChatModel.getFrequencyPenalty());
+ assertEquals(Map.of(), nanoGptChatModel.getLogitBias());
+ assertEquals(false, nanoGptChatModel.getLogprobs());
+ assertEquals(1, nanoGptChatModel.getMaxCompletionTokens());
+ assertEquals(1, nanoGptChatModel.getMaxTokens());
+ assertEquals(0.0, nanoGptChatModel.getPresencePenalty());
+ assertEquals("reasoning", nanoGptChatModel.getReasoning());
+ assertEquals(4, nanoGptChatModel.getSeed());
+ assertEquals(List.of(), nanoGptChatModel.getStop());
+ assertEquals(0.0, nanoGptChatModel.getTemperature());
+ assertEquals(0.0, nanoGptChatModel.getTopK());
+ assertEquals(1, nanoGptChatModel.getTopLogprobs());
+ assertEquals(0.0, nanoGptChatModel.getTopP());
+ assertEquals("verbosity", nanoGptChatModel.getVerbosity());
+ assertEquals("user", nanoGptChatModel.getUser());
+ assertEquals(0.0, nanoGptChatModel.getMinP());
+ assertEquals(1, nanoGptChatModel.getMinTokens());
+ assertEquals(1, nanoGptChatModel.getMirostatMode());
+ assertEquals(0.0, nanoGptChatModel.getMirostatTau());
+ assertEquals(0.0, nanoGptChatModel.getMirostatEta());
+ assertEquals(0.0, nanoGptChatModel.getRepetitionPenalty());
+ assertEquals(0.0, nanoGptChatModel.getTfs());
+ assertEquals(0.0, nanoGptChatModel.getTopA());
+ assertEquals(0.0, nanoGptChatModel.getTypicalP());
+ }
+}
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateImageActionTest.java b/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateImageActionTest.java
new file mode 100644
index 00000000000..a2e1fb8e576
--- /dev/null
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateImageActionTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2025 ByteChef
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.bytechef.component.ai.llm.router.nano.gpt.action;
+
+import static com.bytechef.component.ai.llm.constant.LLMConstants.MODEL;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.N;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.RESPONSE_FORMAT;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.SEED;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.SIZE;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.USER;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.GUIDANCE_SCALE;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.NUM_INFERENCE_STEPS;
+import static com.bytechef.component.ai.llm.router.nano.gpt.constant.NanoGptConstants.STRENGTH;
+import static com.bytechef.component.definition.Authorization.TOKEN;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import com.bytechef.component.ai.llm.router.nano.gpt.model.NanoGptImageModel;
+import com.bytechef.component.definition.Parameters;
+import com.bytechef.component.test.definition.MockParametersFactory;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Nikolina Spehar
+ */
+class NanoGptCreateImageActionTest {
+
+ private final Parameters mockedParameters = MockParametersFactory.create(
+ Map.ofEntries(Map.entry(MODEL, "model"), Map.entry(SIZE, "1024x1024"),
+ Map.entry(RESPONSE_FORMAT, "url"), Map.entry(N, 1), Map.entry(SEED, 1),
+ Map.entry(GUIDANCE_SCALE, 0.0), Map.entry(USER, "user"), Map.entry(STRENGTH, 0.0),
+ Map.entry(NUM_INFERENCE_STEPS, 1)));
+
+ private final Parameters mockedConnectionParameters = MockParametersFactory.create(Map.of(TOKEN, "token"));
+
+ @Test
+ void testCreateImageModel() {
+ NanoGptImageModel nanoGptImageModel = (NanoGptImageModel) NanoGptCreateImageAction.IMAGE_MODEL.createImageModel(
+ mockedParameters, mockedConnectionParameters);
+
+ assertNotNull(nanoGptImageModel);
+
+ assertEquals("model", nanoGptImageModel.getModel());
+ assertEquals("1024x1024", nanoGptImageModel.getSize());
+ assertEquals("url", nanoGptImageModel.getResponseFormat());
+ assertEquals(1, nanoGptImageModel.getN());
+ assertEquals(1, nanoGptImageModel.getSeed());
+ assertEquals(0.0, nanoGptImageModel.getGuidanceScale());
+ assertEquals(0.0, nanoGptImageModel.getStrength());
+ assertEquals(1, nanoGptImageModel.getNumInferenceSteps());
+ assertEquals("user", nanoGptImageModel.getUser());
+ }
+}
diff --git a/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateSpeechActionTest.java b/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateSpeechActionTest.java
new file mode 100644
index 00000000000..86707392ad7
--- /dev/null
+++ b/server/libs/modules/components/ai/llm/router/nano-gpt/src/test/java/com/bytechef/component/ai/llm/router/nano/gpt/action/NanoGptCreateSpeechActionTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2025 ByteChef
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.bytechef.component.ai.llm.router.nano.gpt.action;
+
+import static com.bytechef.component.ai.llm.constant.LLMConstants.INPUT;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.MODEL;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.RESPONSE_FORMAT;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.SPEED;
+import static com.bytechef.component.ai.llm.constant.LLMConstants.VOICE;
+import static com.bytechef.component.definition.Authorization.TOKEN;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentCaptor.forClass;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.bytechef.component.ai.llm.util.ModelUtils;
+import com.bytechef.component.definition.ActionContext;
+import com.bytechef.component.definition.Context.ContextFunction;
+import com.bytechef.component.definition.Context.File;
+import com.bytechef.component.definition.Context.Http.Executor;
+import com.bytechef.component.definition.FileEntry;
+import com.bytechef.component.definition.Parameters;
+import com.bytechef.component.test.definition.MockParametersFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.client.RestClient.RequestBodySpec;
+import org.springframework.web.client.RestClient.RequestBodyUriSpec;
+
+/**
+ * @author Nikolina Spehar
+ */
+class NanoGptCreateSpeechActionTest {
+
+ @SuppressWarnings("unchecked")
+ private final ArgumentCaptor> fileFunctionArgumentCaptor =
+ forClass(ContextFunction.class);
+ private final ArgumentCaptor inputStreamArgumentCaptor = forClass(InputStream.class);
+ private final ArgumentCaptor mediaTypeArgumentCaptor = forClass(MediaType.class);
+ private final byte[] mockedAudioBytes = new byte[] {
+ (byte) 0xFF, (byte) 0xFB, 0x10, 0x00
+ };
+ private final RestClient.Builder mockedBuilder = mock(RestClient.Builder.class);
+ private final ActionContext mockedContext = mock(ActionContext.class);
+ private final File mockedFile = mock(File.class);
+ private final FileEntry mockedFileEntry = mock(FileEntry.class);
+ private final Parameters mockedParameters = MockParametersFactory.create(
+ Map.of(
+ INPUT, "Hello world", MODEL, "tts-model", RESPONSE_FORMAT, "mp3", VOICE, "alloy",
+ SPEED, 1.0, TOKEN, "test-api-key"));
+ private final RequestBodySpec mockedRequestBodySpec = mock(RequestBodySpec.class);
+ private final RequestBodyUriSpec mockedRequestBodyUriSpec = mock(RequestBodyUriSpec.class);
+ private final RestClient mockedRestClient = mock(RestClient.class);
+ private final ArgumentCaptor