From 7fc81472a1328538e11b632adcd8ddaa0c88ed76 Mon Sep 17 00:00:00 2001 From: David Denton Date: Sat, 7 Sep 2024 17:01:08 +0100 Subject: [PATCH] Refine Azure API --- CHANGELOG.md | 3 +- ai/azure/README.md | 2 +- .../connect/azure/action/ChatCompletion.kt | 36 +++++++++++++++++-- .../http4k/connect/azure/AzureAIContract.kt | 11 +++--- .../kotlin/interacting-with-azureai.kt | 4 +-- .../src/examples/kotlin/using the fake.kt | 2 +- .../examples/kotlin/using_connect_client.kt | 2 +- .../connect/azure/ChatCompletionGenerator.kt | 6 ++-- 8 files changed, 46 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 854e5126a..0dde62397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ changes with their rationale when appropriate. Given version `A.B.C.D`, breaking ### v5.24.0.0 - **http4k-connect-*** - Upgrade dependencies including Kotlin to 2.0.20 -- **http4k-connect-ai-openai*** - [Breaking] tightened up types for completion requests. +- **http4k-connect-ai-openai*** - [Breaking] Tightened up types for completion requests. +- **http4k-connect-ai-azure*** - [Breaking] Tightened up types for completion requests. ### v5.23.0.0 - **http4k-connect-*** - Upgrade dependencies including Kotlin to 2.0.20 diff --git a/ai/azure/README.md b/ai/azure/README.md index e69a8e90a..8d9f989b3 100644 --- a/ai/azure/README.md +++ b/ai/azure/README.md @@ -42,7 +42,7 @@ val client = AzureAI.Http( // all operations return a Result monad of the API type val result: Result, RemoteFailure> = client - .chatCompletion(ModelName.of("Meta-Llama-3.1-70B-Instruct"), listOf(Message(User, "good afternoon")), 1000, true) + .chatCompletion(ModelName.of("Meta-Llama-3.1-70B-Instruct"), listOf(Message.User("good afternoon"))), 1000, true) println(result.orThrow().toList()) } diff --git a/ai/azure/client/src/main/kotlin/org/http4k/connect/azure/action/ChatCompletion.kt b/ai/azure/client/src/main/kotlin/org/http4k/connect/azure/action/ChatCompletion.kt index a52bbeca6..698470759 100644 --- a/ai/azure/client/src/main/kotlin/org/http4k/connect/azure/action/ChatCompletion.kt +++ b/ai/azure/client/src/main/kotlin/org/http4k/connect/azure/action/ChatCompletion.kt @@ -4,12 +4,12 @@ package org.http4k.connect.azure.action import org.http4k.connect.Http4kConnectAction import org.http4k.connect.azure.AzureAIMoshi.autoBody -import org.http4k.connect.model.ModelName import org.http4k.connect.azure.CompletionId import org.http4k.connect.azure.ObjectType import org.http4k.connect.azure.User import org.http4k.connect.azure.action.Detail.auto import org.http4k.connect.model.FinishReason +import org.http4k.connect.model.ModelName import org.http4k.connect.model.Role import org.http4k.connect.model.Timestamp import org.http4k.core.Method @@ -52,6 +52,14 @@ data class ChatCompletion( tool_choice = null ) + constructor(model: ModelName, message: Message, max_tokens: Int = 16, stream: Boolean = true) : this( + model, + listOf(message), + max_tokens, + stream = stream, + tool_choice = null + ) + init { require(tools == null || tools.isNotEmpty()) { "Tools cannot be empty" } } @@ -79,11 +87,12 @@ sealed class ResponseFormat { @JsonSerializable data class Message( - val role: Role?, - val content: List, + val role: Role, + val content: List?, val name: User? = null, val tool_calls: List? = null ) { + @Deprecated("Use companion functions") constructor( role: Role, text: String, @@ -91,6 +100,27 @@ data class Message( tool_calls: List? = null ) : this(role, listOf(MessageContent(ContentType.text, text)), name, tool_calls) + + companion object { + fun User(content: String, name: User? = null) = User(listOf(MessageContent(ContentType.text, content)), name) + fun User(content: List, name: User? = null) = Message(Role.User, content, name, null) + + fun System(content: String, name: User? = null) = + System(listOf(MessageContent(ContentType.text, content)), name) + + fun System(content: List, name: User? = null) = Message(Role.System, content, name) + + fun Assistant(content: String, name: User? = null) = + Assistant(listOf(MessageContent(ContentType.text, content)), name) + + fun Assistant(content: List, name: User? = null) = + Message(Role.Assistant, content, name) + + @JvmName("AssistantToolCalls") + fun Assistant(tool_calls: List, name: User? = null) = + Message(Role.Assistant, null, name, tool_calls) + } + } @JsonSerializable diff --git a/ai/azure/client/src/test/kotlin/org/http4k/connect/azure/AzureAIContract.kt b/ai/azure/client/src/test/kotlin/org/http4k/connect/azure/AzureAIContract.kt index 2588dcb0c..23227e3e9 100644 --- a/ai/azure/client/src/test/kotlin/org/http4k/connect/azure/AzureAIContract.kt +++ b/ai/azure/client/src/test/kotlin/org/http4k/connect/azure/AzureAIContract.kt @@ -35,8 +35,8 @@ interface AzureAIContract { val responses = azureAi.chatCompletion( ModelName.of("Meta-Llama-3-8B-Instruct"), listOf( - Message(System, "You are Leonardo Da Vinci"), - Message(User, "What is your favourite colour?") + Message.System("You are Leonardo Da Vinci"), + Message.User("What is your favourite colour?") ), 1000, stream = false @@ -63,11 +63,8 @@ interface AzureAIContract { val responses = azureAi.chatCompletion( ModelName.of("Meta-Llama-3-8B-Instruct"), listOf( - Message(System, "You are Leonardo Da Vinci"), - Message( - User, - "What is your favourite colour?" - ) + Message.System("You are Leonardo Da Vinci"), + Message.User("What is your favourite colour?") ), 1000, stream = true diff --git a/ai/azure/fake/src/examples/kotlin/interacting-with-azureai.kt b/ai/azure/fake/src/examples/kotlin/interacting-with-azureai.kt index 8dbf306f5..5d1d84550 100644 --- a/ai/azure/fake/src/examples/kotlin/interacting-with-azureai.kt +++ b/ai/azure/fake/src/examples/kotlin/interacting-with-azureai.kt @@ -23,9 +23,7 @@ fun main() { println( azureAi.chatCompletion( ModelName.of("Meta-Llama-3.1-70B-Instruct"), - listOf( - Message(User, "Explain pythagoras's theorem to a 5 year old child"), - ), + Message.User("Explain pythagoras's theorem to a 5 year old child"), 1000, false ) diff --git a/ai/azure/fake/src/examples/kotlin/using the fake.kt b/ai/azure/fake/src/examples/kotlin/using the fake.kt index 879453fd3..4e144fafb 100644 --- a/ai/azure/fake/src/examples/kotlin/using the fake.kt +++ b/ai/azure/fake/src/examples/kotlin/using the fake.kt @@ -33,7 +33,7 @@ fun main() { azureAi .chatCompletion( ModelName.of("Meta-Llama-3.1-70B-Instruct"), - listOf(Message(User, "good afternoon")), 1000, true + listOf(Message.User("good afternoon")), 1000, true ) .onFailure { error(it) } .toList() diff --git a/ai/azure/fake/src/examples/kotlin/using_connect_client.kt b/ai/azure/fake/src/examples/kotlin/using_connect_client.kt index 088d6b1d0..b87cbd939 100644 --- a/ai/azure/fake/src/examples/kotlin/using_connect_client.kt +++ b/ai/azure/fake/src/examples/kotlin/using_connect_client.kt @@ -29,7 +29,7 @@ fun main() { // all operations return a Result monad of the API type val result: Result, RemoteFailure> = client - .chatCompletion(ModelName.of("Meta-Llama-3.1-70B-Instruct"), listOf(Message(User, "good afternoon")), 1000, true) + .chatCompletion(ModelName.of("Meta-Llama-3.1-70B-Instruct"), listOf(Message.User("good afternoon")), 1000, true) println(result.orThrow().toList()) } diff --git a/ai/azure/fake/src/main/kotlin/org/http4k/connect/azure/ChatCompletionGenerator.kt b/ai/azure/fake/src/main/kotlin/org/http4k/connect/azure/ChatCompletionGenerator.kt index ee60ef0a2..90edff2a4 100644 --- a/ai/azure/fake/src/main/kotlin/org/http4k/connect/azure/ChatCompletionGenerator.kt +++ b/ai/azure/fake/src/main/kotlin/org/http4k/connect/azure/ChatCompletionGenerator.kt @@ -23,9 +23,9 @@ fun interface ChatCompletionGenerator : (ModelCompletion) -> List { val ChatCompletionGenerator.Companion.ReverseInput get() = ChatCompletionGenerator { req -> req.content().flatMap { m -> - m.content.mapIndexed { i, content -> + m.content?.mapIndexed { i, content -> Choice(i, ChoiceDetail(Role.System, content.text?.reversed() ?: "", null), null, FinishReason.stop) - } + } ?: emptyList() } } @@ -41,7 +41,7 @@ fun ChatCompletionGenerator.Companion.LoremIpsum(random: Random = Random(0)) = C */ val ChatCompletionGenerator.Companion.Echo get() = ChatCompletionGenerator { req -> - req.choices(req.content().first { it.role == User }.content.first().text ?: "") + req.choices(req.content().first { it.role == User }.content?.first()?.text ?: "") } private fun ModelCompletion.choices(msg: String) = (if (stream) msg.split(" ").map { "$it " } else listOf(msg))