Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add ChatCompletions API #47

Merged
merged 4 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions sample/android/src/main/java/co/yml/ychat/android/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ import kotlinx.coroutines.launch

class MainViewModel(private val chatGpt: YChat) : ViewModel() {

private val chatCompletions by lazy {
chatGpt.chatCompletions()
.setMaxTokens(MAX_TOKENS)
.addMessage(
role = "assistant",
content = "You are a helpful assistant."
)
}

private val _items = mutableStateListOf<MessageItem>()
val items = _items

Expand Down Expand Up @@ -47,11 +56,7 @@ class MainViewModel(private val chatGpt: YChat) : ViewModel() {

private suspend fun requestCompletion(message: String): String {
return try {
chatGpt.completion()
.setInput(message)
.saveHistory(false)
.setMaxTokens(MAX_TOKENS)
.execute()
chatCompletions.execute(message).last().content
} catch (e: Exception) {
e.message ?: ERROR
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ import YChat
import Foundation

internal final class CompletionViewModel: ObservableObject {
private var chatGpt: YChat {
private var chatCompletions: ChatCompletions =
YChatCompanion.shared.create(apiKey: Config.apiKey)
}
.chatCompletions()
.setMaxTokens(tokens: 1024)
.addMessage(
role: "assistant",
content: "You are a helpful assistant."
)

@Published
var message: String = ""
Expand All @@ -32,11 +37,7 @@ internal final class CompletionViewModel: ObservableObject {
cleanLastMessage()
addLoading()
do {
let result = try await chatGpt.completion()
.setInput(input: input)
.setMaxTokens(tokens: 1024)
.saveHistory(isSaveHistory: false)
.execute()
let result = try await chatCompletions.execute(content: input)[0].content
removeLoading()
addAIMessage(message: result)
} catch {
Expand Down
17 changes: 16 additions & 1 deletion sample/jvm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,23 @@ This endpoint generates text based on the provided prompt.

##### Parameters:

- input: The prompt for generating text.
- `input`: The prompt for generating text.

##### Example:

`GET http://localhost:8080/api/ychat/completion?input="What is 1 + 1?"`

### Chat Completions Endpoint

This endpoint generates text based on the provided prompt and a specified topic. The generated text will be related to the topic provided.

##### Endpoint: http://localhost:[port_number]/api/ychat/chat-completions

##### Parameters:

- `input`: The prompt for generating text.
- `topic`: The topic to limit the response to.

##### Example:

`GET http://localhost:8080/api/ychat/chat-completions?input="Tell me an exercise plan"&topic=fitness`
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,35 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import co.yml.ychat.jvm.services.CompletionService;
import co.yml.ychat.jvm.services.YChatService;

@RestController
@RequestMapping("api/ychat")
public class YChatController {

@Autowired
private CompletionService completionService;
private YChatService YChatService;

@GetMapping("completion")
public ResponseEntity<String> completion(
@RequestParam(value = "input", defaultValue = Defaults.COMPLETION_INPUT) String input
) throws Exception {
String result = completionService.getCompletionAnswer(input);
String result = YChatService.getCompletionAnswer(input);
return ResponseEntity.ok(result);
}

@GetMapping("chat-completions")
public ResponseEntity<String> chatCompletions(
@RequestParam(value = "input", defaultValue = Defaults.CHAT_COMPLETION_INPUT) String input,
@RequestParam(value = "topic", defaultValue = Defaults.CHAT_COMPLETION_TOPIC) String topic
) throws Exception {
String result = YChatService.getChatCompletionsAnswer(input, topic);
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";
static final String CHAT_COMPLETION_TOPIC = "fitness";
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package co.yml.ychat.jvm.services;

import co.yml.ychat.domain.model.ChatMessage;
import java.util.List;
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 {

@Autowired
private YChat ychat;

private static final int MAX_TOKENS = 512;

public String getCompletionAnswer(String input) throws Exception {
final CompletableFuture<String> future = new CompletableFuture<>();
ychat.completion()
.setMaxTokens(MAX_TOKENS)
.setInput(input)
.execute(new CompletionCallbackResult<>(future));
return future.get();
}

public String getChatCompletionsAnswer(String input, String topic) throws Exception {
final CompletableFuture<List<ChatMessage>> future = new CompletableFuture<>();
String content = "You are a helpful assistant the only answer questions related to " + topic;
ychat.chatCompletions()
.setMaxTokens(MAX_TOKENS)
.addMessage("system", content)
.execute(input, new CompletionCallbackResult<>(future));
return future.get().get(0).getContent();
}

private static class CompletionCallbackResult<T> implements YChat.Callback<T> {

private final CompletableFuture<T> future;

CompletionCallbackResult(CompletableFuture<T> future) {
this.future = future;
}

@Override
public void onSuccess(T result) {
future.complete(result);
}

@Override
public void onError(@NotNull Throwable throwable) {
future.completeExceptionally(throwable);
}
}
}
45 changes: 45 additions & 0 deletions ychat/src/commonMain/kotlin/co/yml/ychat/YChat.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package co.yml.ychat

import co.yml.ychat.entrypoint.features.ChatCompletions
import co.yml.ychat.entrypoint.features.Completion
import co.yml.ychat.entrypoint.impl.YChatImpl
import kotlin.jvm.JvmStatic
Expand Down Expand Up @@ -32,6 +33,50 @@ interface YChat {
*/
fun completion(): Completion

/**
* The chatCompletions api generates a list of chat completions for the given input message.
* It uses machine learning algorithms to generate responses that match the context or pattern
* provided in the input message.
*
* You can configure various parameters to customize the chat completions, such as the model to use,
* the number of results to generate, and the maximum number of tokens allowed for the generated answer.
*
* Example usage:
* ```
* val result = YChat.create(apiKey).chatCompletions()
* .setModel("gpt-3.5-turbo")
* .setResults(3)
* .setMaxTokens(1024)
* .execute("Hello, how are you?")
* ```
* This would generate three chat completion strings based on the input message "Hello, how are you?"
* using the "gpt-3.5-turbo" model, with a maximum of 1024 tokens allowed for each generated answer.
*
* You can also use the `addMessage` method to provide additional context or information to the API,
* which can be used to restrict the generated responses to a certain topic or context.
*
* Example usage:
* ```
* val result = YChat.create(apiKey).chatCompletions()
* .setModel("gpt-3.5-turbo")
* .setResults(3)
* .setMaxTokens(1024)
* .addMessage(
* role = "assistant",
* content = "You are a helpful assistant that only answers questions related to fitness"
* )
* .execute("What is the best exercise for building muscle?")
* ```
* This would generate three chat completion strings based on the input message "What is the
* best exercise for building muscle?", using the "gpt-3.5-turbo" model, with a maximum of 1024
* tokens allowed for each generated answer. The `addMessage` method is used to provide context
* to the API, restricting the generated responses to questions related to fitness, since the
* assistant is set to only answer questions related to that topic.
*
* @return A new instance of the `ChatCompletions` class.
*/
fun chatCompletions(): ChatCompletions

/**
* Callback is an interface used for handling the results of an operation.
* It provides two methods, `onSuccess` and `onError`, for handling the success
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package co.yml.ychat.data.api

import co.yml.ychat.data.dto.ChatCompletionParamsDto
import co.yml.ychat.data.dto.ChatCompletionsDto
import co.yml.ychat.data.dto.CompletionDto
import co.yml.ychat.data.dto.CompletionParamsDto
import co.yml.ychat.data.infrastructure.ApiResult

internal interface ChatGptApi {

suspend fun completion(paramsDto: CompletionParamsDto): ApiResult<CompletionDto>

suspend fun chatCompletions(paramsDto: ChatCompletionParamsDto): ApiResult<ChatCompletionsDto>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package co.yml.ychat.data.api.impl

import co.yml.ychat.data.api.ChatGptApi
import co.yml.ychat.data.dto.ChatCompletionParamsDto
import co.yml.ychat.data.dto.ChatCompletionsDto
import co.yml.ychat.data.dto.CompletionDto
import co.yml.ychat.data.dto.CompletionParamsDto
import co.yml.ychat.data.infrastructure.ApiExecutor
Expand All @@ -16,4 +18,12 @@ internal class ChatGptApiImpl(private val apiExecutor: ApiExecutor) : ChatGptApi
.setBody(paramsDto)
.execute()
}

override suspend fun chatCompletions(paramsDto: ChatCompletionParamsDto): ApiResult<ChatCompletionsDto> {
return apiExecutor
.setEndpoint("v1/chat/completions")
.setHttpMethod(HttpMethod.Post)
.setBody(paramsDto)
.execute()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package co.yml.ychat.data.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
internal data class ChatCompletionParamsDto(
@SerialName("model")
val model: String,
@SerialName("messages")
val messages: List<ChatMessageDto>,
@SerialName("max_tokens")
val maxTokens: Int,
@SerialName("temperature")
val temperature: Double,
@SerialName("top_p")
val topP: Double,
@SerialName("n")
val maxResults: Int = 1,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package co.yml.ychat.data.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
internal data class ChatCompletionsChoiceDto(
@SerialName("index")
val index: Int,
@SerialName("message")
val message: ChatMessageDto,
@SerialName("finish_reason")
val finishReason: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package co.yml.ychat.data.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
internal data class ChatCompletionsDto(
@SerialName("id")
val id: String,
@SerialName("model")
val model: String,
@SerialName("choices")
val choices: List<ChatCompletionsChoiceDto>,
@SerialName("usage")
val usage: UsageDto,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package co.yml.ychat.data.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
internal data class ChatMessageDto(
@SerialName("role")
val role: String,
@SerialName("content")
val content: String,
)
Loading