diff --git a/README.md b/README.md index 4c24824..8079eeb 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ try { ### Features - [ListModels](guides/Features.md#listmodels) +- [RetrieveModel](guides/Features.md#retrieveModel) - [Completions](guides/Features.md#completion) - [ChatCompletions](guides/Features.md#chatcompletions) - [ImageGenerations](guides/Features.md#imagegenerations) diff --git a/guides/Features.md b/guides/Features.md index 00476f3..408d63c 100644 --- a/guides/Features.md +++ b/guides/Features.md @@ -1,6 +1,7 @@ # Features - [ListModels](#listModels) +- [RetrieveModel](#retrieveModel) - [Completion](#completion) - [ChatCompletions](#chatcompletions) - [ImageGenerations](#imagegenerations) @@ -39,6 +40,39 @@ try { } ``` +## RetrieveModel + +The retrieveModel api retrieve the currently model based on the given id, and provides basic information about it such as the owner and availability. + +### Swift + +```swift +var yChat: YChat { + YChatCompanion.shared.create(apiKey: "your-api-key") +} + +do { + let result = try await yChat.retrieveModel().execute(id: "babbage") +} catch { + // catch any error that may occurs on api call. +} +``` + +### Kotlin + +```kotlin +val yChat by lazy { + YChat.create("your-api-key") +} + +try { + val result = yChat.retrieveModel().execute("babbage") + +} catch (e: exception) { + // catch any error that may occurs on api call. +} +``` + ## Completion The completions api can be used for a wide variety of tasks. You input some text as a prompt, and the model will generate a text completion that attempts to match whatever context or pattern you gave it. For example, if you give the API the prompt, "As Descartes said, I think, therefore", it will return the completion " I am" with high probability. @@ -51,7 +85,7 @@ var yChat: YChat { } do { - let result = try await chatGpt.completion() + let result = try await yChat.completion() .setInput(input: "Say this is a test.") .setMaxTokens(tokens: 1024) .set... // you can set more parameters @@ -74,7 +108,6 @@ try { .setMaxTokens(1024) .set... // you can set more parameters .execute() - } catch (e: exception) { // catch any error that may occurs on api call. } @@ -92,7 +125,7 @@ var yChat: YChat { } do { - let result = try await chatGpt.chatCompletions() + let result = try await yChat.chatCompletions() .setMaxTokens(tokens: 1024) .addMessage( role: "assistant", @@ -121,7 +154,6 @@ try { ) .set... // you can set more parameters .execute("What is the best exercise for building muscle?") - } catch (e: exception) { // catch any error that may occurs on api call. } @@ -139,7 +171,7 @@ var yChat: YChat { } do { - let result = try await chatGpt.imageGenerations() + let result = try await yChat.imageGenerations() .setResults(results: 2) .setSize(size: "1024x1024") .set... // you can set more parameters @@ -162,7 +194,6 @@ try { .setSize("1024x1024") .set... // you can set more parameters .execute("ocean") - } catch (e: exception) { // catch any error that may occurs on api call. } @@ -180,7 +211,7 @@ var yChat: YChat { } do { - let result = try await chatGpt.edits() + let result = try await yChat.edits() .setInput(input: "What day of the wek is it?") .setResults(result: 1) .set... // you can set more parameters @@ -203,7 +234,6 @@ try { .setResults(1) .set... // you can set more parameters .execute("Fix the spelling mistakes") - } catch (e: exception) { // catch any error that may occurs on api call. } diff --git a/sample/jvm/README.md b/sample/jvm/README.md index ca1da33..78f1585 100644 --- a/sample/jvm/README.md +++ b/sample/jvm/README.md @@ -81,4 +81,14 @@ This endpoint retrieve a list of currently available artificial intelligence mod ##### Example: -`GET http://localhost:8080/api/ychat/models` \ No newline at end of file +`GET http://localhost:8080/api/ychat/models` + +### Model Endpoint + +This endpoint retrieve the artificial intelligence model based on the given ID. + +##### Endpoint: http://localhost:[port_number]/api/ychat/models/{modelID} + +##### Example: + +`GET http://localhost:8080/api/ychat/models/babbage` \ No newline at end of file diff --git a/sample/jvm/src/main/java/co/yml/ychat/jvm/controller/YChatController.java b/sample/jvm/src/main/java/co/yml/ychat/jvm/controller/YChatController.java index d6c3df8..aa5c416 100644 --- a/sample/jvm/src/main/java/co/yml/ychat/jvm/controller/YChatController.java +++ b/sample/jvm/src/main/java/co/yml/ychat/jvm/controller/YChatController.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -57,6 +58,12 @@ public ResponseEntity> models() throws Exception { return ResponseEntity.ok(result); } + @GetMapping("models/{id}") + public ResponseEntity model(@PathVariable String id) throws Exception { + AIModel result = YChatService.getModel(id); + return ResponseEntity.ok(result); + } + private static class Defaults { static final String COMPLETION_INPUT = "Say this is a test."; static final String CHAT_COMPLETION_INPUT = "Tell me one strength exercise"; diff --git a/sample/jvm/src/main/java/co/yml/ychat/jvm/services/YChatService.java b/sample/jvm/src/main/java/co/yml/ychat/jvm/services/YChatService.java index 5eaff67..63c6807 100644 --- a/sample/jvm/src/main/java/co/yml/ychat/jvm/services/YChatService.java +++ b/sample/jvm/src/main/java/co/yml/ychat/jvm/services/YChatService.java @@ -1,13 +1,13 @@ package co.yml.ychat.jvm.services; +import co.yml.ychat.YChat; import co.yml.ychat.domain.model.AIModel; import co.yml.ychat.domain.model.ChatMessage; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.concurrent.CompletableFuture; -import co.yml.ychat.YChat; @Service public class YChatService { @@ -58,6 +58,13 @@ public List getModels() throws Exception { return future.get(); } + public AIModel getModel(String id) throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + ychat.retrieveModel() + .execute(id, new CompletionCallbackResult<>(future)); + return future.get(); + } + private static class CompletionCallbackResult implements YChat.Callback { private final CompletableFuture future; diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/YChat.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/YChat.kt index a9d2010..6f4e639 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/YChat.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/YChat.kt @@ -5,6 +5,7 @@ import co.yml.ychat.entrypoint.features.Completion import co.yml.ychat.entrypoint.features.Edits import co.yml.ychat.entrypoint.features.ImageGenerations import co.yml.ychat.entrypoint.features.ListModels +import co.yml.ychat.entrypoint.features.RetrieveModel import co.yml.ychat.entrypoint.impl.YChatImpl import kotlin.jvm.JvmStatic import kotlin.jvm.Volatile @@ -32,6 +33,19 @@ interface YChat { */ fun listModels(): ListModels + /** + * The retrieveModel api retrieve the currently model based on the given id, and provides basic + * information about it such as the owner and availability. + * + * Example usage: + * ``` + * val result = YChat.create(apiKey) + * .retrieveModel() + * .execute("gpt-3") + * ``` + */ + fun retrieveModel(): RetrieveModel + /** * The completions api can be used for a wide variety of tasks. You input some text as a * prompt, and the model will generate a text completion that attempts to match whatever diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/data/api/ChatGptApi.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/data/api/ChatGptApi.kt index 89bf0df..3747b5b 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/data/api/ChatGptApi.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/data/api/ChatGptApi.kt @@ -8,6 +8,7 @@ import co.yml.ychat.data.dto.EditsDto import co.yml.ychat.data.dto.EditsParamsDto import co.yml.ychat.data.dto.ImageGenerationsDto import co.yml.ychat.data.dto.ImageGenerationsParamsDto +import co.yml.ychat.data.dto.ModelDto import co.yml.ychat.data.dto.ModelListDto import co.yml.ychat.data.infrastructure.ApiResult @@ -22,4 +23,6 @@ internal interface ChatGptApi { suspend fun edits(paramsDto: EditsParamsDto): ApiResult suspend fun models(): ApiResult + + suspend fun model(id: String): ApiResult } diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/data/api/impl/ChatGptApiImpl.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/data/api/impl/ChatGptApiImpl.kt index aa37dbc..c42355f 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/data/api/impl/ChatGptApiImpl.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/data/api/impl/ChatGptApiImpl.kt @@ -9,6 +9,7 @@ import co.yml.ychat.data.dto.EditsDto import co.yml.ychat.data.dto.EditsParamsDto import co.yml.ychat.data.dto.ImageGenerationsDto import co.yml.ychat.data.dto.ImageGenerationsParamsDto +import co.yml.ychat.data.dto.ModelDto import co.yml.ychat.data.dto.ModelListDto import co.yml.ychat.data.infrastructure.ApiExecutor import co.yml.ychat.data.infrastructure.ApiResult @@ -54,4 +55,11 @@ internal class ChatGptApiImpl(private val apiExecutor: ApiExecutor) : ChatGptApi .setHttpMethod(HttpMethod.Get) .execute() } + + override suspend fun model(id: String): ApiResult { + return apiExecutor + .setEndpoint("v1/models/$id") + .setHttpMethod(HttpMethod.Get) + .execute() + } } diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/di/module/LibraryModule.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/di/module/LibraryModule.kt index 0cf2867..8a4eeb5 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/di/module/LibraryModule.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/di/module/LibraryModule.kt @@ -10,16 +10,19 @@ import co.yml.ychat.domain.usecases.CompletionUseCase import co.yml.ychat.domain.usecases.EditsUseCase import co.yml.ychat.domain.usecases.ImageGenerationsUseCase import co.yml.ychat.domain.usecases.ListModelsUseCase +import co.yml.ychat.domain.usecases.RetrieveModelUseCase import co.yml.ychat.entrypoint.features.ChatCompletions import co.yml.ychat.entrypoint.features.Completion import co.yml.ychat.entrypoint.features.Edits import co.yml.ychat.entrypoint.features.ImageGenerations import co.yml.ychat.entrypoint.features.ListModels +import co.yml.ychat.entrypoint.features.RetrieveModel import co.yml.ychat.entrypoint.impl.ChatCompletionsImpl import co.yml.ychat.entrypoint.impl.CompletionImpl import co.yml.ychat.entrypoint.impl.EditsImpl import co.yml.ychat.entrypoint.impl.ImageGenerationsImpl import co.yml.ychat.entrypoint.impl.ListModelsImpl +import co.yml.ychat.entrypoint.impl.RetrieveModelImpl import kotlinx.coroutines.Dispatchers import org.koin.core.module.Module import org.koin.dsl.module @@ -31,6 +34,7 @@ internal class LibraryModule(private val apiKey: String) { private val entrypointModule = module { factory { ListModelsImpl(Dispatchers.Default, get()) } + factory { RetrieveModelImpl(Dispatchers.Default, get()) } factory { CompletionImpl(Dispatchers.Default, get()) } factory { ChatCompletionsImpl(Dispatchers.Default, get()) } factory { ImageGenerationsImpl(Dispatchers.Default, get()) } @@ -39,6 +43,7 @@ internal class LibraryModule(private val apiKey: String) { private val domainModule = module { factory { ListModelsUseCase(get()) } + factory { RetrieveModelUseCase(get()) } factory { CompletionUseCase(get(), get()) } factory { ChatCompletionsUseCase(get()) } factory { ImageGenerationsUseCase(get()) } diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ChatCompletionsUseCase.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ChatCompletionsUseCase.kt index e27f943..2e32fe3 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ChatCompletionsUseCase.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ChatCompletionsUseCase.kt @@ -6,7 +6,7 @@ import co.yml.ychat.domain.mapper.toChatMessages import co.yml.ychat.domain.model.ChatCompletionsParams import co.yml.ychat.domain.model.ChatMessage -internal data class ChatCompletionsUseCase(private val chatGptApi: ChatGptApi) { +internal class ChatCompletionsUseCase(private val chatGptApi: ChatGptApi) { suspend fun requestChatCompletions(params: ChatCompletionsParams): List { val requestDto = params.toChatCompletionParamsDto() diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/EditsUseCase.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/EditsUseCase.kt index 68e62de..028a760 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/EditsUseCase.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/EditsUseCase.kt @@ -5,7 +5,7 @@ import co.yml.ychat.domain.mapper.toEditsModel import co.yml.ychat.domain.mapper.toEditsParamsDto import co.yml.ychat.domain.model.EditsParams -internal data class EditsUseCase(private val chatGptApi: ChatGptApi) { +internal class EditsUseCase(private val chatGptApi: ChatGptApi) { suspend fun requestEdits(params: EditsParams): List { val requestDto = params.toEditsParamsDto() diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ImageGenerationsUseCase.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ImageGenerationsUseCase.kt index f0aa77f..f7cf67f 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ImageGenerationsUseCase.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ImageGenerationsUseCase.kt @@ -5,7 +5,7 @@ import co.yml.ychat.domain.mapper.toImageGenerated import co.yml.ychat.domain.mapper.toImageGenerationsParamsDto import co.yml.ychat.domain.model.ImageGenerationsParams -internal data class ImageGenerationsUseCase(private val chatGptApi: ChatGptApi) { +internal class ImageGenerationsUseCase(private val chatGptApi: ChatGptApi) { suspend fun requestImageGenerations(params: ImageGenerationsParams): List { val requestDto = params.toImageGenerationsParamsDto() diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ListModelsUseCase.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ListModelsUseCase.kt index bffbf40..37c489c 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ListModelsUseCase.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/ListModelsUseCase.kt @@ -4,7 +4,7 @@ import co.yml.ychat.data.api.ChatGptApi import co.yml.ychat.domain.mapper.toModel import co.yml.ychat.domain.model.AIModel -internal data class ListModelsUseCase(private val chatGptApi: ChatGptApi) { +internal class ListModelsUseCase(private val chatGptApi: ChatGptApi) { suspend fun getListModels(): List { val response = chatGptApi.models() diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/RetrieveModelUseCase.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/RetrieveModelUseCase.kt new file mode 100644 index 0000000..68a7e8e --- /dev/null +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/domain/usecases/RetrieveModelUseCase.kt @@ -0,0 +1,13 @@ +package co.yml.ychat.domain.usecases + +import co.yml.ychat.data.api.ChatGptApi +import co.yml.ychat.domain.mapper.toModel +import co.yml.ychat.domain.model.AIModel + +internal class RetrieveModelUseCase(private val chatGptApi: ChatGptApi) { + + suspend fun getModel(id: String): AIModel { + val response = chatGptApi.model(id) + return response.getBodyOrThrow().toModel() + } +} diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/features/RetrieveModel.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/features/RetrieveModel.kt new file mode 100644 index 0000000..282bb83 --- /dev/null +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/features/RetrieveModel.kt @@ -0,0 +1,29 @@ +package co.yml.ychat.entrypoint.features + +import co.yml.ychat.YChat +import co.yml.ychat.data.exception.ChatGptException +import co.yml.ychat.domain.model.AIModel +import kotlin.coroutines.cancellation.CancellationException + +interface RetrieveModel { + + /** + * Retrieves an artificial intelligence model based on the given [id] and provides basic + * information about it, such as the owner and availability. + * + * @param id The ID of the model to retrieve. + * @return The artificial intelligence model with the given [id]. + * @throws CancellationException if the operation is cancelled. + * @throws ChatGptException if there is an error generating chat completions. + */ + @Throws(CancellationException::class, ChatGptException::class) + suspend fun execute(id: String): AIModel + + /** + * Retrieves an artificial intelligence model based on the given [id], and passes it to + * the provided callback. + * + * @param callback The callback to receive the model. + */ + fun execute(id: String, callback: YChat.Callback) +} diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/impl/RetrieveModelImpl.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/impl/RetrieveModelImpl.kt new file mode 100644 index 0000000..1d722d2 --- /dev/null +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/impl/RetrieveModelImpl.kt @@ -0,0 +1,30 @@ +package co.yml.ychat.entrypoint.impl + +import co.yml.ychat.YChat +import co.yml.ychat.domain.model.AIModel +import co.yml.ychat.domain.usecases.RetrieveModelUseCase +import co.yml.ychat.entrypoint.features.RetrieveModel +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + +internal class RetrieveModelImpl( + private val dispatcher: CoroutineDispatcher, + private val retrieveModelUseCase: RetrieveModelUseCase, +) : RetrieveModel { + + private val scope by lazy { CoroutineScope(SupervisorJob() + dispatcher) } + + override suspend fun execute(id: String): AIModel { + return retrieveModelUseCase.getModel(id) + } + + override fun execute(id: String, callback: YChat.Callback) { + scope.launch { + runCatching { execute(id) } + .onSuccess { callback.onSuccess(it) } + .onFailure { callback.onError(it) } + } + } +} diff --git a/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/impl/YChatImpl.kt b/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/impl/YChatImpl.kt index 94fd515..965e90a 100644 --- a/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/impl/YChatImpl.kt +++ b/ychat/src/commonMain/kotlin/co/yml/ychat/entrypoint/impl/YChatImpl.kt @@ -7,6 +7,7 @@ import co.yml.ychat.entrypoint.features.Completion import co.yml.ychat.entrypoint.features.Edits import co.yml.ychat.entrypoint.features.ImageGenerations import co.yml.ychat.entrypoint.features.ListModels +import co.yml.ychat.entrypoint.features.RetrieveModel import org.koin.core.KoinApplication internal class YChatImpl(apiKey: String) : YChat { @@ -22,6 +23,10 @@ internal class YChatImpl(apiKey: String) : YChat { return koinApp.koin.get() } + override fun retrieveModel(): RetrieveModel { + return koinApp.koin.get() + } + override fun completion(): Completion { return koinApp.koin.get() } diff --git a/ychat/src/commonTest/kotlin/co/yml/ychat/domain/usecases/RetrieveModelUseCaseTest.kt b/ychat/src/commonTest/kotlin/co/yml/ychat/domain/usecases/RetrieveModelUseCaseTest.kt new file mode 100644 index 0000000..d25e8bc --- /dev/null +++ b/ychat/src/commonTest/kotlin/co/yml/ychat/domain/usecases/RetrieveModelUseCaseTest.kt @@ -0,0 +1,56 @@ +package co.yml.ychat.domain.usecases + +import co.yml.ychat.data.api.ChatGptApi +import co.yml.ychat.data.dto.ModelDto +import co.yml.ychat.data.exception.ChatGptException +import co.yml.ychat.data.infrastructure.ApiResult +import io.mockk.coEvery +import io.mockk.mockk +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.runBlocking + +class RetrieveModelUseCaseTest { + + private lateinit var useCase: RetrieveModelUseCase + + private val chatGptApiMock = mockk() + + @BeforeTest + fun setup() { + useCase = RetrieveModelUseCase(chatGptApiMock) + } + + @Test + fun `on getModel when request succeed then should return formatted result`() { + // arrange + val expectedId = "modelId" + val modelListDto = buildModelDto(expectedId) + val apiResult = ApiResult(body = modelListDto) + coEvery { chatGptApiMock.model(expectedId) } returns apiResult + + // act + val result = runBlocking { useCase.getModel(expectedId) } + + // assert + assertEquals(expectedId, result.id) + } + + @Test + fun `on getModel when not request succeed then should throw an exception`() { + // arrange + val apiResult = ApiResult(exception = ChatGptException()) + coEvery { chatGptApiMock.model("1") } returns apiResult + + // act + val result = runCatching { runBlocking { useCase.getModel("1") } } + + // assert + assertEquals(true, result.exceptionOrNull() is ChatGptException) + } + + private fun buildModelDto(id: String): ModelDto { + return ModelDto(id = id, ownedBy = "", emptyList()) + } +} diff --git a/ychat/src/commonTest/kotlin/co/yml/ychat/entrypoint/YChatTest.kt b/ychat/src/commonTest/kotlin/co/yml/ychat/entrypoint/YChatTest.kt index bd1a692..35d5c2c 100644 --- a/ychat/src/commonTest/kotlin/co/yml/ychat/entrypoint/YChatTest.kt +++ b/ychat/src/commonTest/kotlin/co/yml/ychat/entrypoint/YChatTest.kt @@ -129,6 +129,20 @@ class YChatTest { assertEquals(expectedResult, result.first().id) } + @Test + fun `on retrieveModel execute method should return result successfully`() { + // arrange + val expectedResult = "model1" + val modelSuccessResult = MockStorage.modelSuccessResult(expectedResult) + mockHttpEngine(modelSuccessResult) + + // act + val result = runBlocking { yChat.retrieveModel().execute(expectedResult) } + + // assert + assertEquals(expectedResult, result.id) + } + private fun mockHttpEngine(result: String) { val httpEngine = MockEngine { respond( diff --git a/ychat/src/commonTest/kotlin/infrastructure/MockStorage.kt b/ychat/src/commonTest/kotlin/infrastructure/MockStorage.kt index 21b968f..62b9c65 100644 --- a/ychat/src/commonTest/kotlin/infrastructure/MockStorage.kt +++ b/ychat/src/commonTest/kotlin/infrastructure/MockStorage.kt @@ -2,31 +2,137 @@ package infrastructure object MockStorage { - fun completionSuccessResult(text: String) = "{\"id\":\"1\",\"object\":\"text_completion\"," + - "\"created\":1675532539,\"model\":\"text-davinci-003\"," + - "\"choices\":[{\"text\":\"\n\n$text\",\"index\":0,\"logprobs\":null," + - "\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":8,\"completion_tokens\":9," + - "\"total_tokens\":17}}" + fun completionSuccessResult(text: String) = """ + { + "id": "1", + "object": "text_completion", + "created": 1675532539, + "model": "text-davinci-003", + "choices": [ + { + "text": "\n\n$text", + "index": 0, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 8, + "completion_tokens": 9, + "total_tokens": 17 + } + } + """ - fun chatCompletionsSuccessResult(text: String) = "{\"id\":\"1\",\"object\":\"chat.completion\"," + - "\"created\":1678144798,\"model\":\"gpt-3.5-turbo-0301\"," + - "\"usage\":{\"prompt_tokens\":13,\"completion_tokens\":12,\"total_tokens\":25}," + - "\"choices\":[{\"message\":{\"role\":\"assistant\",\"content\":\"$text\"}," + - "\"finish_reason\":\"stop\",\"index\":0}]}" + fun chatCompletionsSuccessResult(text: String) = """ + { + "id": "1", + "object": "chat.completion", + "created": 1678144798, + "model": "gpt-3.5-turbo-0301", + "usage": { + "prompt_tokens": 13, + "completion_tokens": 12, + "total_tokens": 25 + }, + "choices": [ + { + "message": { + "role": "assistant", + "content": "$text" + }, + "finish_reason": "stop", + "index": 0 + } + ] + } + """ - fun imageGenerationsSuccessResult(text: String) = - "{\"created\":1678805561,\"data\":[{\"url\":\"$text\"}]}" + fun imageGenerationsSuccessResult(text: String) = """ + { + "created": 1678805561, + "data": [ + { + "url": "$text" + } + ] + } + """ - fun editsSuccessResult(text: String) = "{\"object\":\"edit\",\"created\":1679072839," + - "\"choices\":[{\"text\":\"$text\",\"index\":0}]," + - "\"usage\":{\"prompt_tokens\":25,\"completion_tokens\":28,\"total_tokens\":53}}" + fun editsSuccessResult(text: String) = """ + { + "object": "edit", + "created": 1679072839, + "choices": [ + { + "text": "$text", + "index": 0 + } + ], + "usage": { + "prompt_tokens": 25, + "completion_tokens": 28, + "total_tokens": 53 + } + } + """ - fun listModelsSuccessResult(id: String) = - "{\"object\":\"list\",\"data\":[{\"id\":\"$id\",\"object\":\"model\"," + - "\"created\":1649358449,\"owned_by\":\"openai\",\"permission\":" + - "[{\"id\":\"modelperm-49FUp5v084tBB49tC4z8LPH5\",\"object\":\"model_permission\"," + - "\"created\":1669085501,\"allow_create_engine\":false,\"allow_sampling\":true," + - "\"allow_logprobs\":true,\"allow_search_indices\":false,\"allow_view\":true," + - "\"allow_fine_tuning\":false,\"organization\":\"*\",\"group\":null,\"is_blocking" + - "\":false}],\"root\":\"$id\",\"parent\":null}]}" + fun listModelsSuccessResult(id: String) = """ + { + "object": "list", + "data": [ + { + "id": "$id", + "object": "model", + "created": 1649358449, + "owned_by": "openai", + "permission": [ + { + "id": "modelperm-49FUp5v084tBB49tC4z8LPH5", + "object": "model_permission", + "created": 1669085501, + "allow_create_engine": false, + "allow_sampling": true, + "allow_logprobs": true, + "allow_search_indices": false, + "allow_view": true, + "allow_fine_tuning": false, + "organization": "*", + "group": null, + "is_blocking": false + } + ], + "root": "$id", + "parent": null + } + ] + } + """ + + fun modelSuccessResult(id: String) = """ + { + "id": "$id", + "object": "model", + "created": 1649358449, + "owned_by": "openai", + "permission": [ + { + "id": "modelperm-49FUp5v084tBB49tC4z8LPH5", + "object": "model_permission", + "created": 1669085501, + "allow_create_engine": false, + "allow_sampling": true, + "allow_logprobs": true, + "allow_search_indices": false, + "allow_view": true, + "allow_fine_tuning": false, + "organization": "*", + "group": null, + "is_blocking": false + } + ], + "root": "$id", + "parent": null + } + """ }