Skip to content

Commit d34eb8f

Browse files
authored
Merge pull request #47 from yml-org/feature/CM-1219/chat-completions
feat: Add ChatCompletions API
2 parents 91a1cea + d58093b commit d34eb8f

File tree

28 files changed

+594
-68
lines changed

28 files changed

+594
-68
lines changed

sample/android/src/main/java/co/yml/ychat/android/MainViewModel.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ import kotlinx.coroutines.launch
1111

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

14+
private val chatCompletions by lazy {
15+
chatGpt.chatCompletions()
16+
.setMaxTokens(MAX_TOKENS)
17+
.addMessage(
18+
role = "assistant",
19+
content = "You are a helpful assistant."
20+
)
21+
}
22+
1423
private val _items = mutableStateListOf<MessageItem>()
1524
val items = _items
1625

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

4857
private suspend fun requestCompletion(message: String): String {
4958
return try {
50-
chatGpt.completion()
51-
.setInput(message)
52-
.saveHistory(false)
53-
.setMaxTokens(MAX_TOKENS)
54-
.execute()
59+
chatCompletions.execute(message).last().content
5560
} catch (e: Exception) {
5661
e.message ?: ERROR
5762
}

sample/ios/YChatApp/Features/Completion/ViewModel/CompletionViewModel.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@ import YChat
1010
import Foundation
1111

1212
internal final class CompletionViewModel: ObservableObject {
13-
private var chatGpt: YChat {
13+
private var chatCompletions: ChatCompletions =
1414
YChatCompanion.shared.create(apiKey: Config.apiKey)
15-
}
15+
.chatCompletions()
16+
.setMaxTokens(tokens: 1024)
17+
.addMessage(
18+
role: "assistant",
19+
content: "You are a helpful assistant."
20+
)
1621

1722
@Published
1823
var message: String = ""
@@ -32,11 +37,7 @@ internal final class CompletionViewModel: ObservableObject {
3237
cleanLastMessage()
3338
addLoading()
3439
do {
35-
let result = try await chatGpt.completion()
36-
.setInput(input: input)
37-
.setMaxTokens(tokens: 1024)
38-
.saveHistory(isSaveHistory: false)
39-
.execute()
40+
let result = try await chatCompletions.execute(content: input)[0].content
4041
removeLoading()
4142
addAIMessage(message: result)
4243
} catch {

sample/jvm/README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,23 @@ This endpoint generates text based on the provided prompt.
2323

2424
##### Parameters:
2525

26-
- input: The prompt for generating text.
26+
- `input`: The prompt for generating text.
2727

2828
##### Example:
2929

3030
`GET http://localhost:8080/api/ychat/completion?input="What is 1 + 1?"`
31+
32+
### Chat Completions Endpoint
33+
34+
This endpoint generates text based on the provided prompt and a specified topic. The generated text will be related to the topic provided.
35+
36+
##### Endpoint: http://localhost:[port_number]/api/ychat/chat-completions
37+
38+
##### Parameters:
39+
40+
- `input`: The prompt for generating text.
41+
- `topic`: The topic to limit the response to.
42+
43+
##### Example:
44+
45+
`GET http://localhost:8080/api/ychat/chat-completions?input="Tell me an exercise plan"&topic=fitness`

sample/jvm/src/main/java/co/yml/ychat/jvm/controller/YChatController.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,35 @@
66
import org.springframework.web.bind.annotation.RequestMapping;
77
import org.springframework.web.bind.annotation.RequestParam;
88
import org.springframework.web.bind.annotation.RestController;
9-
import co.yml.ychat.jvm.services.CompletionService;
9+
import co.yml.ychat.jvm.services.YChatService;
1010

1111
@RestController
1212
@RequestMapping("api/ychat")
1313
public class YChatController {
1414

1515
@Autowired
16-
private CompletionService completionService;
16+
private YChatService YChatService;
1717

1818
@GetMapping("completion")
1919
public ResponseEntity<String> completion(
2020
@RequestParam(value = "input", defaultValue = Defaults.COMPLETION_INPUT) String input
2121
) throws Exception {
22-
String result = completionService.getCompletionAnswer(input);
22+
String result = YChatService.getCompletionAnswer(input);
23+
return ResponseEntity.ok(result);
24+
}
25+
26+
@GetMapping("chat-completions")
27+
public ResponseEntity<String> chatCompletions(
28+
@RequestParam(value = "input", defaultValue = Defaults.CHAT_COMPLETION_INPUT) String input,
29+
@RequestParam(value = "topic", defaultValue = Defaults.CHAT_COMPLETION_TOPIC) String topic
30+
) throws Exception {
31+
String result = YChatService.getChatCompletionsAnswer(input, topic);
2332
return ResponseEntity.ok(result);
2433
}
2534

2635
private static class Defaults {
2736
static final String COMPLETION_INPUT = "Say this is a test.";
37+
static final String CHAT_COMPLETION_INPUT = "Tell me one strength exercise";
38+
static final String CHAT_COMPLETION_TOPIC = "fitness";
2839
}
2940
}

sample/jvm/src/main/java/co/yml/ychat/jvm/services/CompletionService.java

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package co.yml.ychat.jvm.services;
2+
3+
import co.yml.ychat.domain.model.ChatMessage;
4+
import java.util.List;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.stereotype.Service;
8+
import java.util.concurrent.CompletableFuture;
9+
import co.yml.ychat.YChat;
10+
11+
@Service
12+
public class YChatService {
13+
14+
@Autowired
15+
private YChat ychat;
16+
17+
private static final int MAX_TOKENS = 512;
18+
19+
public String getCompletionAnswer(String input) throws Exception {
20+
final CompletableFuture<String> future = new CompletableFuture<>();
21+
ychat.completion()
22+
.setMaxTokens(MAX_TOKENS)
23+
.setInput(input)
24+
.execute(new CompletionCallbackResult<>(future));
25+
return future.get();
26+
}
27+
28+
public String getChatCompletionsAnswer(String input, String topic) throws Exception {
29+
final CompletableFuture<List<ChatMessage>> future = new CompletableFuture<>();
30+
String content = "You are a helpful assistant the only answer questions related to " + topic;
31+
ychat.chatCompletions()
32+
.setMaxTokens(MAX_TOKENS)
33+
.addMessage("system", content)
34+
.execute(input, new CompletionCallbackResult<>(future));
35+
return future.get().get(0).getContent();
36+
}
37+
38+
private static class CompletionCallbackResult<T> implements YChat.Callback<T> {
39+
40+
private final CompletableFuture<T> future;
41+
42+
CompletionCallbackResult(CompletableFuture<T> future) {
43+
this.future = future;
44+
}
45+
46+
@Override
47+
public void onSuccess(T result) {
48+
future.complete(result);
49+
}
50+
51+
@Override
52+
public void onError(@NotNull Throwable throwable) {
53+
future.completeExceptionally(throwable);
54+
}
55+
}
56+
}

ychat/src/commonMain/kotlin/co/yml/ychat/YChat.kt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package co.yml.ychat
22

3+
import co.yml.ychat.entrypoint.features.ChatCompletions
34
import co.yml.ychat.entrypoint.features.Completion
45
import co.yml.ychat.entrypoint.impl.YChatImpl
56
import kotlin.jvm.JvmStatic
@@ -32,6 +33,50 @@ interface YChat {
3233
*/
3334
fun completion(): Completion
3435

36+
/**
37+
* The chatCompletions api generates a list of chat completions for the given input message.
38+
* It uses machine learning algorithms to generate responses that match the context or pattern
39+
* provided in the input message.
40+
*
41+
* You can configure various parameters to customize the chat completions, such as the model to use,
42+
* the number of results to generate, and the maximum number of tokens allowed for the generated answer.
43+
*
44+
* Example usage:
45+
* ```
46+
* val result = YChat.create(apiKey).chatCompletions()
47+
* .setModel("gpt-3.5-turbo")
48+
* .setResults(3)
49+
* .setMaxTokens(1024)
50+
* .execute("Hello, how are you?")
51+
* ```
52+
* This would generate three chat completion strings based on the input message "Hello, how are you?"
53+
* using the "gpt-3.5-turbo" model, with a maximum of 1024 tokens allowed for each generated answer.
54+
*
55+
* You can also use the `addMessage` method to provide additional context or information to the API,
56+
* which can be used to restrict the generated responses to a certain topic or context.
57+
*
58+
* Example usage:
59+
* ```
60+
* val result = YChat.create(apiKey).chatCompletions()
61+
* .setModel("gpt-3.5-turbo")
62+
* .setResults(3)
63+
* .setMaxTokens(1024)
64+
* .addMessage(
65+
* role = "assistant",
66+
* content = "You are a helpful assistant that only answers questions related to fitness"
67+
* )
68+
* .execute("What is the best exercise for building muscle?")
69+
* ```
70+
* This would generate three chat completion strings based on the input message "What is the
71+
* best exercise for building muscle?", using the "gpt-3.5-turbo" model, with a maximum of 1024
72+
* tokens allowed for each generated answer. The `addMessage` method is used to provide context
73+
* to the API, restricting the generated responses to questions related to fitness, since the
74+
* assistant is set to only answer questions related to that topic.
75+
*
76+
* @return A new instance of the `ChatCompletions` class.
77+
*/
78+
fun chatCompletions(): ChatCompletions
79+
3580
/**
3681
* Callback is an interface used for handling the results of an operation.
3782
* It provides two methods, `onSuccess` and `onError`, for handling the success
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package co.yml.ychat.data.api
22

3+
import co.yml.ychat.data.dto.ChatCompletionParamsDto
4+
import co.yml.ychat.data.dto.ChatCompletionsDto
35
import co.yml.ychat.data.dto.CompletionDto
46
import co.yml.ychat.data.dto.CompletionParamsDto
57
import co.yml.ychat.data.infrastructure.ApiResult
68

79
internal interface ChatGptApi {
810

911
suspend fun completion(paramsDto: CompletionParamsDto): ApiResult<CompletionDto>
12+
13+
suspend fun chatCompletions(paramsDto: ChatCompletionParamsDto): ApiResult<ChatCompletionsDto>
1014
}

ychat/src/commonMain/kotlin/co/yml/ychat/data/api/impl/ChatGptApiImpl.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package co.yml.ychat.data.api.impl
22

33
import co.yml.ychat.data.api.ChatGptApi
4+
import co.yml.ychat.data.dto.ChatCompletionParamsDto
5+
import co.yml.ychat.data.dto.ChatCompletionsDto
46
import co.yml.ychat.data.dto.CompletionDto
57
import co.yml.ychat.data.dto.CompletionParamsDto
68
import co.yml.ychat.data.infrastructure.ApiExecutor
@@ -16,4 +18,12 @@ internal class ChatGptApiImpl(private val apiExecutor: ApiExecutor) : ChatGptApi
1618
.setBody(paramsDto)
1719
.execute()
1820
}
21+
22+
override suspend fun chatCompletions(paramsDto: ChatCompletionParamsDto): ApiResult<ChatCompletionsDto> {
23+
return apiExecutor
24+
.setEndpoint("v1/chat/completions")
25+
.setHttpMethod(HttpMethod.Post)
26+
.setBody(paramsDto)
27+
.execute()
28+
}
1929
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package co.yml.ychat.data.dto
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
internal data class ChatCompletionParamsDto(
8+
@SerialName("model")
9+
val model: String,
10+
@SerialName("messages")
11+
val messages: List<ChatMessageDto>,
12+
@SerialName("max_tokens")
13+
val maxTokens: Int,
14+
@SerialName("temperature")
15+
val temperature: Double,
16+
@SerialName("top_p")
17+
val topP: Double,
18+
@SerialName("n")
19+
val maxResults: Int = 1,
20+
)

0 commit comments

Comments
 (0)