From 568a549fdd24ca8781035ea5c89785fce11e4a75 Mon Sep 17 00:00:00 2001 From: raulraja Date: Sun, 13 Aug 2023 22:53:14 +0200 Subject: [PATCH 1/2] simplify conversations and their JVM integrations --- core/build.gradle.kts | 5 + .../xebia/functional/xef/auto/Conversation.kt | 126 ++++++++++-- .../xef/auto/PlatformConversation.kt | 17 ++ .../com/xebia/functional/xef/llm/Chat.kt | 42 ++-- .../functional/xef/auto/JSConversation.kt | 18 ++ .../xef/auto/PlatformConversation.js.kt | 12 ++ .../xebia/functional/xef/agents/BingSearch.kt | 43 ---- .../functional/xef/agents/DefaultSearch.kt | 12 -- .../functional/xef/agents/ScrapeUrlContent.kt | 7 - .../com/xebia/functional/xef/auto/FromJson.kt | 5 + .../functional/xef/auto/JVMConversation.kt | 14 ++ .../xef/auto/PlatformConversation.jvm.kt | 185 ++++++++++++++++++ .../xef/auto/futures/ExecutionContext.kt | 49 +++++ .../serialization/JacksonSerialization.kt | 30 +++ .../functional/xef/auto/NativeConversation.kt | 18 ++ .../xef/auto/PlatformConversation.native.kt | 13 ++ .../xef/java/auto/jdk21/ASCIIArt.java | 10 +- .../xef/java/auto/jdk21/Animals.java | 18 +- .../functional/xef/java/auto/jdk21/Book.java | 10 +- .../functional/xef/java/auto/jdk21/Books.java | 14 +- .../xef/java/auto/jdk21/BreakingNews.java | 14 +- .../xef/java/auto/jdk21/ChessAI.java | 13 +- .../xef/java/auto/jdk21/Colors.java | 10 +- .../xef/java/auto/jdk21/DivergentTasks.java | 16 +- .../xef/java/auto/jdk21/Employees.java | 10 +- .../functional/xef/java/auto/jdk21/Fact.java | 14 +- .../functional/xef/java/auto/jdk21/Love.java | 10 +- .../xef/java/auto/jdk21/Markets.java | 15 +- .../xef/java/auto/jdk21/MealPlan.java | 16 +- .../xef/java/auto/jdk21/MeaningOfLife.java | 10 +- .../xef/java/auto/jdk21/Movies.java | 10 +- .../xef/java/auto/jdk21/PDFDocument.java | 23 ++- .../xef/java/auto/jdk21/Persons.java | 10 +- .../xef/java/auto/jdk21/Planets.java | 12 +- .../functional/xef/java/auto/jdk21/Poems.java | 16 +- .../xef/java/auto/jdk21/Populations.java | 14 +- .../xef/java/auto/jdk21/Recipes.java | 10 +- .../xef/java/auto/jdk21/TopAttractions.java | 10 +- .../java/auto/jdk21/TouristAttractions.java | 10 +- .../xef/java/auto/jdk21/Weather.java | 15 +- .../xef/java/auto/jdk21/gpt4all/Chat.java | 15 +- .../java/auto/jdk21/sql/DatabaseExample.java | 92 --------- .../java/auto/jdk21/tot/ControlSignals.java | 4 +- .../xef/java/auto/jdk21/tot/Critiques.java | 4 +- .../xef/java/auto/jdk21/tot/Problems.java | 21 +- .../xef/java/auto/jdk21/tot/Solutions.java | 4 +- .../xef/java/auto/jdk8/ASCIIArt.java | 8 +- .../xef/java/auto/jdk8/Animals.java | 16 +- .../functional/xef/java/auto/jdk8/Book.java | 8 +- .../functional/xef/java/auto/jdk8/Books.java | 11 +- .../xef/java/auto/jdk8/BreakingNews.java | 14 +- .../xef/java/auto/jdk8/ChessAI.java | 39 ++-- .../functional/xef/java/auto/jdk8/Colors.java | 9 +- .../xef/java/auto/jdk8/DivergentTasks.java | 14 +- .../xef/java/auto/jdk8/Employee.java | 8 +- .../functional/xef/java/auto/jdk8/Fact.java | 12 +- .../functional/xef/java/auto/jdk8/Love.java | 8 +- .../xef/java/auto/jdk8/Markets.java | 15 +- .../xef/java/auto/jdk8/MealPlan.java | 13 +- .../xef/java/auto/jdk8/MeaningOfLife.java | 8 +- .../functional/xef/java/auto/jdk8/Movies.java | 8 +- .../xef/java/auto/jdk8/PDFDocument.java | 17 +- .../xef/java/auto/jdk8/Persons.java | 8 +- .../xef/java/auto/jdk8/Planets.java | 10 +- .../functional/xef/java/auto/jdk8/Poems.java | 14 +- .../xef/java/auto/jdk8/Populations.java | 12 +- .../xef/java/auto/jdk8/Recipes.java | 8 +- .../xef/java/auto/jdk8/TopAttractions.java | 8 +- .../java/auto/jdk8/TouristAttractions.java | 8 +- .../xef/java/auto/jdk8/Weather.java | 14 +- .../xef/java/auto/jdk8/gpt4all/Chat.java | 10 +- .../java/auto/jdk8/sql/DatabaseExample.java | 92 --------- .../java/auto/jdk8/tot/ControlSignals.java | 4 +- .../xef/java/auto/jdk8/tot/Critiques.java | 4 +- .../xef/java/auto/jdk8/tot/Problems.java | 18 +- .../xef/java/auto/jdk8/tot/Solutions.java | 4 +- .../com/xebia/functional/xef/auto/ASCIIArt.kt | 4 +- .../com/xebia/functional/xef/auto/Animal.kt | 4 +- .../com/xebia/functional/xef/auto/Book.kt | 4 +- .../xebia/functional/xef/auto/BreakingNews.kt | 10 +- .../com/xebia/functional/xef/auto/ChessAI.kt | 4 +- .../com/xebia/functional/xef/auto/Colors.kt | 4 +- .../xebia/functional/xef/auto/Conversation.kt | 4 +- .../functional/xef/auto/DivergentTasks.kt | 7 +- .../com/xebia/functional/xef/auto/Employee.kt | 4 +- .../com/xebia/functional/xef/auto/Fact.kt | 4 +- .../com/xebia/functional/xef/auto/Love.kt | 4 +- .../com/xebia/functional/xef/auto/Markets.kt | 7 +- .../com/xebia/functional/xef/auto/MealPlan.kt | 7 +- .../functional/xef/auto/MeaningOfLife.kt | 4 +- .../com/xebia/functional/xef/auto/Movie.kt | 4 +- .../xebia/functional/xef/auto/PDFDocument.kt | 19 +- .../com/xebia/functional/xef/auto/Person.kt | 4 +- .../com/xebia/functional/xef/auto/Planet.kt | 21 +- .../com/xebia/functional/xef/auto/Poem.kt | 25 +-- .../xebia/functional/xef/auto/Population.kt | 11 +- .../com/xebia/functional/xef/auto/Recipe.kt | 11 +- .../functional/xef/auto/TopAttraction.kt | 17 +- .../functional/xef/auto/TouristAttraction.kt | 17 +- .../com/xebia/functional/xef/auto/Weather.kt | 14 +- .../auto/expressions/WorkoutPlanProgram.kt | 5 +- .../xebia/functional/xef/auto/fields/Book.kt | 4 +- .../functional/xef/auto/fields/NewsSummary.kt | 7 +- .../com/xebia/functional/xef/auto/gpc/Chat.kt | 4 +- .../xebia/functional/xef/auto/gpt4all/Chat.kt | 6 +- .../xef/auto/memory/ChatWithMemory.kt | 4 +- .../xef/auto/prompts/ExpertSystemExample.kt | 21 +- .../auto/prompts/PromptEvaluationExample.kt | 4 +- .../xef/auto/reasoning/CodeExample.kt | 4 +- .../xef/auto/reasoning/ReActExample.kt | 5 +- .../xef/auto/reasoning/SerpApiExample.kt | 29 ++- .../xef/auto/reasoning/TextExample.kt | 4 +- .../auto/reasoning/ToolSelectionExample.kt | 4 +- .../WikipediaSearchByParamsExample.kt | 3 +- .../auto/reasoning/WikipediaSearchExample.kt | 3 +- .../xef/auto/sql/DatabaseExample.kt | 62 +++--- .../com/xebia/functional/xef/auto/tot/Main.kt | 30 +-- examples/scala/build.gradle.kts | 4 +- .../functional/xef/scala/auto/Book.scala | 2 +- .../xef/scala/auto/BreakingNews.scala | 6 +- .../functional/xef/scala/auto/ChessAI.scala | 2 +- .../xef/scala/auto/DivergentTasks.scala | 6 +- .../functional/xef/scala/auto/Markets.scala | 6 +- .../functional/xef/scala/auto/MealPlan.scala | 6 +- .../xef/scala/auto/PDFDocument.scala | 5 +- .../xef/scala/auto/Population.scala | 5 +- .../functional/xef/scala/auto/Weather.scala | 12 +- .../com/xebia/functional/gpt4all/GPT4All.kt | 22 +++ gradle/libs.versions.toml | 4 +- .../functional/xef/java/auto/AIDatabase.java | 38 ---- .../functional/xef/java/auto/AIScope.java | 151 -------------- .../xebia/functional/xef/java/auto/Empty.java | 7 + .../xef/java/auto/ExecutionContext.java | 82 -------- .../xef/auto/llm/openai/Conversation.kt | 10 - .../functional/xef/auto/llm/openai/OpenAI.kt | 28 +++ .../xef/auto/llm/openai/OpenAIConversation.kt | 8 + .../xef/reasoning/serpapi/Search.kt | 9 + .../xef/reasoning/serpapi/SearchTool.kt} | 32 +-- .../xef/reasoning/serpapi/Search.js.kt | 17 ++ .../xef/reasoning/pdf/ReadPDFFromUrl.kt | 11 ++ .../xef/reasoning/serpapi/Search.jvm.kt | 30 +++ .../reasoning/serpapi/Search.macosArm64.kt | 17 ++ .../xef/reasoning/serpapi/Search.native.kt | 17 ++ scala/build.gradle.kts | 1 + .../xef/scala/agents/DefaultSearch.scala | 9 - .../functional/xef/scala/auto/package.scala | 142 +++++++------- 146 files changed, 1369 insertions(+), 1201 deletions(-) create mode 100644 core/src/commonMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.kt create mode 100644 core/src/jsMain/kotlin/com/xebia/functional/xef/auto/JSConversation.kt create mode 100644 core/src/jsMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.js.kt delete mode 100644 core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/BingSearch.kt delete mode 100644 core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/DefaultSearch.kt delete mode 100644 core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/ScrapeUrlContent.kt create mode 100644 core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/FromJson.kt create mode 100644 core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/JVMConversation.kt create mode 100644 core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.jvm.kt create mode 100644 core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/futures/ExecutionContext.kt create mode 100644 core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/serialization/JacksonSerialization.kt create mode 100644 core/src/nativeMain/kotlin/com/xebia/functional/xef/auto/NativeConversation.kt create mode 100644 core/src/nativeMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.native.kt delete mode 100644 examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/sql/DatabaseExample.java delete mode 100644 examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/sql/DatabaseExample.java delete mode 100644 java/src/main/java/com/xebia/functional/xef/java/auto/AIDatabase.java delete mode 100644 java/src/main/java/com/xebia/functional/xef/java/auto/AIScope.java create mode 100644 java/src/main/java/com/xebia/functional/xef/java/auto/Empty.java delete mode 100644 java/src/main/java/com/xebia/functional/xef/java/auto/ExecutionContext.java delete mode 100644 openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/Conversation.kt create mode 100644 openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/OpenAIConversation.kt create mode 100644 reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.kt rename reasoning/src/{jvmMain/kotlin/com/xebia/functional/xef/reasoning/search/Search.kt => commonMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/SearchTool.kt} (63%) create mode 100644 reasoning/src/jsMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.js.kt create mode 100644 reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.jvm.kt create mode 100644 reasoning/src/macosArm64Main/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.macosArm64.kt create mode 100644 reasoning/src/nativeMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.native.kt delete mode 100644 scala/src/main/scala/com/xebia/functional/xef/scala/agents/DefaultSearch.scala diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 3e74c49df..2e45edfa2 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -87,6 +87,11 @@ kotlin { implementation(libs.logback) implementation(libs.skrape) implementation(libs.rss.reader) + api(libs.jackson) + api(libs.jackson.schema) + api(libs.jackson.schema.jakarta) + api(libs.jakarta.validation) + implementation(libs.kotlinx.coroutines.reactive) api(libs.ktor.client.cio) } } diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/auto/Conversation.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/auto/Conversation.kt index bbb70a477..e1c5a8a10 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/auto/Conversation.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/auto/Conversation.kt @@ -9,36 +9,44 @@ import com.xebia.functional.xef.llm.models.images.ImagesGenerationResponse import com.xebia.functional.xef.prompt.Prompt import com.xebia.functional.xef.vectorstores.ConversationId import com.xebia.functional.xef.vectorstores.VectorStore -import kotlin.jvm.JvmName -import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmSynthetic +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.uuid.UUID import kotlinx.uuid.generateUUID -/** - * The [Conversation] is the context in which [AI] values are run. It encapsulates all the - * dependencies required to run [AI] values, and provides convenient syntax for writing [AI] based - * programs. - */ -class Conversation -@JvmOverloads -constructor( - val store: VectorStore, - val conversationId: ConversationId? = ConversationId(UUID.generateUUID().toString()) -) : AutoCloseable, AutoClose by autoClose() { +interface Conversation : AutoClose, AutoCloseable { + + val store: VectorStore + + val conversationId: ConversationId? + + val conversation: Conversation @AiDsl + @JvmSynthetic suspend fun addContext(vararg docs: String) { store.addTexts(docs.toList()) } + fun CoroutineScope.addContextAsync(vararg docs: String): Deferred = async { + store.addTexts(docs.toList()) + } + + fun CoroutineScope.addContextAsync(docs: Iterable): Deferred = async { + store.addTexts(docs.toList()) + } + @AiDsl - suspend fun addContext(docs: Iterable) { + @JvmSynthetic + suspend fun addContext(docs: Iterable): Unit { store.addTexts(docs.toList()) } @AiDsl - @JvmName("promptWithSerializer") + @JvmSynthetic suspend fun ChatWithFunctions.prompt( prompt: String, functions: List, @@ -47,36 +55,73 @@ constructor( ): A { return prompt( prompt = Prompt(prompt), - scope = this@Conversation, + scope = conversation, serializer = serializer, functions = functions, promptConfiguration = promptConfiguration, ) } + fun CoroutineScope.promptAsync( + chatWithFunctions: ChatWithFunctions, + prompt: String, + functions: List, + serializer: (json: String) -> A, + promptConfiguration: PromptConfiguration, + ): Deferred { + return async { + chatWithFunctions.prompt( + prompt = Prompt(prompt), + scope = conversation, + serializer = serializer, + functions = functions, + promptConfiguration = promptConfiguration, + ) + } + } + @AiDsl + @JvmSynthetic suspend fun Chat.promptMessage( question: String, promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS ): String = - promptMessages(question, this@Conversation, emptyList(), promptConfiguration).firstOrNull() + promptMessages(question, conversation, emptyList(), promptConfiguration).firstOrNull() ?: throw AIError.NoResponse() @AiDsl + fun CoroutineScope.promptMessageAsync( + chat: Chat, + question: String, + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): Deferred = async { + chat.promptMessages(question, conversation, emptyList(), promptConfiguration).firstOrNull() + ?: throw AIError.NoResponse() + } + + @AiDsl + @JvmSynthetic suspend fun Chat.promptMessages( question: String, functions: List = emptyList(), promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS - ): List = - promptMessages(Prompt(question), this@Conversation, functions, promptConfiguration) + ): List = promptMessages(Prompt(question), conversation, functions, promptConfiguration) + + fun CoroutineScope.promptMessagesAsync( + chat: Chat, + question: String, + functions: List = emptyList(), + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): Deferred> = async { + chat.promptMessages(Prompt(question), conversation, functions, promptConfiguration) + } @AiDsl fun Chat.promptStreaming( question: String, functions: List, promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS - ): Flow = - promptStreaming(Prompt(question), this@Conversation, functions, promptConfiguration) + ): Flow = promptStreaming(Prompt(question), conversation, functions, promptConfiguration) /** * Run a [prompt] describes the images you want to generate within the context of [Conversation]. @@ -86,6 +131,8 @@ constructor( * @param numberImages number of images to generate. * @param size the size of the images to generate. */ + @AiDsl + @JvmSynthetic suspend fun Images.images( prompt: String, numberImages: Int = 1, @@ -93,6 +140,16 @@ constructor( promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS ): ImagesGenerationResponse = this.images(Prompt(prompt), numberImages, size, promptConfiguration) + fun CoroutineScope.imagesAsync( + images: Images, + prompt: String, + numberImages: Int = 1, + size: String = "1024x1024", + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): Deferred = async { + images.images(Prompt(prompt), numberImages, size, promptConfiguration) + } + /** * Run a [prompt] describes the images you want to generate within the context of [Conversation]. * Returns a [ImagesGenerationResponse] containing time and urls with images generated. @@ -101,10 +158,37 @@ constructor( * @param numberImages number of images to generate. * @param size the size of the images to generate. */ + @AiDsl + @JvmSynthetic suspend fun Images.images( prompt: Prompt, numberImages: Int = 1, size: String = "1024x1024", promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS ): ImagesGenerationResponse = images(prompt, store, numberImages, size, promptConfiguration) + + fun CoroutineScope.imagesAsync( + images: Images, + prompt: Prompt, + numberImages: Int = 1, + size: String = "1024x1024", + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): Deferred = async { + images.images(prompt, store, numberImages, size, promptConfiguration) + } + + companion object { + + operator fun invoke( + store: VectorStore, + conversationId: ConversationId? = ConversationId(UUID.generateUUID().toString()) + ): PlatformConversation = PlatformConversation.create(store, conversationId) + + @JvmSynthetic + suspend operator fun invoke( + store: VectorStore, + conversationId: ConversationId? = ConversationId(UUID.generateUUID().toString()), + block: suspend PlatformConversation.() -> A + ): A = block(invoke(store, conversationId)) + } } diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.kt new file mode 100644 index 000000000..90931bc4d --- /dev/null +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.kt @@ -0,0 +1,17 @@ +package com.xebia.functional.xef.auto + +import com.xebia.functional.xef.vectorstores.ConversationId +import com.xebia.functional.xef.vectorstores.VectorStore + +expect abstract class PlatformConversation( + store: VectorStore, + conversationId: ConversationId?, +) : Conversation { + + companion object { + fun create( + store: VectorStore, + conversationId: ConversationId?, + ): PlatformConversation + } +} diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt index f38040c9c..5005af7e6 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt @@ -176,16 +176,13 @@ interface Chat : LLM { buffer: StringBuilder, ) { val lastRequestMessage = request.messages.lastOrNull() - if (scope.conversationId != null && lastRequestMessage != null) { + val cid = scope.conversationId + if (cid != null && lastRequestMessage != null) { val requestMemory = - Memory( - conversationId = scope.conversationId, - content = lastRequestMessage, - timestamp = getTimeMillis() - ) + Memory(conversationId = cid, content = lastRequestMessage, timestamp = getTimeMillis()) val responseMemory = Memory( - conversationId = scope.conversationId, + conversationId = cid, content = Message(role = Role.ASSISTANT, content = buffer.toString(), name = Role.ASSISTANT.name), timestamp = getTimeMillis(), @@ -200,17 +197,15 @@ interface Chat : LLM { ): List = also { val firstChoice = firstOrNull() val requestUserMessage = request.messages.lastOrNull() - if (requestUserMessage != null && firstChoice != null && scope.conversationId != null) { + val cid = scope.conversationId + if (requestUserMessage != null && firstChoice != null && cid != null) { val role = firstChoice.message?.role?.uppercase()?.let { Role.valueOf(it) } ?: Role.USER + val requestMemory = - Memory( - conversationId = scope.conversationId, - content = requestUserMessage, - timestamp = getTimeMillis() - ) + Memory(conversationId = cid, content = requestUserMessage, timestamp = getTimeMillis()) val firstChoiceMemory = Memory( - conversationId = scope.conversationId, + conversationId = cid, content = Message( role = role, @@ -230,17 +225,14 @@ interface Chat : LLM { ): List = also { val firstChoice = firstOrNull() val requestUserMessage = request.messages.lastOrNull() - if (requestUserMessage != null && firstChoice != null && scope.conversationId != null) { + val cid = scope.conversationId + if (requestUserMessage != null && firstChoice != null && cid != null) { val role = firstChoice.message?.role?.name?.uppercase()?.let { Role.valueOf(it) } ?: Role.USER val requestMemory = - Memory( - conversationId = scope.conversationId, - content = requestUserMessage, - timestamp = getTimeMillis() - ) + Memory(conversationId = cid, content = requestUserMessage, timestamp = getTimeMillis()) val firstChoiceMemory = Memory( - conversationId = scope.conversationId, + conversationId = cid, content = Message(role = role, content = firstChoice.message?.content ?: "", name = role.name), timestamp = getTimeMillis() @@ -255,12 +247,14 @@ interface Chat : LLM { private suspend fun memories( scope: Conversation, promptConfiguration: PromptConfiguration - ): List = - if (scope.conversationId != null) { - scope.store.memories(scope.conversationId, promptConfiguration.memoryLimit) + ): List { + val cid = scope.conversationId + return if (cid != null) { + scope.store.memories(cid, promptConfiguration.memoryLimit) } else { emptyList() } + } private suspend fun fitMessagesByTokens( history: List, diff --git a/core/src/jsMain/kotlin/com/xebia/functional/xef/auto/JSConversation.kt b/core/src/jsMain/kotlin/com/xebia/functional/xef/auto/JSConversation.kt new file mode 100644 index 000000000..5055f25f5 --- /dev/null +++ b/core/src/jsMain/kotlin/com/xebia/functional/xef/auto/JSConversation.kt @@ -0,0 +1,18 @@ +package com.xebia.functional.xef.auto + +import com.xebia.functional.xef.vectorstores.ConversationId +import com.xebia.functional.xef.vectorstores.VectorStore + +class JSConversation( + override val store: VectorStore, + override val conversationId: ConversationId? +) : PlatformConversation(store, conversationId) { + + override val conversation: Conversation = this + + override fun close() {} + + override fun autoClose(autoCloseable: A): A { + return autoCloseable + } +} diff --git a/core/src/jsMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.js.kt b/core/src/jsMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.js.kt new file mode 100644 index 000000000..0143e74fa --- /dev/null +++ b/core/src/jsMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.js.kt @@ -0,0 +1,12 @@ +package com.xebia.functional.xef.auto + +import com.xebia.functional.xef.vectorstores.ConversationId +import com.xebia.functional.xef.vectorstores.VectorStore + +actual abstract class PlatformConversation +actual constructor(store: VectorStore, conversationId: ConversationId?) : Conversation, AutoClose { + actual companion object { + actual fun create(store: VectorStore, conversationId: ConversationId?): PlatformConversation = + JSConversation(store, conversationId) + } +} diff --git a/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/BingSearch.kt b/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/BingSearch.kt deleted file mode 100644 index 21ccf2545..000000000 --- a/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/BingSearch.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.xebia.functional.xef.agents - -import arrow.core.flatten -import arrow.fx.coroutines.parMap -import com.apptasticsoftware.rssreader.Item -import com.apptasticsoftware.rssreader.RssReader -import com.xebia.functional.xef.textsplitters.TextSplitter -import io.ktor.http.* -import java.util.stream.Collectors -import kotlin.jvm.optionals.toList -import kotlinx.coroutines.Dispatchers - -suspend fun bingSearch( - search: String, - splitter: TextSplitter, - url: String = "https://www.bing.com/news/search?q=${search.encodeURLParameter()}&format=rss", - maxLinks: Int = 10 -): List { - val items: List = RssReader().read(url).collect(Collectors.toList()) - val links = items.map { it.link }.flatMap { it.toList() }.take(maxLinks) - val linkedDocs = - links - .parMap(Dispatchers.IO) { link -> - try { - scrapeUrlContent(link, splitter) - } catch (e: Exception) { - // ignore errors when scrapping nested content due to certificates and other remote issues - emptyList() - } - } - .flatten() - val docs = - items.map { - """| - |${it.title} - |${it.description} - |${it.link} - |${it.pubDate} - """ - .trimMargin() - } - return splitter.splitDocuments(linkedDocs + docs) -} diff --git a/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/DefaultSearch.kt b/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/DefaultSearch.kt deleted file mode 100644 index 32a8c3706..000000000 --- a/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/DefaultSearch.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:JvmName("Search") - -package com.xebia.functional.xef.agents - -import com.xebia.functional.tokenizer.ModelType -import com.xebia.functional.xef.textsplitters.TokenTextSplitter - -suspend fun search(prompt: String): List = - bingSearch( - search = prompt, - TokenTextSplitter(ModelType.GPT_3_5_TURBO, chunkSize = 100, chunkOverlap = 50) - ) diff --git a/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/ScrapeUrlContent.kt b/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/ScrapeUrlContent.kt deleted file mode 100644 index f1a97ba4d..000000000 --- a/core/src/jvmMain/kotlin/com/xebia/functional/xef/agents/ScrapeUrlContent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.xebia.functional.xef.agents - -import com.xebia.functional.xef.loaders.ScrapeURLTextLoader -import com.xebia.functional.xef.textsplitters.TextSplitter - -suspend fun scrapeUrlContent(url: String, splitter: TextSplitter): List = - ScrapeURLTextLoader(url).loadAndSplit(splitter) diff --git a/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/FromJson.kt b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/FromJson.kt new file mode 100644 index 000000000..3a49c21be --- /dev/null +++ b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/FromJson.kt @@ -0,0 +1,5 @@ +package com.xebia.functional.xef.auto + +fun interface FromJson { + fun fromJson(a: String): A +} diff --git a/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/JVMConversation.kt b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/JVMConversation.kt new file mode 100644 index 000000000..f1758497d --- /dev/null +++ b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/JVMConversation.kt @@ -0,0 +1,14 @@ +package com.xebia.functional.xef.auto + +import com.xebia.functional.xef.vectorstores.ConversationId +import com.xebia.functional.xef.vectorstores.VectorStore +import java.io.Closeable + +open class JVMConversation( + override val store: VectorStore, + override val conversationId: ConversationId?, +) : + PlatformConversation(store, conversationId), AutoClose by autoClose(), AutoCloseable, Closeable { + + override val conversation: Conversation = this +} diff --git a/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.jvm.kt b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.jvm.kt new file mode 100644 index 000000000..379dcecf3 --- /dev/null +++ b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.jvm.kt @@ -0,0 +1,185 @@ +package com.xebia.functional.xef.auto + +import com.xebia.functional.xef.auto.serialization.JacksonSerialization +import com.xebia.functional.xef.llm.Chat +import com.xebia.functional.xef.llm.ChatWithFunctions +import com.xebia.functional.xef.llm.Images +import com.xebia.functional.xef.llm.models.functions.CFunction +import com.xebia.functional.xef.llm.models.images.ImagesGenerationResponse +import com.xebia.functional.xef.prompt.Prompt +import com.xebia.functional.xef.vectorstores.ConversationId +import com.xebia.functional.xef.vectorstores.VectorStore +import java.util.concurrent.CompletableFuture +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.future.asCompletableFuture +import kotlinx.coroutines.reactive.asPublisher +import org.reactivestreams.Publisher + +actual abstract class PlatformConversation +actual constructor( + store: VectorStore, + conversationId: ConversationId?, +) : Conversation, AutoClose, AutoCloseable { + + val coroutineScope = CoroutineScope(SupervisorJob()) + + fun addContext(docs: List): CompletableFuture = + coroutineScope.addContextAsync(docs).asCompletableFuture() + + fun addContextFromArray(docs: Array): CompletableFuture = + coroutineScope.addContextAsync(docs.toList()).asCompletableFuture() + + fun prompt( + chat: ChatWithFunctions, + prompt: String, + functions: List, + serializer: FromJson, + promptConfiguration: PromptConfiguration + ): CompletableFuture = + coroutineScope + .promptAsync( + chatWithFunctions = chat, + prompt = prompt, + functions = functions, + serializer = serializer::fromJson, + promptConfiguration = promptConfiguration, + ) + .asCompletableFuture() + + @JvmOverloads + fun prompt( + chat: ChatWithFunctions, + prompt: String, + target: Class, + functions: List = listOf(generateCFunctionFromClass(target)), + serializer: FromJson = FromJson { json -> + JacksonSerialization.objectMapper.readValue(json, target) + }, + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): CompletableFuture = + coroutineScope + .promptAsync( + chatWithFunctions = chat, + prompt = prompt, + functions = functions, + serializer = serializer::fromJson, + promptConfiguration = promptConfiguration, + ) + .asCompletableFuture() + + fun generateCFunctionFromClass(target: Class) = + CFunction( + name = target.simpleName, + description = "Generated function for ${target.simpleName}", + parameters = JacksonSerialization.schemaGenerator.generateSchema(target).toString() + ) + + @JvmOverloads + fun promptMessage( + chat: Chat, + question: String, + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): CompletableFuture = + coroutineScope + .promptMessageAsync( + chat = chat, + question = question, + promptConfiguration = promptConfiguration, + ) + .asCompletableFuture() + + @JvmOverloads + fun promptMessages( + chat: Chat, + question: String, + functions: List = emptyList(), + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): CompletableFuture> = + coroutineScope + .promptMessagesAsync( + chat = chat, + question = question, + functions = functions, + promptConfiguration = promptConfiguration, + ) + .asCompletableFuture() + + @JvmOverloads + fun promptStreaming( + chat: Chat, + question: String, + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS, + functions: List = emptyList(), + ): Publisher = + chat + .promptStreaming( + question = question, + scope = conversation, + functions = functions, + promptConfiguration = promptConfiguration, + ) + .asPublisher() + + /** + * Run a [prompt] describes the images you want to generate within the context of [Conversation]. + * Returns a [ImagesGenerationResponse] containing time and urls with images generated. + * + * @param prompt a [Prompt] describing the images you want to generate. + * @param numberImages number of images to generate. + * @param size the size of the images to generate. + */ + @AiDsl + fun images( + images: Images, + prompt: String, + numberImages: Int = 1, + size: String = "1024x1024", + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): CompletableFuture = + coroutineScope + .imagesAsync( + images = images, + prompt = prompt, + numberImages = numberImages, + size = size, + promptConfiguration = promptConfiguration, + ) + .asCompletableFuture() + + /** + * Run a [prompt] describes the images you want to generate within the context of [Conversation]. + * Returns a [ImagesGenerationResponse] containing time and urls with images generated. + * + * @param prompt a [Prompt] describing the images you want to generate. + * @param numberImages number of images to generate. + * @param size the size of the images to generate. + */ + @AiDsl + fun images( + images: Images, + prompt: Prompt, + numberImages: Int = 1, + size: String = "1024x1024", + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): CompletableFuture = + coroutineScope + .imagesAsync( + images = images, + prompt = prompt, + numberImages = numberImages, + size = size, + promptConfiguration = promptConfiguration, + ) + .asCompletableFuture() + + actual companion object { + actual fun create(store: VectorStore, conversationId: ConversationId?): PlatformConversation = + JVMConversation(store, conversationId) + } + + override fun close() { + coroutineScope.cancel() + } +} diff --git a/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/futures/ExecutionContext.kt b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/futures/ExecutionContext.kt new file mode 100644 index 000000000..5ebbb28fb --- /dev/null +++ b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/futures/ExecutionContext.kt @@ -0,0 +1,49 @@ +package com.xebia.functional.xef.auto.futures + +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.ThreadFactory +import java.util.concurrent.atomic.AtomicInteger +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.cancel +import kotlinx.coroutines.future.future + +interface ExecutionContext : AutoCloseable { + + val executorService: ExecutorService + val coroutineScope: CoroutineScope + + fun future(block: suspend CoroutineScope.() -> A): CompletableFuture = + coroutineScope.future(coroutineScope.coroutineContext, CoroutineStart.DEFAULT, block) + + override fun close() { + coroutineScope.cancel() + executorService.shutdown() + } + + private class AIScopeThreadFactory : ThreadFactory { + private val counter = AtomicInteger() + + override fun newThread(r: Runnable): Thread { + val t = Thread(r) + t.name = "xef-ai-scope-worker-${counter.getAndIncrement()}" + t.isDaemon = true + return t + } + } + + companion object { + @JvmField val DEFAULT = from(Executors.newCachedThreadPool(AIScopeThreadFactory())) + + @JvmStatic + fun from(executorService: ExecutorService): ExecutionContext = + object : ExecutionContext { + override val executorService: ExecutorService = executorService + override val coroutineScope: CoroutineScope = + CoroutineScope(executorService.asCoroutineDispatcher()) + } + } +} diff --git a/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/serialization/JacksonSerialization.kt b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/serialization/JacksonSerialization.kt new file mode 100644 index 000000000..94e520f30 --- /dev/null +++ b/core/src/jvmMain/kotlin/com/xebia/functional/xef/auto/serialization/JacksonSerialization.kt @@ -0,0 +1,30 @@ +package com.xebia.functional.xef.auto.serialization + +import com.fasterxml.jackson.databind.ObjectMapper +import com.github.victools.jsonschema.generator.OptionPreset +import com.github.victools.jsonschema.generator.SchemaGenerator +import com.github.victools.jsonschema.generator.SchemaGeneratorConfig +import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder +import com.github.victools.jsonschema.module.jakarta.validation.JakartaValidationModule +import com.github.victools.jsonschema.module.jakarta.validation.JakartaValidationOption + +object JacksonSerialization { + val module: JakartaValidationModule = + JakartaValidationModule( + JakartaValidationOption.NOT_NULLABLE_FIELD_IS_REQUIRED, + JakartaValidationOption.INCLUDE_PATTERN_EXPRESSIONS + ) + + val configBuilder: SchemaGeneratorConfigBuilder = + SchemaGeneratorConfigBuilder( + com.github.victools.jsonschema.generator.SchemaVersion.DRAFT_7, + OptionPreset.PLAIN_JSON + ) + .with(module) + + val config: SchemaGeneratorConfig = configBuilder.build() + + val schemaGenerator = SchemaGenerator(config) + + val objectMapper = ObjectMapper() +} diff --git a/core/src/nativeMain/kotlin/com/xebia/functional/xef/auto/NativeConversation.kt b/core/src/nativeMain/kotlin/com/xebia/functional/xef/auto/NativeConversation.kt new file mode 100644 index 000000000..da4f1ead7 --- /dev/null +++ b/core/src/nativeMain/kotlin/com/xebia/functional/xef/auto/NativeConversation.kt @@ -0,0 +1,18 @@ +package com.xebia.functional.xef.auto + +import com.xebia.functional.xef.vectorstores.ConversationId +import com.xebia.functional.xef.vectorstores.VectorStore + +class NativeConversation( + override val store: VectorStore, + override val conversationId: ConversationId?, +) : PlatformConversation(store, conversationId) { + + override val conversation: Conversation = this + + override fun close() {} + + override fun autoClose(autoCloseable: A): A { + return autoCloseable + } +} diff --git a/core/src/nativeMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.native.kt b/core/src/nativeMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.native.kt new file mode 100644 index 000000000..1a8d8b5b0 --- /dev/null +++ b/core/src/nativeMain/kotlin/com/xebia/functional/xef/auto/PlatformConversation.native.kt @@ -0,0 +1,13 @@ +package com.xebia.functional.xef.auto + +import com.xebia.functional.xef.vectorstores.ConversationId +import com.xebia.functional.xef.vectorstores.VectorStore + +actual abstract class PlatformConversation +actual constructor(store: VectorStore, conversationId: ConversationId?) : + Conversation, AutoClose, AutoCloseable { + actual companion object { + actual fun create(store: VectorStore, conversationId: ConversationId?): PlatformConversation = + NativeConversation(store, conversationId) + } +} diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/ASCIIArt.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/ASCIIArt.java index a94a3af01..effafaaa6 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/ASCIIArt.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/ASCIIArt.java @@ -1,16 +1,16 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class ASCIIArt { public String art; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("ASCII art of a cat dancing", ASCIIArt.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "ASCII art of a cat dancing", ASCIIArt.class) .thenAccept(art -> System.out.println(art.art)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Animals.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Animals.java index 3c24c256e..0c6fc6561 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Animals.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Animals.java @@ -1,25 +1,25 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Animals { - private final AIScope scope; + private final PlatformConversation scope; - public Animals(AIScope scope) { + public Animals(PlatformConversation scope) { this.scope = scope; } public CompletableFuture uniqueAnimal() { - return scope.prompt("A unique animal species.", Animal.class); + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A unique animal species.", Animal.class); } public CompletableFuture groundbreakingInvention() { - return scope.prompt("A groundbreaking invention from the 20th century.", Invention.class); + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A groundbreaking invention from the 20th century.", Invention.class); } public CompletableFuture story(Animal animal, Invention invention) { @@ -27,7 +27,7 @@ public CompletableFuture story(Animal animal, Invention invention) { "Write a short story of 500 words that involves the following elements:" + "1. A unique animal species called ${animal.name} that lives in " + animal.habitat + " and has a diet of " + animal.diet + "." + "2. A groundbreaking invention from the 20th century called " + invention.name + " , invented by " + invention.inventor + " in " + invention.year + ", which serves the purpose of " + invention.purpose + "."; - return scope.prompt(storyPrompt, Story.class); + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, storyPrompt, Story.class); } public record Animal(String name, String habitat, String diet){} @@ -39,7 +39,7 @@ public void tell() { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { + try (PlatformConversation scope = OpenAI.conversation()) { Animals animals = new Animals(scope); animals.uniqueAnimal() .thenCompose(animal -> diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Book.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Book.java index e98bc22d2..a2b1c3d2f 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Book.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Book.java @@ -1,9 +1,9 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Book { @@ -12,8 +12,8 @@ public class Book { public String summary; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("To Kill a Mockingbird by Harper Lee summary.", Book.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "To Kill a Mockingbird by Harper Lee summary.", Book.class) .thenAccept(book -> System.out.println("To Kill a Mockingbird summary:\n" + book.summary)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Books.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Books.java index 2f17fbc94..9c1e8e4bb 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Books.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Books.java @@ -1,28 +1,28 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; import jakarta.validation.constraints.NotNull; + import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Books { - private final AIScope scope; + private final PlatformConversation scope; - public Books(AIScope scope) { + public Books(PlatformConversation scope) { this.scope = scope; } public record Book(@NotNull String title, @NotNull String author, @NotNull int year, @NotNull String genre){} public CompletableFuture bookSelection(String topic) { - return scope.prompt("Give me a selection of books about " + topic, Books.Book.class); + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Give me a selection of books about " + topic, Books.Book.class); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { + try (PlatformConversation scope = OpenAI.conversation()) { Books books = new Books(scope); books.bookSelection("artificial intelligence") .thenAccept(System.out::println) diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/BreakingNews.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/BreakingNews.java index 8f4d6ccf7..592e7589c 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/BreakingNews.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/BreakingNews.java @@ -1,7 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -17,17 +18,18 @@ public class BreakingNews { public record BreakingNew(String summary) { } - private static CompletableFuture writeParagraph(AIScope scope) { + private static CompletableFuture writeParagraph(PlatformConversation scope) { var currentDate = dtf.format(now); - return scope.prompt("write a paragraph of about 300 words about: " + currentDate + " Covid News", BreakingNews.BreakingNew.class) + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "write a paragraph of about 300 words about: " + currentDate + " Covid News", BreakingNews.BreakingNew.class) .thenAccept(breakingNews -> System.out.println(currentDate + " Covid news summary:\n" + breakingNews)); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { + try (PlatformConversation scope = OpenAI.conversation()) { var currentDate = dtf.format(now); - scope.addContext(scope.search(currentDate + " Covid News").get()); + var search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search(currentDate + " Covid News").get()); writeParagraph(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/ChessAI.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/ChessAI.java index eb5aeef2f..c60876686 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/ChessAI.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/ChessAI.java @@ -1,7 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.ArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; @@ -14,7 +15,7 @@ public record ChessBoard(String board){} public record GameState(Boolean ended, String winner){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { + try (PlatformConversation scope = OpenAI.conversation()) { var moves = new ArrayList(); var gameEnded = false; var winner = ""; @@ -29,7 +30,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc currentPlayer, moves.stream().map(ChessMove::toString).collect(Collectors.joining(", "))); - ChessMove move = scope.prompt(prompt, ChessMove.class).get(); + ChessMove move = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, prompt, ChessMove.class).get(); moves.add(move); // Update boardState according to move.move @@ -41,7 +42,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc Add a brief description of the move and it's implications""", moves.stream().map(it -> it.player + ":" + it.move).collect(Collectors.joining(", "))); - ChessBoard chessBoard= scope.prompt(boardPrompt, ChessBoard.class).get(); + ChessBoard chessBoard= scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, boardPrompt, ChessBoard.class).get(); System.out.println("Current board:\n" + chessBoard.board); var gameStatePrompt = String.format(""" @@ -49,7 +50,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc has the game ended (win, draw, or stalemate)?""", moves.stream().map(ChessMove::toString).collect(Collectors.joining(", "))); - GameState gameState = scope.prompt(gameStatePrompt, GameState.class).get(); + GameState gameState = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, gameStatePrompt, GameState.class).get(); gameEnded = gameState.ended; winner = gameState.winner; diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Colors.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Colors.java index e91e156ba..cac62790d 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Colors.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Colors.java @@ -1,18 +1,18 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Colors { public List colors; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("a selection of 10 beautiful colors that go well together", Colors.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "a selection of 10 beautiful colors that go well together", Colors.class) .thenAccept(colors -> System.out.println("Colors:\n" + colors.colors)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/DivergentTasks.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/DivergentTasks.java index 01a80da11..1c325ea86 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/DivergentTasks.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/DivergentTasks.java @@ -1,23 +1,25 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; + import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class DivergentTasks { public Long numberOfMedicalNeedlesInWorld; - private static CompletableFuture numberOfMedical(AIScope scope) { - return scope.prompt("Provide the number of medical needles in the world", DivergentTasks.class) + private static CompletableFuture numberOfMedical(PlatformConversation scope) { + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Provide the number of medical needles in the world", DivergentTasks.class) .thenAccept(numberOfNeedles -> System.out.println("Needles in world:\n" + numberOfNeedles.numberOfMedicalNeedlesInWorld)); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.addContext(scope.search("Estimate amount of medical needles in the world").get()); + try (PlatformConversation scope = OpenAI.conversation()) { + Search search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search("Estimate amount of medical needles in the world").get()); numberOfMedical(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Employees.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Employees.java index 77694f5fa..ff8e852a3 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Employees.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Employees.java @@ -1,9 +1,9 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Employees { @@ -16,8 +16,8 @@ public record Company(String name, Address address){} "Use the information provided."; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt(complexPrompt, Employees.Employee.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, complexPrompt, Employees.Employee.class) .thenAccept(employeeData -> System.out.println( "Employee Information:\n\n" + "Name: " + employeeData.firstName + " " + employeeData.lastName + "\n" + diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Fact.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Fact.java index bd36a247b..22138ad31 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Fact.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Fact.java @@ -1,9 +1,9 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Fact { @@ -11,9 +11,9 @@ public record FactRecord(String topic, String content){} public record Riddle(FactRecord fact1, FactRecord fact2, String riddle){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - var fact1 = scope.prompt("A fascinating fact about you", FactRecord.class).get(); - var fact2 = scope.prompt("An interesting fact about me", FactRecord.class).get(); + try (PlatformConversation scope = OpenAI.conversation()) { + var fact1 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A fascinating fact about you", FactRecord.class).get(); + var fact2 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "An interesting fact about me", FactRecord.class).get(); var riddlePrompt = ""+ "Create a riddle that combines the following facts:\n\n" + @@ -21,7 +21,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc "Fact 1: " + fact1.content + "\n" + "Fact 2: " + fact2.content; - scope.prompt(riddlePrompt, Riddle.class) + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, riddlePrompt, Riddle.class) .thenAccept(riddle -> System.out.println("Riddle:\n\n" + riddle)).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Love.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Love.java index 696dd33bb..82fcefdd8 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Love.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Love.java @@ -1,16 +1,16 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Love { public List loveList; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("tell me you like me with just emojis", Love.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "tell me you like me with just emojis", Love.class) .thenAccept(love -> System.out.println(love.loveList)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Markets.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Markets.java index 630f3f26b..aa8e5e0b3 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Markets.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Markets.java @@ -1,7 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -15,21 +16,21 @@ public class Markets { public record Market(String news, List raisingStockSymbols, List decreasingStockSymbols) { } - private static CompletableFuture stockMarketSummary(AIScope scope) { + private static CompletableFuture stockMarketSummary(PlatformConversation scope) { var news = "|" + "|Write a short summary of the stock market results given the provided context."; - return scope.prompt(news, Market.class) + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, news, Market.class) .thenAccept(markets -> System.out.println(markets)); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { + try (PlatformConversation scope = OpenAI.conversation()) { var dtf = DateTimeFormatter.ofPattern("dd/M/yyyy"); var now = LocalDateTime.now(); var currentDate = dtf.format(now); - - scope.addContext(scope.search(currentDate + "Stock market results, raising stocks, decreasing stocks").get()); + var search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search(currentDate + "Stock market results, raising stocks, decreasing stocks").get()); stockMarketSummary(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/MealPlan.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/MealPlan.java index 35a1f811e..ef58bac07 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/MealPlan.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/MealPlan.java @@ -1,25 +1,27 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; + import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class MealPlan { public record MealPlanRecord(String name, List recipes){} public record Recipe(String name, List ingredients){} - private static CompletableFuture mealPlan(AIScope scope) { - return scope.prompt("Meal plan for the week for a person with gall bladder stones that includes 5 recipes.", MealPlanRecord.class) + private static CompletableFuture mealPlan(PlatformConversation scope) { + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Meal plan for the week for a person with gall bladder stones that includes 5 recipes.", MealPlanRecord.class) .thenAccept(mealPlan -> System.out.println(mealPlan)); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.addContext(scope.search("gall bladder stones meals").get()); + try (PlatformConversation scope = OpenAI.conversation()) { + Search search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search("gall bladder stones meals").get()); mealPlan(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/MeaningOfLife.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/MeaningOfLife.java index a953cbe8b..f5f906c47 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/MeaningOfLife.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/MeaningOfLife.java @@ -1,17 +1,17 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class MeaningOfLife { public List mainTheories; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("What are the main theories about the meaning of life", MeaningOfLife.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "What are the main theories about the meaning of life", MeaningOfLife.class) .thenAccept(meaningOfLife -> System.out.println("There are several theories about the meaning of life:\n" + meaningOfLife.mainTheories)) .get(); diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Movies.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Movies.java index 2295472ef..aaf48bec7 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Movies.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Movies.java @@ -1,17 +1,17 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Movies { public record Movie(String title, String genre, String director){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("Please provide a movie title, genre and director for the Inception movie", Movie.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Please provide a movie title, genre and director for the Inception movie", Movie.class) .thenAccept(movie -> System.out.println(movie)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/PDFDocument.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/PDFDocument.java index 4bfd18d44..9df1994ac 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/PDFDocument.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/PDFDocument.java @@ -1,13 +1,12 @@ package com.xebia.functional.xef.java.auto.jdk21; -import static com.xebia.functional.xef.textsplitters.TokenTextSplitterKt.TokenTextSplitter; - -import com.xebia.functional.tokenizer.ModelType; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; import com.xebia.functional.xef.java.auto.jdk21.util.ConsoleUtil; +import com.xebia.functional.xef.reasoning.pdf.PDF; + +import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; public class PDFDocument { @@ -17,7 +16,7 @@ public record AIResponse(String answer, String source){} private static final String PDF_URL = "https://people.cs.ksu.edu/~schmidt/705a/Scala/Programming-in-Scala.pdf"; - private static CompletableFuture askQuestion(AIScope scope) { + private static CompletableFuture askQuestion(PlatformConversation scope) { System.out.println("Enter your question ( to exit): "); @@ -25,7 +24,7 @@ private static CompletableFuture askQuestion(AIScope scope) { if (line == null || line.isBlank()) { return CompletableFuture.completedFuture(null); } else { - scope.prompt(line, AIResponse.class) + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, line, AIResponse.class) .thenAccept(aiRes -> System.out.println(aiRes.answer + "\n---\n" + aiRes.source + "\n---\n")); @@ -34,10 +33,10 @@ private static CompletableFuture askQuestion(AIScope scope) { } public static void main(String[] args) throws Exception { - - var textSplitter = TokenTextSplitter(ModelType.getDEFAULT_SPLITTER_MODEL(), 100, 50); - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.addContext(scope.pdf(PDF_URL, textSplitter).get()); + try (PlatformConversation scope = OpenAI.conversation()) { + PDF pdf = new PDF(OpenAI.FromEnvironment.DEFAULT_CHAT, + OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, scope); + scope.addContext(List.of(pdf.readPDFFromUrl.readPDFFromUrl(PDF_URL).get())); askQuestion(scope).get(); } finally { diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Persons.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Persons.java index 5d4bd3306..59c76d875 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Persons.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Persons.java @@ -1,17 +1,17 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Persons { public record Person(String name, int age){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("What is your name and age?", Person.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "What is your name and age?", Person.class) .thenAccept(person -> System.out.println(person)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Planets.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Planets.java index 97cc7ce88..a1a954156 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Planets.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Planets.java @@ -1,19 +1,19 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Planets { public record Planet(String name, double distanceFromSun, List moons){} public record Moon(String name, double distanceFromPlanetInKm){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - var earth = scope.prompt("Information about Earth and its moon.", Planet.class).get(); - var mars = scope.prompt("Information about Mars and its moons.", Planet.class).get(); + try (PlatformConversation scope = OpenAI.conversation()) { + var earth = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Information about Earth and its moon.", Planet.class).get(); + var mars = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Information about Mars and its moons.", Planet.class).get(); System.out.println("Celestial bodies information:\n\n" + planetInfo(earth) + "\n\n" + planetInfo(mars)); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Poems.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Poems.java index 97190047d..4b203dfd7 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Poems.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Poems.java @@ -1,25 +1,25 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Poems { public record Poem(String title, String content){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - var poem1 = scope.prompt("A short poem about the beauty of nature.", Poem.class).get(); - var poem2 = scope.prompt("A short poem about the power of technology.", Poem.class).get(); - var poem3 = scope.prompt("A short poem about the wisdom of artificial intelligence.", Poem.class).get(); + try (PlatformConversation scope = OpenAI.conversation()) { + var poem1 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A short poem about the beauty of nature.", Poem.class).get(); + var poem2 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A short poem about the power of technology.", Poem.class).get(); + var poem3 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A short poem about the wisdom of artificial intelligence.", Poem.class).get(); var combinedPoems = String.format("%s\n\n%s\n\n%s", poem1.content, poem2.content, poem3.content); var newPoemPrompt = "Write a new poem that combines ideas from the following themes: the beauty " + "of nature, the power of technology, and the wisdom of artificial intelligence. Here are some " + "examples of poems on these themes: " + combinedPoems; - scope.prompt(newPoemPrompt, Poem.class). + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, newPoemPrompt, Poem.class). thenAccept(poem -> System.out.printf("New Poem:\n\n" + poem.content)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Populations.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Populations.java index 5420bd194..dedaf5fcf 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Populations.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Populations.java @@ -1,9 +1,9 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Populations { @@ -11,10 +11,10 @@ public record Population(int population, String description){} public record Image(String description, String url){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - var cadiz = scope.prompt("What is the population of Cádiz, Spain.", Population.class).get(); - var seattle = scope.prompt("What is the population of Seattle, WA.", Population.class).get(); - var img = scope.prompt("A hybrid city of Cádiz, Spain and Seattle, US.", Image.class).get(); + try (PlatformConversation scope = OpenAI.conversation()) { + var cadiz = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "What is the population of Cádiz, Spain.", Population.class).get(); + var seattle = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "What is the population of Seattle, WA.", Population.class).get(); + var img = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A hybrid city of Cádiz, Spain and Seattle, US.", Image.class).get(); System.out.println(img); System.out.println("The population of Cádiz is " + cadiz.population + " and the population of Seattle is " + seattle.population); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Recipes.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Recipes.java index 03f101884..2d6d19b2f 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Recipes.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Recipes.java @@ -1,18 +1,18 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Recipes { public record Recipe(String name, List ingredients){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - var recipe = scope.prompt("Recipe for chocolate chip cookies.", Recipe.class).get(); + try (PlatformConversation scope = OpenAI.conversation()) { + var recipe = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Recipe for chocolate chip cookies.", Recipe.class).get(); System.out.println("The recipe for " + recipe.name + " is " + recipe.ingredients ); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/TopAttractions.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/TopAttractions.java index 9a8655e43..ef1a19e78 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/TopAttractions.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/TopAttractions.java @@ -1,10 +1,10 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.Objects; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class TopAttractions { @@ -13,8 +13,8 @@ public record City(String name, String country){} public record Weather(City city, Double temperature, String description){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("Top attraction in Cádiz, Spain.", TopAttraction.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Top attraction in Cádiz, Spain.", TopAttraction.class) .thenAccept(attraction -> System.out.println( "The top attraction in " + attraction.city.name + " is " + attraction.attractionName + "." + "Here's a brief description: " + attraction.description + "." + diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/TouristAttractions.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/TouristAttractions.java index 4f0dfc008..607414db1 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/TouristAttractions.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/TouristAttractions.java @@ -1,17 +1,17 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class TouristAttractions { public record TouristAttraction(String name, String location, String history){} public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.prompt("Statue of Liberty location and history.", TouristAttraction.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Statue of Liberty location and history.", TouristAttraction.class) .thenAccept(statueOfLiberty -> System.out.println( statueOfLiberty.name + "is located in " + statueOfLiberty.location + " and has the following history: " + statueOfLiberty.history diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Weather.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Weather.java index a1d99583e..718e691d2 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Weather.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/Weather.java @@ -1,26 +1,27 @@ package com.xebia.functional.xef.java.auto.jdk21; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; public class Weather { public List answer; - private static CompletableFuture clothesRecommend(AIScope scope) { - return scope.prompt("Knowing this forecast, what clothes do you recommend I should wear?", Weather.class) + private static CompletableFuture clothesRecommend(PlatformConversation scope) { + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Knowing this forecast, what clothes do you recommend I should wear?", Weather.class) .thenAccept(weather -> System.out.println(weather.answer) ); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()))) { - scope.addContext(scope.search("Weather in Cádiz, Spain").get()); + try (PlatformConversation scope = OpenAI.conversation()) { + Search search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search("Weather in Cádiz, Spain").get()); clothesRecommend(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/gpt4all/Chat.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/gpt4all/Chat.java index a63d2641c..1a7b3f930 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/gpt4all/Chat.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/gpt4all/Chat.java @@ -3,17 +3,16 @@ import com.xebia.functional.gpt4all.GPT4All; import com.xebia.functional.gpt4all.Gpt4AllModel; import com.xebia.functional.xef.auto.PromptConfiguration; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Path; import java.util.Objects; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; public class Chat { public static void main(String[] args) throws ExecutionException, InterruptedException, IOException { @@ -37,10 +36,8 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc * to provide embeddings for docs in contextScope. */ - try (var scope = new AIScope(new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor())); - var br = new BufferedReader(new InputStreamReader(System.in))) { - - System.out.println("🤖 Context loaded: " + scope.getExec().getContext()); + try (var scope = OpenAI.conversation(); + var br = new BufferedReader(new InputStreamReader(System.in))) { System.out.println("\n🤖 Enter your question: "); diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/sql/DatabaseExample.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/sql/DatabaseExample.java deleted file mode 100644 index 8d2f74a29..000000000 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/sql/DatabaseExample.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.xebia.functional.xef.java.auto.jdk21.sql; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.xebia.functional.xef.auto.PromptConfiguration; -import com.xebia.functional.xef.auto.llm.openai.OpenAI; -import com.xebia.functional.xef.auto.llm.openai.OpenAIModel; -import com.xebia.functional.xef.java.auto.AIDatabase; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; -import com.xebia.functional.xef.java.auto.jdk21.util.ConsoleUtil; -import com.xebia.functional.xef.sql.jdbc.JdbcConfig; -import java.io.PrintStream; -import java.util.Arrays; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; -import org.jetbrains.annotations.NotNull; - -public class DatabaseExample { - - private static final OpenAIModel MODEL = new OpenAI().DEFAULT_CHAT; - private static PrintStream out = System.out; - private static ConsoleUtil util = new ConsoleUtil(); - - @NotNull - private static JdbcConfig getJdbcConfig() { - var env = System.getenv(); - var vendor = env.getOrDefault("XEF_SQL_DB_VENDOR", "mysql"); - var host = env.getOrDefault("XEF_SQL_DB_HOST", "localhost"); - var username = env.getOrDefault("XEF_SQL_DB_USER", "user"); - var password = env.getOrDefault("XEF_SQL_DB_PASSWORD", "password"); - var port = Integer.parseInt(env.getOrDefault("XEF_SQL_DB_PORT", "3306")); - var database = env.getOrDefault("XEF_SQL_DB_DATABASE", "database"); - var model = MODEL; - - return new JdbcConfig(vendor, host, username, password, port, database, model); - } - - static final Function1 promptConfiguration = - (Function1) builder -> { - builder.docsInContext(50); - return Unit.INSTANCE; - }; - - public static void main(String[] args) throws Exception { - - var executionContext = new ExecutionContext(Executors.newVirtualThreadPerTaskExecutor()); - try (var scope = new AIScope(new ObjectMapper(), executionContext)) { - var database = new AIDatabase(getJdbcConfig(), executionContext); - - out.println("llmdb> Welcome to the LLMDB (An LLM interface to your SQL Database) !"); - out.println("llmdb> You can ask me questions about the database and I will try to answer them."); - out.println("llmdb> You can type `exit` to exit the program."); - out.println("llmdb> Loading recommended prompts..."); - - Arrays.stream(database.getInterestingPromptsForDatabase().get() - .split("\n")).forEach(it -> out.println("llmdb> " + it)); - - while (true) { - out.println("user> "); - var input = util.readLine(); - if (input.equals("exit")) break; - - try { - database.extendContext(database.promptQuery(input).get()); - CompletableFuture result = scope.promptMessage(MODEL, "|\n" + - " You are a database assistant that helps users to query and summarize results from the database.\n" + - " Instructions:\n" + - " 1. Summarize the information provided in the `Context` and follow to step 2.\n" + - " 2. If the information relates to the `input` then answer the question otherwise return just the summary.\n" + - " ```input\n" + - " " + input + " \n" + - " ```\n" + - " 3. Try to answer and provide information with as much detail as you can\n" + - " ", PromptConfiguration.Companion.build(promptConfiguration)); - - out.println(result.get()); - } catch (Exception e) { - out.println("llmdb> " + e.getMessage()); - e.printStackTrace(); - } - } - } - finally { - util.close(); - } - - } - - -} diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/ControlSignals.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/ControlSignals.java index 6e8f4c5f3..1bfaa87af 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/ControlSignals.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/ControlSignals.java @@ -1,5 +1,7 @@ package com.xebia.functional.xef.java.auto.jdk21.tot; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.renderHistory; import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.truncateText; @@ -27,7 +29,7 @@ public static CompletableFuture controlSignal(Problems.Memory " 5. Ensure the guidance accounts for previous answers in the `history`.\n" + " \n"); - return Problems.Memory.getAiScope().prompt(guidancePrompt, ControlSignal.class); + return Problems.Memory.getAiScope().prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, guidancePrompt, ControlSignal.class); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Critiques.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Critiques.java index 1547f6af1..8651f3279 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Critiques.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Critiques.java @@ -1,5 +1,7 @@ package com.xebia.functional.xef.java.auto.jdk21.tot; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.truncateText; import java.util.concurrent.CompletableFuture; @@ -31,6 +33,6 @@ public static CompletableFuture critique(Problems.Memory memory " 1. Provide a critique and determine if the answer truly accomplishes the goal.\n" + " \n"); - return Problems.Memory.getAiScope().prompt(prompt, Critique.class); + return Problems.Memory.getAiScope().prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, prompt, Critique.class); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Problems.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Problems.java index 44ca246e9..dfbc53e00 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Problems.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Problems.java @@ -1,14 +1,15 @@ package com.xebia.functional.xef.java.auto.jdk21.tot; -import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.truncateText; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import org.jetbrains.annotations.Nullable; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.stream.Stream; -import org.jetbrains.annotations.Nullable; + +import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.truncateText; public class Problems { @@ -82,27 +83,27 @@ static class Memory implements AutoCloseable { public Problem problem; public List> history; - private static AIScope aiScope = null; + private static PlatformConversation aiScope = null; public Memory(Problem problem, List> history) { this.problem = problem; this.history = history; - checkAIScope(); + checkPlatformConversation(); } public Memory addResult(Solutions.Solution result) { List> historyUpdate = Stream.concat(this.history.stream(), Stream.of(result)).toList(); - checkAIScope(); + checkPlatformConversation(); return new Memory<>(this.problem, historyUpdate); } - private static void checkAIScope() { + private static void checkPlatformConversation() { if(aiScope == null){ - aiScope = new AIScope(new ExecutionContext(Executors.newSingleThreadExecutor())); + aiScope = OpenAI.conversation(); } } - public static AIScope getAiScope() { + public static PlatformConversation getAiScope() { return aiScope; } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Solutions.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Solutions.java index c529adbc5..21e0750ce 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Solutions.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk21/tot/Solutions.java @@ -1,5 +1,7 @@ package com.xebia.functional.xef.java.auto.jdk21.tot; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.renderHistory; import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.truncateText; @@ -54,7 +56,7 @@ public static Solution solution(Problems.Memory memory, " \n"; try { - return Problems.Memory.getAiScope().prompt(enhancedPrompt, Solution.class).get(); + return Problems.Memory.getAiScope().prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, enhancedPrompt, Solution.class).get(); } catch (Exception e) { System.err.printf("Solutions.solution enhancedPrompt threw exception: %s - %s\n", e.getClass().getName(), e.getMessage()); diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/ASCIIArt.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/ASCIIArt.java index d3a1bfc5b..a59e83bc6 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/ASCIIArt.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/ASCIIArt.java @@ -1,14 +1,16 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; public class ASCIIArt { public String art; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("ASCII art of a cat dancing", ASCIIArt.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "ASCII art of a cat dancing", ASCIIArt.class) .thenAccept(art -> System.out.println(art.art)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Animals.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Animals.java index d50aada5f..c54d0aedf 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Animals.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Animals.java @@ -1,23 +1,25 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class Animals { - private final AIScope scope; + private final PlatformConversation scope; - public Animals(AIScope scope) { + public Animals(PlatformConversation scope) { this.scope = scope; } public CompletableFuture uniqueAnimal() { - return scope.prompt("A unique animal species.", Animal.class); + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A unique animal species.", Animal.class); } public CompletableFuture groundbreakingInvention() { - return scope.prompt("A groundbreaking invention from the 20th century.", Invention.class); + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A groundbreaking invention from the 20th century.", Invention.class); } public CompletableFuture story(Animal animal, Invention invention) { @@ -25,7 +27,7 @@ public CompletableFuture story(Animal animal, Invention invention) { "Write a short story of 500 words that involves the following elements:" + "1. A unique animal species called ${animal.name} that lives in " + animal.habitat + " and has a diet of " + animal.diet + "." + "2. A groundbreaking invention from the 20th century called " + invention.name + " , invented by " + invention.inventor + " in " + invention.year + ", which serves the purpose of " + invention.purpose + "."; - return scope.prompt(storyPrompt, Story.class); + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, storyPrompt, Story.class); } public static class Animal { @@ -52,7 +54,7 @@ public void tell() { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { + try (PlatformConversation scope = OpenAI.conversation()) { Animals animals = new Animals(scope); animals.uniqueAnimal() .thenCompose(animal -> animals.groundbreakingInvention() diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Book.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Book.java index d52b98f18..96a8d99a8 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Book.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Book.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; public class Book { @@ -10,8 +12,8 @@ public class Book { public String summary; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("To Kill a Mockingbird by Harper Lee summary.", Book.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "To Kill a Mockingbird by Harper Lee summary.", Book.class) .thenAccept(book -> System.out.println("To Kill a Mockingbird summary:\n" + book.summary)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Books.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Books.java index d5cb444a9..e47c064ef 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Books.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Books.java @@ -1,15 +1,16 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; import jakarta.validation.constraints.NotNull; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class Books { - private final AIScope scope; + private final PlatformConversation scope; - public Books(AIScope scope) { + public Books(PlatformConversation scope) { this.scope = scope; } @@ -31,11 +32,11 @@ public String toString() { } public CompletableFuture bookSelection(String topic) { - return scope.prompt("Give me a selection of books about " + topic, Book.class); + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Give me a selection of books about " + topic, Book.class); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { + try (PlatformConversation scope = OpenAI.conversation()) { Books books = new Books(scope); books.bookSelection("artificial intelligence") .thenAccept(System.out::println) diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/BreakingNews.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/BreakingNews.java index ff0cb035d..4f04456f4 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/BreakingNews.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/BreakingNews.java @@ -1,6 +1,9 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.CompletableFuture; @@ -20,17 +23,18 @@ public String toString() { '}'; } - private static CompletableFuture writeParagraph(AIScope scope) { + private static CompletableFuture writeParagraph(PlatformConversation scope) { String currentDate = dtf.format(now); - return scope.prompt("write a paragraph of about 300 words about: " + currentDate + " Covid News", BreakingNews.class) + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "write a paragraph of about 300 words about: " + currentDate + " Covid News", BreakingNews.class) .thenAccept(breakingNews -> System.out.println(currentDate + " Covid news summary:\n" + breakingNews)); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { + try (PlatformConversation scope = OpenAI.conversation()) { String currentDate = dtf.format(now); - scope.addContext(scope.search(currentDate + " Covid News").get()); + Search search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search(currentDate + " Covid News").get()); writeParagraph(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/ChessAI.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/ChessAI.java index d51d9a97e..1b12bc935 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/ChessAI.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/ChessAI.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; @@ -31,7 +33,8 @@ private static class GameState { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { + PlatformConversation scope = OpenAI.conversation(); + try { List moves = new ArrayList<>(); boolean gameEnded = false; String winner = ""; @@ -40,39 +43,41 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc String currentPlayer = ((moves.size() % 2) == 0) ? "Player 1 (White)" : "Player 2 (Black)"; String prompt = String.format(""" - |%s, it's your turn. - |Previous moves: %s - |Make your next move:""", - currentPlayer, - moves.stream().map(ChessMove::toString).collect(Collectors.joining(", "))); + |%s, it's your turn. + |Previous moves: %s + |Make your next move:""", + currentPlayer, + moves.stream().map(ChessMove::toString).collect(Collectors.joining(", "))); - ChessMove move = scope.prompt(prompt, ChessMove.class).get(); + ChessMove move = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, prompt, ChessMove.class).get(); moves.add(move); // Update boardState according to move.move // ... String boardPrompt = String.format(""" - Given the following chess moves: %s, - generate a chess board on a table with appropriate emoji representations for each move and piece. - Add a brief description of the move and it's implications""", - moves.stream().map(it -> it.player + ":" + it.move).collect(Collectors.joining(", "))); + Given the following chess moves: %s, + generate a chess board on a table with appropriate emoji representations for each move and piece. + Add a brief description of the move and it's implications""", + moves.stream().map(it -> it.player + ":" + it.move).collect(Collectors.joining(", "))); - ChessBoard chessBoard= scope.prompt(boardPrompt, ChessBoard.class).get(); + ChessBoard chessBoard = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, boardPrompt, ChessBoard.class).get(); System.out.println("Current board:\n" + chessBoard.board); String gameStatePrompt = String.format(""" - Given the following chess moves: %s, - has the game ended (win, draw, or stalemate)?""", - moves.stream().map(ChessMove::toString).collect(Collectors.joining(", "))); + Given the following chess moves: %s, + has the game ended (win, draw, or stalemate)?""", + moves.stream().map(ChessMove::toString).collect(Collectors.joining(", "))); - GameState gameState = scope.prompt(gameStatePrompt, GameState.class).get(); + GameState gameState = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, gameStatePrompt, GameState.class).get(); gameEnded = gameState.ended; winner = gameState.winner; } System.out.println("Game over. Final move: " + moves.get(moves.size() - 1) + ", Winner: " + winner); + } finally { + scope.close(); } } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Colors.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Colors.java index 6ff74b74d..123969893 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Colors.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Colors.java @@ -1,6 +1,7 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; import java.util.List; import java.util.concurrent.ExecutionException; @@ -9,10 +10,10 @@ public class Colors { public List colors; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("a selection of 10 beautiful colors that go well together", Colors.class) + PlatformConversation scope = OpenAI.conversation(); + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "a selection of 10 beautiful colors that go well together", Colors.class) .thenAccept(colors -> System.out.println("Colors:\n" + colors.colors)) .get(); - } + } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/DivergentTasks.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/DivergentTasks.java index 4058726c3..464831646 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/DivergentTasks.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/DivergentTasks.java @@ -1,6 +1,9 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; + import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -8,14 +11,15 @@ public class DivergentTasks { public Long numberOfMedicalNeedlesInWorld; - private static CompletableFuture numberOfMedical(AIScope scope) { - return scope.prompt("Provide the number of medical needles in the world", DivergentTasks.class) + private static CompletableFuture numberOfMedical(PlatformConversation scope) { + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Provide the number of medical needles in the world", DivergentTasks.class) .thenAccept(numberOfNeedles -> System.out.println("Needles in world:\n" + numberOfNeedles.numberOfMedicalNeedlesInWorld)); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.addContext(scope.search("Estimate amount of medical needles in the world").get()); + try (PlatformConversation scope = OpenAI.conversation()) { + Search search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search("Estimate amount of medical needles in the world").get()); numberOfMedical(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Employee.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Employee.java index 5c349cf1b..15fce6613 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Employee.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Employee.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; public class Employee { @@ -27,8 +29,8 @@ private static class Company { "Use the information provided."; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt(complexPrompt, Employee.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, complexPrompt, Employee.class) .thenAccept(employeeData -> System.out.println( "Employee Information:\n\n" + "Name: " + employeeData.firstName + " " + employeeData.lastName + "\n" + diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Fact.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Fact.java index fe8e03dcc..2ae0977c5 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Fact.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Fact.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; public class Fact { @@ -36,9 +38,9 @@ public String toString() { public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - FactClass fact1 = scope.prompt("A fascinating fact about you", FactClass.class).get(); - FactClass fact2 = scope.prompt("An interesting fact about me", FactClass.class).get(); + try (PlatformConversation scope = OpenAI.conversation()) { + FactClass fact1 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A fascinating fact about you", FactClass.class).get(); + FactClass fact2 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "An interesting fact about me", FactClass.class).get(); String riddlePrompt = ""+ "Create a riddle that combines the following facts:\n\n" + @@ -46,7 +48,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc "Fact 1: " + fact1.content + "\n" + "Fact 2: " + fact2.content; - scope.prompt(riddlePrompt, Riddle.class) + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, riddlePrompt, Riddle.class) .thenAccept(riddle -> System.out.println("Riddle:\n\n" + riddle)).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Love.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Love.java index 8b23b5eda..f759dc333 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Love.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Love.java @@ -1,14 +1,16 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.ExecutionException; public class Love { public List loveList; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("tell me you like me with just emojis", Love.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "tell me you like me with just emojis", Love.class) .thenAccept(love -> System.out.println(love.loveList)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Markets.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Markets.java index 52f39a821..a56891aba 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Markets.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Markets.java @@ -1,6 +1,9 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; @@ -21,21 +24,21 @@ public String toString() { '}'; } - private static CompletableFuture stockMarketSummary(AIScope scope) { + private static CompletableFuture stockMarketSummary(PlatformConversation scope) { String news = "|" + "|Write a short summary of the stock market results given the provided context."; - return scope.prompt(news, Markets.class) + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, news, Markets.class) .thenAccept(markets -> System.out.println(markets)); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { + try (PlatformConversation scope = OpenAI.conversation()) { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd/M/yyyy"); LocalDateTime now = LocalDateTime.now(); String currentDate = dtf.format(now); - - scope.addContext(scope.search(currentDate + "Stock market results, raising stocks, decreasing stocks").get()); + Search search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search(currentDate + "Stock market results, raising stocks, decreasing stocks").get()); stockMarketSummary(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/MealPlan.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/MealPlan.java index b14f53c54..4626bfd2c 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/MealPlan.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/MealPlan.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -31,14 +33,15 @@ public String toString() { '}'; } - private static CompletableFuture mealPlan(AIScope scope) { - return scope.prompt("Meal plan for the week for a person with gall bladder stones that includes 5 recipes.", MealPlan.class) + private static CompletableFuture mealPlan(PlatformConversation scope) { + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Meal plan for the week for a person with gall bladder stones that includes 5 recipes.", MealPlan.class) .thenAccept(mealPlan -> System.out.println(mealPlan)); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.addContext(scope.search("gall bladder stones meals").get()); + try (PlatformConversation scope = OpenAI.conversation()) { + Search search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search("gall bladder stones meals").get()); mealPlan(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/MeaningOfLife.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/MeaningOfLife.java index 96fc54e5a..cb1c99cb1 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/MeaningOfLife.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/MeaningOfLife.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.ExecutionException; @@ -8,8 +10,8 @@ public class MeaningOfLife { public List mainTheories; public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("What are the main theories about the meaning of life", MeaningOfLife.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "What are the main theories about the meaning of life", MeaningOfLife.class) .thenAccept(meaningOfLife -> System.out.println("There are several theories about the meaning of life:\n" + meaningOfLife.mainTheories)) .get(); diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Movies.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Movies.java index 0f3bed0ea..f18be8497 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Movies.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Movies.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; public class Movies { @@ -18,8 +20,8 @@ public String toString() { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("Please provide a movie title, genre and director for the Inception movie", Movie.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Please provide a movie title, genre and director for the Inception movie", Movie.class) .thenAccept(movie -> System.out.println(movie)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/PDFDocument.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/PDFDocument.java index 2c80f8565..f329b55f4 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/PDFDocument.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/PDFDocument.java @@ -3,9 +3,13 @@ import static com.xebia.functional.xef.textsplitters.TokenTextSplitterKt.TokenTextSplitter; import com.xebia.functional.tokenizer.ModelType; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; import com.xebia.functional.xef.java.auto.jdk21.util.ConsoleUtil; +import com.xebia.functional.xef.reasoning.pdf.PDF; import com.xebia.functional.xef.textsplitters.TextSplitter; + +import java.util.List; import java.util.concurrent.CompletableFuture; public class PDFDocument { @@ -19,7 +23,7 @@ public static class AIResponse { private static final String PDF_URL = "https://people.cs.ksu.edu/~schmidt/705a/Scala/Programming-in-Scala.pdf"; - private static CompletableFuture askQuestion(AIScope scope) { + private static CompletableFuture askQuestion(PlatformConversation scope) { System.out.println("Enter your question ( to exit): "); @@ -27,7 +31,7 @@ private static CompletableFuture askQuestion(AIScope scope) { if (line == null || line.isBlank()) { return CompletableFuture.completedFuture(null); } else { - scope.prompt(line, AIResponse.class) + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, line, AIResponse.class) .thenAccept(aiRes -> System.out.println(aiRes.answer + "\n---\n" + aiRes.source + "\n---\n")); @@ -37,9 +41,10 @@ private static CompletableFuture askQuestion(AIScope scope) { public static void main(String[] args) throws Exception { - TextSplitter textSplitter = TokenTextSplitter(ModelType.getDEFAULT_SPLITTER_MODEL(), 100, 50); - try (AIScope scope = new AIScope()) { - scope.addContext(scope.pdf(PDF_URL, textSplitter).get()); + try (PlatformConversation scope = OpenAI.conversation()) { + PDF pdf = new PDF(OpenAI.FromEnvironment.DEFAULT_CHAT, + OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, scope); + scope.addContext(List.of(pdf.readPDFFromUrl.readPDFFromUrl(PDF_URL).get())); askQuestion(scope).get(); } finally { diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Persons.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Persons.java index a41861ee0..aca84d45e 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Persons.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Persons.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; public class Persons { @@ -16,8 +18,8 @@ public String toString() { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("What is your name and age?", Person.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "What is your name and age?", Person.class) .thenAccept(person -> System.out.println(person)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Planets.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Planets.java index bcda78b52..4ba711a47 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Planets.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Planets.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -18,9 +20,9 @@ static class Moon { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - CompletableFuture earth = scope.prompt("Information about Earth and its moon.", Planet.class); - CompletableFuture mars = scope.prompt("Information about Mars and its moons.", Planet.class); + try (PlatformConversation scope = OpenAI.conversation()) { + CompletableFuture earth = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Information about Earth and its moon.", Planet.class); + CompletableFuture mars = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Information about Mars and its moons.", Planet.class); System.out.println("Celestial bodies information:\n\n" + planetInfo(earth.get()) + "\n\n" + planetInfo(mars.get())); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Poems.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Poems.java index 31d7785fb..a6e1d049c 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Poems.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Poems.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -11,17 +13,17 @@ public static class Poem { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - CompletableFuture poem1 = scope.prompt("A short poem about the beauty of nature.", Poem.class); - CompletableFuture poem2 = scope.prompt("A short poem about the power of technology.", Poem.class); - CompletableFuture poem3 = scope.prompt("A short poem about the wisdom of artificial intelligence.", Poem.class); + try (PlatformConversation scope = OpenAI.conversation()) { + CompletableFuture poem1 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A short poem about the beauty of nature.", Poem.class); + CompletableFuture poem2 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A short poem about the power of technology.", Poem.class); + CompletableFuture poem3 = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A short poem about the wisdom of artificial intelligence.", Poem.class); String combinedPoems = String.format("%s\n\n%s\n\n%s", poem1.get().content, poem2.get().content, poem3.get().content); String newPoemPrompt = "Write a new poem that combines ideas from the following themes: the beauty " + "of nature, the power of technology, and the wisdom of artificial intelligence. Here are some " + "examples of poems on these themes: " + combinedPoems; - scope.prompt(newPoemPrompt, Poem.class). + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, newPoemPrompt, Poem.class). thenAccept(poem -> System.out.printf("New Poem:\n\n" + poem.content)) .get(); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Populations.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Populations.java index 54039543c..f3d443a47 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Populations.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Populations.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -17,10 +19,10 @@ static class Image { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - CompletableFuture cadiz = scope.prompt("What is the population of Cádiz, Spain.", Population.class); - CompletableFuture seattle = scope.prompt("What is the population of Seattle, WA.", Population.class); - CompletableFuture img = scope.prompt("A hybrid city of Cádiz, Spain and Seattle, US.", Image.class); + try (PlatformConversation scope = OpenAI.conversation()) { + CompletableFuture cadiz = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "What is the population of Cádiz, Spain.", Population.class); + CompletableFuture seattle = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "What is the population of Seattle, WA.", Population.class); + CompletableFuture img = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "A hybrid city of Cádiz, Spain and Seattle, US.", Image.class); System.out.println(img.get()); System.out.println("The population of Cádiz is " + cadiz.get().population + " and the population of Seattle is " + seattle.get().population); } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Recipes.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Recipes.java index 51e448ae7..9e8547093 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Recipes.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Recipes.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.List; import java.util.concurrent.ExecutionException; @@ -12,8 +14,8 @@ static class Recipe { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - Recipe recipe = scope.prompt("Recipe for chocolate chip cookies.", Recipe.class).get(); + try (PlatformConversation scope = OpenAI.conversation()) { + Recipe recipe = scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Recipe for chocolate chip cookies.", Recipe.class).get(); System.out.println("The recipe for " + recipe.name + " is " + recipe.ingredients ); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/TopAttractions.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/TopAttractions.java index 797fc4e66..856f55666 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/TopAttractions.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/TopAttractions.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; public class TopAttractions { @@ -24,8 +26,8 @@ static class Weather { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("Top attraction in Cádiz, Spain.", TopAttraction.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Top attraction in Cádiz, Spain.", TopAttraction.class) .thenAccept(attraction -> System.out.println( "The top attraction in " + attraction.city.name + " is " + attraction.attractionName + "." + "Here's a brief description: " + attraction.description + "." + diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/TouristAttractions.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/TouristAttractions.java index 6e2c4fbcb..7203b21ba 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/TouristAttractions.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/TouristAttractions.java @@ -1,6 +1,8 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import java.util.concurrent.ExecutionException; public class TouristAttractions { @@ -12,8 +14,8 @@ static class TouristAttraction { } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.prompt("Statue of Liberty location and history.", TouristAttraction.class) + try (PlatformConversation scope = OpenAI.conversation()) { + scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Statue of Liberty location and history.", TouristAttraction.class) .thenAccept(statueOfLiberty -> System.out.println( statueOfLiberty.name + "is located in " + statueOfLiberty.location + " and has the following history: " + statueOfLiberty.history diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Weather.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Weather.java index e2f41d432..a6a6d281c 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Weather.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/Weather.java @@ -1,6 +1,9 @@ package com.xebia.functional.xef.java.auto.jdk8; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; +import com.xebia.functional.xef.reasoning.serpapi.Search; + import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -8,16 +11,17 @@ public class Weather { public List answer; - private static CompletableFuture clothesRecommend(AIScope scope) { - return scope.prompt("Knowing this forecast, what clothes do you recommend I should wear?", Weather.class) + private static CompletableFuture clothesRecommend(PlatformConversation scope) { + return scope.prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, "Knowing this forecast, what clothes do you recommend I should wear?", Weather.class) .thenAccept(weather -> System.out.println(weather.answer) ); } public static void main(String[] args) throws ExecutionException, InterruptedException { - try (AIScope scope = new AIScope()) { - scope.addContext(scope.search("Weather in Cádiz, Spain").get()); + try (PlatformConversation scope = OpenAI.conversation()) { + Search search = new Search(OpenAI.FromEnvironment.DEFAULT_CHAT, scope, 3); + scope.addContextFromArray(search.search("Weather in Cádiz, Spain").get()); clothesRecommend(scope).get(); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/gpt4all/Chat.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/gpt4all/Chat.java index 42c84b03a..8398ef571 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/gpt4all/Chat.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/gpt4all/Chat.java @@ -3,7 +3,7 @@ import com.xebia.functional.gpt4all.GPT4All; import com.xebia.functional.gpt4all.Gpt4AllModel; import com.xebia.functional.xef.auto.PromptConfiguration; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -11,6 +11,8 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; + +import com.xebia.functional.xef.auto.llm.openai.OpenAI; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -37,10 +39,8 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc * to provide embeddings for docs in contextScope. */ - try (AIScope scope = new AIScope(); - BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { - - System.out.println("🤖 Context loaded: " + scope.getExec().getContext()); + try (PlatformConversation scope = OpenAI.conversation(); + BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { System.out.println("\n🤖 Enter your question: "); diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/sql/DatabaseExample.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/sql/DatabaseExample.java deleted file mode 100644 index 617624649..000000000 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/sql/DatabaseExample.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.xebia.functional.xef.java.auto.jdk8.sql; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.xebia.functional.xef.auto.PromptConfiguration; -import com.xebia.functional.xef.auto.llm.openai.OpenAI; -import com.xebia.functional.xef.auto.llm.openai.OpenAIModel; -import com.xebia.functional.xef.java.auto.AIDatabase; -import com.xebia.functional.xef.java.auto.AIScope; -import com.xebia.functional.xef.java.auto.ExecutionContext; -import com.xebia.functional.xef.java.auto.jdk21.util.ConsoleUtil; -import com.xebia.functional.xef.sql.jdbc.JdbcConfig; -import java.io.PrintStream; -import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; -import org.jetbrains.annotations.NotNull; - -public class DatabaseExample { - - private static final OpenAIModel MODEL = new OpenAI().DEFAULT_CHAT; - private static PrintStream out = System.out; - private static ConsoleUtil util = new ConsoleUtil(); - - @NotNull - private static JdbcConfig getJdbcConfig() { - Map env = System.getenv(); - String vendor = env.getOrDefault("XEF_SQL_DB_VENDOR", "mysql"); - String host = env.getOrDefault("XEF_SQL_DB_HOST", "localhost"); - String username = env.getOrDefault("XEF_SQL_DB_USER", "user"); - String password = env.getOrDefault("XEF_SQL_DB_PASSWORD", "password"); - int port = Integer.parseInt(env.getOrDefault("XEF_SQL_DB_PORT", "3306")); - String database = env.getOrDefault("XEF_SQL_DB_DATABASE", "database"); - OpenAIModel model = MODEL; - - return new JdbcConfig(vendor, host, username, password, port, database, model); - } - - static final Function1 promptConfiguration = - (Function1) builder -> { - builder.docsInContext(50); - return Unit.INSTANCE; - }; - - public static void main(String[] args) throws Exception { - - ExecutionContext executionContext = new ExecutionContext(); - try (AIScope scope = new AIScope(new ObjectMapper(), executionContext)) { - AIDatabase database = new AIDatabase(getJdbcConfig(), executionContext); - - out.println("llmdb> Welcome to the LLMDB (An LLM interface to your SQL Database) !"); - out.println("llmdb> You can ask me questions about the database and I will try to answer them."); - out.println("llmdb> You can type `exit` to exit the program."); - out.println("llmdb> Loading recommended prompts..."); - - Arrays.stream(database.getInterestingPromptsForDatabase().get() - .split("\n")).forEach(it -> out.println("llmdb> " + it)); - - while (true) { - out.println("user> "); - String input = util.readLine(); - if (input.equals("exit")) break; - - try { - database.extendContext(database.promptQuery(input).get()); - CompletableFuture result = scope.promptMessage(MODEL, "|\n" + - " You are a database assistant that helps users to query and summarize results from the database.\n" + - " Instructions:\n" + - " 1. Summarize the information provided in the `Context` and follow to step 2.\n" + - " 2. If the information relates to the `input` then answer the question otherwise return just the summary.\n" + - " ```input\n" + - " " + input + " \n" + - " ```\n" + - " 3. Try to answer and provide information with as much detail as you can\n" + - " ", PromptConfiguration.Companion.build(promptConfiguration)); - - out.println(result.get()); - } catch (Exception e) { - out.println("llmdb> " + e.getMessage()); - e.printStackTrace(); - } - } - } - finally { - util.close(); - } - - } - - -} diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/ControlSignals.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/ControlSignals.java index 19a421566..1e1fd2370 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/ControlSignals.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/ControlSignals.java @@ -1,5 +1,7 @@ package com.xebia.functional.xef.java.auto.jdk8.tot; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import static com.xebia.functional.xef.java.auto.jdk8.tot.Rendering.renderHistory; import static com.xebia.functional.xef.java.auto.jdk8.tot.Rendering.truncateText; @@ -28,7 +30,7 @@ public static CompletableFuture controlSignal( " 5. Ensure the guidance accounts for previous answers in the `history`.\n" + " \n"); - return Problems.Memory.getAiScope().prompt(guidancePrompt, ControlSignal.class); + return Problems.Memory.getAiScope().prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, guidancePrompt, ControlSignal.class); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Critiques.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Critiques.java index 1fd33657d..0fdaf67a1 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Critiques.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Critiques.java @@ -1,5 +1,7 @@ package com.xebia.functional.xef.java.auto.jdk8.tot; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.truncateText; import java.util.concurrent.CompletableFuture; @@ -31,6 +33,6 @@ public static CompletableFuture critique(Problems.Memory memory " 1. Provide a critique and determine if the answer truly accomplishes the goal.\n" + " \n"); - return Problems.Memory.getAiScope().prompt(prompt, Critique.class); + return Problems.Memory.getAiScope().prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, prompt, Critique.class); } } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Problems.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Problems.java index 778085e5e..84010f76c 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Problems.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Problems.java @@ -2,10 +2,12 @@ import static com.xebia.functional.xef.java.auto.jdk21.tot.Rendering.truncateText; -import com.xebia.functional.xef.java.auto.AIScope; +import com.xebia.functional.xef.auto.PlatformConversation; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; + +import com.xebia.functional.xef.auto.llm.openai.OpenAI; import org.jetbrains.annotations.Nullable; public class Problems { @@ -81,27 +83,27 @@ static class Memory implements AutoCloseable { public Problem problem; public List> history; - private static AIScope aiScope = null; + private static PlatformConversation aiScope = null; public Memory(Problem problem, List> history) { this.problem = problem; this.history = history; - checkAIScope(); + checkPlatformConversation(); } public Memory addResult(Solutions.Solution result) { List> historyUpdate = Stream.concat(this.history.stream(), Stream.of(result)).toList(); - checkAIScope(); + checkPlatformConversation(); return new Memory<>(this.problem, historyUpdate); } - private static void checkAIScope() { - if(aiScope == null){ - aiScope = new AIScope(); + private static void checkPlatformConversation() { + if(aiScope == null) { + aiScope = OpenAI.conversation(); } } - public static AIScope getAiScope() { + public static PlatformConversation getAiScope() { return aiScope; } diff --git a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Solutions.java b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Solutions.java index 70b1018a2..21853735d 100644 --- a/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Solutions.java +++ b/examples/java/src/main/java/com/xebia/functional/xef/java/auto/jdk8/tot/Solutions.java @@ -1,5 +1,7 @@ package com.xebia.functional.xef.java.auto.jdk8.tot; +import com.xebia.functional.xef.auto.llm.openai.OpenAI; + import static com.xebia.functional.xef.java.auto.jdk8.tot.Rendering.renderHistory; import static com.xebia.functional.xef.java.auto.jdk8.tot.Rendering.truncateText; @@ -54,7 +56,7 @@ public static Solution solution(Problems.Memory memory, " \n"; try { - return Problems.Memory.getAiScope().prompt(enhancedPrompt, Solution.class).get(); + return Problems.Memory.getAiScope().prompt(OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, enhancedPrompt, Solution.class).get(); } catch (Exception e) { System.err.printf("Solutions.solution enhancedPrompt threw exception: %s - %s\n", e.getClass().getName(), e.getMessage()); diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/ASCIIArt.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/ASCIIArt.kt index b7117031b..07e3fab25 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/ASCIIArt.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/ASCIIArt.kt @@ -1,12 +1,12 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @Serializable data class ASCIIArt(val art: String) suspend fun main() { - val art: ASCIIArt = conversation { prompt("ASCII art of a cat dancing") } + val art: ASCIIArt = OpenAI.conversation { prompt("ASCII art of a cat dancing") } println(art) } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Animal.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Animal.kt index 77ba1f348..67a68e278 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Animal.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Animal.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @@ -13,7 +13,7 @@ data class Invention(val name: String, val inventor: String, val year: Int, val data class Story(val animal: Animal, val invention: Invention, val shortStory: String) suspend fun main() { - conversation { + OpenAI.conversation { val animal: Animal = prompt("A unique animal species.") val invention: Invention = prompt("A groundbreaking invention from the 20th century.") diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Book.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Book.kt index d6aaaf819..7f61ae2e6 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Book.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Book.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @@ -12,7 +12,7 @@ data class Book( ) suspend fun main() { - conversation { + OpenAI.conversation { val toKillAMockingbird: Book = prompt("To Kill a Mockingbird by Harper Lee summary.") println("To Kill a Mockingbird summary:\n ${toKillAMockingbird.summary}") } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/BreakingNews.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/BreakingNews.kt index d3d4b77e1..6b99662e9 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/BreakingNews.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/BreakingNews.kt @@ -1,18 +1,20 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.agents.search -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt +import com.xebia.functional.xef.reasoning.serpapi.Search import java.text.SimpleDateFormat -import java.util.Date +import java.util.* import kotlinx.serialization.Serializable @Serializable data class BreakingNewsAboutCovid(val summary: String) suspend fun main() { - conversation { + OpenAI.conversation { val sdf = SimpleDateFormat("dd/M/yyyy") val currentDate = sdf.format(Date()) + val search = + Search(model = OpenAI.FromEnvironment.DEFAULT_CHAT, scope = this, maxResultsInContext = 3) val docs = search("$currentDate Covid News") addContext(docs) val news: BreakingNewsAboutCovid = diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/ChessAI.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/ChessAI.kt index e651d02b8..ff665cc24 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/ChessAI.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/ChessAI.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @@ -11,7 +11,7 @@ import kotlinx.serialization.Serializable @Serializable data class GameState(val ended: Boolean, val winner: String) suspend fun main() { - conversation { + OpenAI.conversation { val moves = mutableListOf() var gameEnded = false var winner = "" diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Colors.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Colors.kt index 1753a873b..cb952904a 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Colors.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Colors.kt @@ -1,13 +1,13 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @Serializable data class Colors(val colors: List) suspend fun main() { - conversation { + OpenAI.conversation { val colors: Colors = prompt("a selection of 10 beautiful colors that go well together") println(colors) } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt index 208ac0e1d..8cf0fdb53 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt @@ -1,10 +1,10 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.promptMessage suspend fun main() { - conversation { + OpenAI.conversation { val emailMessage = """ |You are a Marketing Responsible and have the information about different products. You have to prepare diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/DivergentTasks.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/DivergentTasks.kt index afeb19409..8828ab076 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/DivergentTasks.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/DivergentTasks.kt @@ -1,14 +1,15 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.agents.search -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt +import com.xebia.functional.xef.reasoning.serpapi.Search import kotlinx.serialization.Serializable @Serializable data class NumberOfMedicalNeedlesInWorld(val numberOfNeedles: Long) suspend fun main() { - conversation { + OpenAI.conversation { + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, this) addContext(search("Estimate amount of medical needles in the world")) val needlesInWorld: NumberOfMedicalNeedlesInWorld = prompt("Provide the number of medical needles in the world") diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Employee.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Employee.kt index 1fa95c3fa..c6964355f 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Employee.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Employee.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @@ -18,7 +18,7 @@ data class Employee( @Serializable data class Company(val name: String, val address: Address) suspend fun main() { - conversation { + OpenAI.conversation { val complexPrompt = """| |Provide made up information for an Employee that includes their first name, last name, age, position, and their company's name and address (street, city, and country). diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Fact.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Fact.kt index 0cc604ca6..6c75c6697 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Fact.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Fact.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable @Serializable data class Riddle(val fact1: Fact, val fact2: Fact, val riddle: String) suspend fun main() { - conversation { + OpenAI.conversation { val fact1: Fact = prompt("A fascinating fact about you") val fact2: Fact = prompt("An interesting fact about me") diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Love.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Love.kt index ee20b561f..cd743a5fe 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Love.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Love.kt @@ -1,10 +1,10 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.promptMessage suspend fun main() { - conversation { + OpenAI.conversation { val love: String = promptMessage("tell me you like me with just emojis") println(love) } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Markets.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Markets.kt index b2d400247..84ac2634b 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Markets.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Markets.kt @@ -1,8 +1,8 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.agents.search -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt +import com.xebia.functional.xef.reasoning.serpapi.Search import java.text.SimpleDateFormat import java.util.Date import kotlinx.serialization.Serializable @@ -15,9 +15,10 @@ data class MarketNews( ) suspend fun main() { - conversation { + OpenAI.conversation { val sdf = SimpleDateFormat("dd/M/yyyy") val currentDate = sdf.format(Date()) + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, this) addContext(search("$currentDate Stock market results, raising stocks, decreasing stocks")) val news: MarketNews = prompt( diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/MealPlan.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/MealPlan.kt index 1df3d353e..ac53199d1 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/MealPlan.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/MealPlan.kt @@ -1,14 +1,15 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.agents.search -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt +import com.xebia.functional.xef.reasoning.serpapi.Search import kotlinx.serialization.Serializable @Serializable data class MealPlan(val name: String, val recipes: List) suspend fun main() { - conversation { + OpenAI.conversation { + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, this) addContext(search("gall bladder stones meals")) val mealPlan: MealPlan = prompt( diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/MeaningOfLife.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/MeaningOfLife.kt index 9b7fcdd8d..eac0a7336 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/MeaningOfLife.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/MeaningOfLife.kt @@ -1,13 +1,13 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @Serializable data class MeaningOfLife(val mainTheories: List) suspend fun main() { - conversation { + OpenAI.conversation { val meaningOfLife: MeaningOfLife = prompt("What are the main theories about the meaning of life") println("There are several theories about the meaning of life:\n ${meaningOfLife.mainTheories}") diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Movie.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Movie.kt index bc5c230c5..c6bd4e240 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Movie.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Movie.kt @@ -1,13 +1,13 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @Serializable data class Movie(val title: String, val genre: String, val director: String) suspend fun main() { - conversation { + OpenAI.conversation { val movie: Movie = prompt("Please provide a movie title, genre and director for the Inception movie") println("The movie ${movie.title} is a ${movie.genre} film directed by ${movie.director}.") diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/PDFDocument.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/PDFDocument.kt index a3ca89237..d959495f0 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/PDFDocument.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/PDFDocument.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import com.xebia.functional.xef.pdf.pdf import kotlinx.serialization.Serializable @@ -9,12 +9,13 @@ import kotlinx.serialization.Serializable const val pdfUrl = "https://people.cs.ksu.edu/~schmidt/705a/Scala/Programming-in-Scala.pdf" -suspend fun main() = conversation { - addContext(pdf(url = pdfUrl)) - while (true) { - print("Enter your question: ") - val line = readlnOrNull() ?: break - val response: AIResponse = prompt(line) - println("${response.answer}\n---\n${response.source}\n---\n") +suspend fun main() = + OpenAI.conversation { + addContext(pdf(url = pdfUrl)) + while (true) { + print("Enter your question: ") + val line = readlnOrNull() ?: break + val response: AIResponse = prompt(line) + println("${response.answer}\n---\n${response.source}\n---\n") + } } -} diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Person.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Person.kt index 5d4a6dce8..d35c749dd 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Person.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Person.kt @@ -1,13 +1,13 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @Serializable data class Person(val name: String, val age: Int) suspend fun main() { - conversation { + OpenAI.conversation { val person: Person = prompt("What is your name and age?") println("Hello ${person.name}, you are ${person.age} years old.") } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Planet.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Planet.kt index dfd35e11c..cc0643aaf 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Planet.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Planet.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @@ -9,17 +9,18 @@ data class Planet(val name: String, val distanceFromSun: Double, val moons: List @Serializable data class Moon(val name: String, val distanceFromPlanet: Double) -suspend fun main() = conversation { - val earth: Planet = prompt("Information about Earth and its moon.") - val mars: Planet = prompt("Information about Mars and its moons.") +suspend fun main() = + OpenAI.conversation { + val earth: Planet = prompt("Information about Earth and its moon.") + val mars: Planet = prompt("Information about Mars and its moons.") - fun planetInfo(planet: Planet): String { - return """${planet.name} is ${planet.distanceFromSun} million km away from the Sun. + fun planetInfo(planet: Planet): String { + return """${planet.name} is ${planet.distanceFromSun} million km away from the Sun. |It has the following moons: |${planet.moons.joinToString("\n") { " - ${it.name}: ${it.distanceFromPlanet} km away from ${planet.name}" }} """ - .trimMargin() - } + .trimMargin() + } - println("Celestial bodies information:\n\n${planetInfo(earth)}\n\n${planetInfo(mars)}") -} + println("Celestial bodies information:\n\n${planetInfo(earth)}\n\n${planetInfo(mars)}") + } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Poem.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Poem.kt index 6a19c9c78..c06ba8328 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Poem.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Poem.kt @@ -1,27 +1,28 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @Serializable data class Poem(val title: String, val content: String) -suspend fun main() = conversation { - val poem1: Poem = prompt("A short poem about the beauty of nature.") - val poem2: Poem = prompt("A short poem about the power of technology.") - val poem3: Poem = prompt("A short poem about the wisdom of artificial intelligence.") +suspend fun main() = + OpenAI.conversation { + val poem1: Poem = prompt("A short poem about the beauty of nature.") + val poem2: Poem = prompt("A short poem about the power of technology.") + val poem3: Poem = prompt("A short poem about the wisdom of artificial intelligence.") - val combinedPoemContent = "${poem1.content}\n\n${poem2.content}\n\n${poem3.content}" + val combinedPoemContent = "${poem1.content}\n\n${poem2.content}\n\n${poem3.content}" - val newPoemPrompt = - """ + val newPoemPrompt = + """ Write a new poem that combines ideas from the following themes: the beauty of nature, the power of technology, and the wisdom of artificial intelligence. Here are some examples of poems on these themes: $combinedPoemContent """ - .trimIndent() + .trimIndent() - val newPoem: Poem = prompt(newPoemPrompt) + val newPoem: Poem = prompt(newPoemPrompt) - println("New Poem:\n\n${newPoem.content}") -} + println("New Poem:\n\n${newPoem.content}") + } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Population.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Population.kt index 687ad2461..8e59681d7 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Population.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Population.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.image import kotlinx.serialization.Serializable @@ -12,7 +12,8 @@ data class Image( val url: String, ) -suspend fun main() = conversation { - val img: Image = image("") - println(img) -} +suspend fun main() = + OpenAI.conversation { + val img: Image = image("") + println(img) + } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Recipe.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Recipe.kt index b2fe15b09..532df03b3 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Recipe.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Recipe.kt @@ -1,12 +1,13 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @Serializable data class Recipe(val name: String, val ingredients: List) -suspend fun main() = conversation { - val recipe: Recipe = prompt("Recipe for chocolate chip cookies.") - println("The recipe for ${recipe.name} is ${recipe.ingredients}") -} +suspend fun main() = + OpenAI.conversation { + val recipe: Recipe = prompt("Recipe for chocolate chip cookies.") + println("The recipe for ${recipe.name} is ${recipe.ingredients}") + } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/TopAttraction.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/TopAttraction.kt index 11c3bccd9..74398f31e 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/TopAttraction.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/TopAttraction.kt @@ -1,6 +1,6 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @@ -16,14 +16,15 @@ data class TopAttraction( @Serializable data class Weather(val city: City, val temperature: Double, val description: String) -suspend fun main() = conversation { - val nearbyTopAttraction: TopAttraction = prompt("Top attraction in Cádiz, Spain.") - println( - """ +suspend fun main() = + OpenAI.conversation { + val nearbyTopAttraction: TopAttraction = prompt("Top attraction in Cádiz, Spain.") + println( + """ |The top attraction in ${nearbyTopAttraction.city.name} is ${nearbyTopAttraction.attractionName}. |Here's a brief description: ${nearbyTopAttraction.description}. |The weather in ${nearbyTopAttraction.city.name} is ${nearbyTopAttraction.weather.temperature} degrees Celsius and ${nearbyTopAttraction.weather.description}. |""" - .trimMargin() - ) -} + .trimMargin() + ) + } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/TouristAttraction.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/TouristAttraction.kt index e384d818b..fdcc5d4c8 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/TouristAttraction.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/TouristAttraction.kt @@ -1,17 +1,18 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @Serializable data class TouristAttraction(val name: String, val location: String, val history: String) -suspend fun main() = conversation { - val statueOfLiberty: TouristAttraction = prompt("Statue of Liberty location and history.") - println( - """|${statueOfLiberty.name} is located in ${statueOfLiberty.location} and has the following history: +suspend fun main() = + OpenAI.conversation { + val statueOfLiberty: TouristAttraction = prompt("Statue of Liberty location and history.") + println( + """|${statueOfLiberty.name} is located in ${statueOfLiberty.location} and has the following history: |${statueOfLiberty.history}""" - .trimMargin() - ) -} + .trimMargin() + ) + } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Weather.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Weather.kt index 0966f1986..ff7410006 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Weather.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Weather.kt @@ -1,8 +1,8 @@ package com.xebia.functional.xef.auto -import com.xebia.functional.xef.agents.search -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.promptMessage +import com.xebia.functional.xef.reasoning.serpapi.Search import io.github.oshai.kotlinlogging.KotlinLogging suspend fun main() { @@ -14,7 +14,9 @@ suspend fun main() { logger.info { answer } } -private suspend fun getQuestionAnswer(question: String): String = conversation { - addContext(search("Weather in Cádiz, Spain")) - promptMessage(question) -} +private suspend fun getQuestionAnswer(question: String): String = + OpenAI.conversation { + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, this) + addContext(search("Weather in Cádiz, Spain")) + promptMessage(question) + } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/expressions/WorkoutPlanProgram.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/expressions/WorkoutPlanProgram.kt index 94705625e..a01959f37 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/expressions/WorkoutPlanProgram.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/expressions/WorkoutPlanProgram.kt @@ -2,11 +2,10 @@ package com.xebia.functional.xef.auto.expressions import com.xebia.functional.xef.auto.Conversation import com.xebia.functional.xef.auto.llm.openai.OpenAI -import com.xebia.functional.xef.auto.llm.openai.conversation import com.xebia.functional.xef.llm.ChatWithFunctions import com.xebia.functional.xef.prompt.expressions.Expression import com.xebia.functional.xef.prompt.expressions.ExpressionResult -import com.xebia.functional.xef.reasoning.search.Search +import com.xebia.functional.xef.reasoning.serpapi.Search import com.xebia.functional.xef.reasoning.tools.LLMTool import com.xebia.functional.xef.reasoning.tools.Tool @@ -45,7 +44,7 @@ suspend fun taskSplitter( suspend fun main() { - conversation { + OpenAI.conversation { val model = OpenAI().DEFAULT_SERIALIZATION val math = LLMTool.create( diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/fields/Book.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/fields/Book.kt index a22bf1174..5dab2d548 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/fields/Book.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/fields/Book.kt @@ -2,7 +2,7 @@ package com.xebia.functional.xef.auto.fields import com.xebia.functional.xef.auto.Description import com.xebia.functional.xef.auto.conversation -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import kotlinx.serialization.Serializable @@ -14,7 +14,7 @@ data class Book( ) suspend fun main() { - conversation { + OpenAI.conversation { val toKillAMockingbird: Book = prompt("To Kill a Mockingbird by Harper Lee") println(toKillAMockingbird) } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/fields/NewsSummary.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/fields/NewsSummary.kt index f2be2e86a..3a9ecd531 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/fields/NewsSummary.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/fields/NewsSummary.kt @@ -1,10 +1,10 @@ package com.xebia.functional.xef.auto.fields -import com.xebia.functional.xef.agents.search import com.xebia.functional.xef.auto.Description import com.xebia.functional.xef.auto.conversation -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt +import com.xebia.functional.xef.reasoning.serpapi.Search import java.time.LocalDate import kotlinx.serialization.Serializable @@ -21,7 +21,8 @@ data class NewsItems( ) suspend fun main() { - conversation { + OpenAI.conversation { + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, this) addContext(search("Covid news on ${LocalDate.now()}")) val news: NewsItems = prompt() println(news) diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/gpc/Chat.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/gpc/Chat.kt index 85e7f5bb2..2284e18c9 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/gpc/Chat.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/gpc/Chat.kt @@ -1,10 +1,10 @@ package com.xebia.functional.xef.auto.gpc -import com.xebia.functional.gpt4all.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.gcp.GcpChat suspend fun main() { - conversation { + OpenAI.conversation { val gcp = autoClose( GcpChat("us-central1-aiplatform.googleapis.com", "xef-demo", "codechat-bison@001", "token") diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/gpt4all/Chat.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/gpt4all/Chat.kt index 47ebffdbb..d97444864 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/gpt4all/Chat.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/gpt4all/Chat.kt @@ -16,18 +16,18 @@ suspend fun main() { val url = "https://huggingface.co/nomic-ai/ggml-replit-code-v1-3b/resolve/main/ggml-replit-code-v1-3b.bin" val modelPath: Path = Path.of(path) - val GPT4All = GPT4All(url, modelPath) + val model = GPT4All(url, modelPath) println("🤖 GPT4All loaded: $GPT4All") /** * Uses internally [HuggingFaceLocalEmbeddings] default of "sentence-transformers", * "msmarco-distilbert-dot-v5" to provide embeddings for docs in contextScope. */ - conversation { + GPT4All.conversation { println("🤖 Context loaded: $store") // hack until https://github.com/nomic-ai/gpt4all/pull/1126 is accepted or merged val out = System.out - GPT4All.use { gpT4All: GPT4All -> + model.use { gpT4All: GPT4All -> while (true) { print("\n🤖 Enter your question: ") val userInput = readlnOrNull() ?: break diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/memory/ChatWithMemory.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/memory/ChatWithMemory.kt index 2fbeac3b4..2eda22a7c 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/memory/ChatWithMemory.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/memory/ChatWithMemory.kt @@ -1,12 +1,10 @@ package com.xebia.functional.xef.auto.memory -import com.xebia.functional.xef.auto.conversation import com.xebia.functional.xef.auto.llm.openai.OpenAI -import com.xebia.functional.xef.auto.llm.openai.conversation suspend fun main() { val model = OpenAI().DEFAULT_CHAT - conversation { + OpenAI.conversation { while (true) { println(">") val question = readLine() ?: break diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/prompts/ExpertSystemExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/prompts/ExpertSystemExample.kt index 2d09190a6..e16b295bb 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/prompts/ExpertSystemExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/prompts/ExpertSystemExample.kt @@ -1,29 +1,30 @@ package com.xebia.functional.xef.auto.prompts import com.xebia.functional.xef.auto.conversation -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.auto.llm.openai.prompt import com.xebia.functional.xef.prompt.experts.ExpertSystem import kotlinx.serialization.Serializable @Serializable data class FinalSolution(val resolvedCode: String) -suspend fun main() = conversation { - val expert = - functionProgrammerFix( - """| +suspend fun main() = + OpenAI.conversation { + val expert = + functionProgrammerFix( + """| |```kotlin |fun access(list: List, index: Int): Int { | return list[index] |} |``` """ - .trimMargin() - ) - val solution: FinalSolution = prompt(expert) + .trimMargin() + ) + val solution: FinalSolution = prompt(expert) - println("solution: ${solution}") -} + println("solution: ${solution}") + } private fun functionProgrammerFix(code: String) = ExpertSystem( diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/prompts/PromptEvaluationExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/prompts/PromptEvaluationExample.kt index 49ed12510..9d904b6f2 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/prompts/PromptEvaluationExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/prompts/PromptEvaluationExample.kt @@ -1,12 +1,10 @@ package com.xebia.functional.xef.auto.prompts -import com.xebia.functional.xef.auto.conversation import com.xebia.functional.xef.auto.llm.openai.OpenAI -import com.xebia.functional.xef.auto.llm.openai.conversation import com.xebia.functional.xef.prompt.evaluator.PromptEvaluator suspend fun main() { - conversation { + OpenAI.conversation { val score = PromptEvaluator.evaluate( model = OpenAI().DEFAULT_CHAT, diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CodeExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CodeExample.kt index 4f10676c3..11021ee73 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CodeExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CodeExample.kt @@ -1,12 +1,10 @@ package com.xebia.functional.xef.auto.reasoning -import com.xebia.functional.xef.auto.conversation import com.xebia.functional.xef.auto.llm.openai.OpenAI -import com.xebia.functional.xef.auto.llm.openai.conversation import com.xebia.functional.xef.reasoning.code.Code suspend fun main() { - conversation { + OpenAI.conversation { val code = Code(model = OpenAI().DEFAULT_CHAT, scope = this) val sourceCode = diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/ReActExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/ReActExample.kt index 7679fd9a3..0498d97e5 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/ReActExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/ReActExample.kt @@ -2,14 +2,13 @@ package com.xebia.functional.xef.auto.reasoning import com.xebia.functional.xef.auto.conversation import com.xebia.functional.xef.auto.llm.openai.OpenAI -import com.xebia.functional.xef.auto.llm.openai.conversation import com.xebia.functional.xef.llm.models.chat.Message -import com.xebia.functional.xef.reasoning.search.Search +import com.xebia.functional.xef.reasoning.serpapi.Search import com.xebia.functional.xef.reasoning.tools.LLMTool import com.xebia.functional.xef.reasoning.tools.ReActAgent suspend fun main() { - conversation { + OpenAI.conversation { val model = OpenAI().DEFAULT_CHAT val serialization = OpenAI().DEFAULT_SERIALIZATION val math = diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/SerpApiExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/SerpApiExample.kt index bd9fe8512..ff2374be0 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/SerpApiExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/SerpApiExample.kt @@ -1,26 +1,23 @@ package com.xebia.functional.xef.auto.reasoning -import com.xebia.functional.gpt4all.conversation import com.xebia.functional.xef.reasoning.serpapi.SerpApiClient suspend fun main() { - conversation { - val client = SerpApiClient() + val client = SerpApiClient() - val searchData = - SerpApiClient.SearchData( - search = "german+shepperd", - ) + val searchData = + SerpApiClient.SearchData( + search = "german+shepperd", + ) - val answer = client.search(searchData) + val answer = client.search(searchData) - answer.searchResults.forEach { - println( - "\n\uD83E\uDD16 Search Information:\n\n" + - "Title: ${it.title}\n" + - "Document: ${it.document}\n" + - "Source: ${it.source}\n" - ) - } + answer.searchResults.forEach { + println( + "\n\uD83E\uDD16 Search Information:\n\n" + + "Title: ${it.title}\n" + + "Document: ${it.document}\n" + + "Source: ${it.source}\n" + ) } } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/TextExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/TextExample.kt index 3c04f5db0..3acf19aee 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/TextExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/TextExample.kt @@ -1,13 +1,11 @@ package com.xebia.functional.xef.auto.reasoning -import com.xebia.functional.xef.auto.conversation import com.xebia.functional.xef.auto.llm.openai.OpenAI -import com.xebia.functional.xef.auto.llm.openai.conversation import com.xebia.functional.xef.reasoning.text.Text import com.xebia.functional.xef.reasoning.text.summarize.SummaryLength suspend fun main() { - conversation { + OpenAI.conversation { val text = Text(model = OpenAI().DEFAULT_CHAT, scope = this) val inputText = diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/ToolSelectionExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/ToolSelectionExample.kt index da9bc316b..e466689eb 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/ToolSelectionExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/ToolSelectionExample.kt @@ -1,15 +1,13 @@ package com.xebia.functional.xef.auto.reasoning -import com.xebia.functional.xef.auto.conversation import com.xebia.functional.xef.auto.llm.openai.OpenAI -import com.xebia.functional.xef.auto.llm.openai.conversation import com.xebia.functional.xef.reasoning.filesystem.Files import com.xebia.functional.xef.reasoning.pdf.PDF import com.xebia.functional.xef.reasoning.text.Text import com.xebia.functional.xef.reasoning.tools.ToolSelection suspend fun main() { - conversation { + OpenAI.conversation { val model = OpenAI().DEFAULT_CHAT val serialization = OpenAI().DEFAULT_SERIALIZATION val text = Text(model = model, scope = this) diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/WikipediaSearchByParamsExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/WikipediaSearchByParamsExample.kt index 083462577..172e580ce 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/WikipediaSearchByParamsExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/WikipediaSearchByParamsExample.kt @@ -1,10 +1,11 @@ package com.xebia.functional.xef.auto.reasoning import com.xebia.functional.gpt4all.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.reasoning.wikipedia.WikipediaClient suspend fun main() { - conversation { + OpenAI.conversation { val client = WikipediaClient() val searchDataByPageId = WikipediaClient.SearchDataByParam(pageId = 5222) diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/WikipediaSearchExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/WikipediaSearchExample.kt index ab0586b41..382caa490 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/WikipediaSearchExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/WikipediaSearchExample.kt @@ -1,10 +1,11 @@ package com.xebia.functional.xef.auto.reasoning import com.xebia.functional.gpt4all.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import com.xebia.functional.xef.reasoning.wikipedia.WikipediaClient suspend fun main() { - conversation { + OpenAI.conversation { val client = WikipediaClient() val searchData = WikipediaClient.SearchData(search = "Capital%20de%20Colombia") diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/sql/DatabaseExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/sql/DatabaseExample.kt index 85cbcc4d1..f38931dcc 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/sql/DatabaseExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/sql/DatabaseExample.kt @@ -3,7 +3,6 @@ package com.xebia.functional.xef.auto.sql import arrow.core.raise.catch import com.xebia.functional.xef.auto.PromptConfiguration import com.xebia.functional.xef.auto.llm.openai.OpenAI -import com.xebia.functional.xef.auto.llm.openai.conversation import com.xebia.functional.xef.sql.SQL import com.xebia.functional.xef.sql.jdbc.JdbcConfig @@ -20,26 +19,27 @@ val config = model = model ) -suspend fun main() = conversation { - SQL.fromJdbcConfig(config) { - println("llmdb> Welcome to the LLMDB (An LLM interface to your SQL Database) !") - println("llmdb> You can ask me questions about the database and I will try to answer them.") - println("llmdb> You can type `exit` to exit the program.") - println("llmdb> Loading recommended prompts...") - val interestingPrompts = getInterestingPromptsForDatabase() - interestingPrompts.split("\n").forEach { it -> println("llmdb> $it") } +suspend fun main() = + OpenAI.conversation { + SQL.fromJdbcConfig(config) { + println("llmdb> Welcome to the LLMDB (An LLM interface to your SQL Database) !") + println("llmdb> You can ask me questions about the database and I will try to answer them.") + println("llmdb> You can type `exit` to exit the program.") + println("llmdb> Loading recommended prompts...") + val interestingPrompts = getInterestingPromptsForDatabase() + interestingPrompts.split("\n").forEach { it -> println("llmdb> $it") } - while (true) { - // a cli chat with the content - print("user> ") - val input = readln() - if (input == "exit") break - catch( - { - addContext(*promptQuery(input).toTypedArray()) - val result = - model.promptMessage( - """| + while (true) { + // a cli chat with the content + print("user> ") + val input = readln() + if (input == "exit") break + catch( + { + addContext(*promptQuery(input).toTypedArray()) + val result = + model.promptMessage( + """| |You are a database assistant that helps users to query and summarize results from the database. |Instructions: |1. Summarize the information provided in the `Context` and follow to step 2. @@ -49,16 +49,16 @@ suspend fun main() = conversation { |``` |3. Try to answer and provide information with as much detail as you can """ - .trimMargin(), - promptConfiguration = PromptConfiguration.invoke { docsInContext(50) } - ) - println("llmdb> $result") - }, - { exception -> - println("llmdb> ${exception.message}") - exception.printStackTrace() - } - ) + .trimMargin(), + promptConfiguration = PromptConfiguration.invoke { docsInContext(50) } + ) + println("llmdb> $result") + }, + { exception -> + println("llmdb> ${exception.message}") + exception.printStackTrace() + } + ) + } } } -} diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/tot/Main.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/tot/Main.kt index 7ca813b14..f73e71ed0 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/tot/Main.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/tot/Main.kt @@ -1,15 +1,15 @@ package com.xebia.functional.xef.auto.tot -import com.xebia.functional.xef.auto.conversation -import com.xebia.functional.xef.auto.llm.openai.conversation +import com.xebia.functional.xef.auto.llm.openai.OpenAI import kotlinx.serialization.Serializable @Serializable data class FinalSolution(val solution: String) -suspend fun main() = conversation { - val problem = - Problem( - """| +suspend fun main() = + OpenAI.conversation { + val problem = + Problem( + """| |You are an expert functional programmer. |1. You never throw exceptions. |2. You never use null. @@ -26,14 +26,14 @@ suspend fun main() = conversation { | |Return a concise solution that fixes the problems in the code. """ - .trimMargin() - ) - val maxRounds = 5 + .trimMargin() + ) + val maxRounds = 5 - val solution = solve(problem, maxRounds) + val solution = solve(problem, maxRounds) - println("✅ Final solution: ${solution.answer}") - println("✅ Solution validity: ${solution.isValid}") - println("✅ Solution reasoning: ${solution.reasoning}") - println("✅ Solution code: ${solution.value?.solution}") -} + println("✅ Final solution: ${solution.answer}") + println("✅ Solution validity: ${solution.isValid}") + println("✅ Solution reasoning: ${solution.reasoning}") + println("✅ Solution code: ${solution.value?.solution}") + } diff --git a/examples/scala/build.gradle.kts b/examples/scala/build.gradle.kts index b051b0f98..0b1e196a2 100644 --- a/examples/scala/build.gradle.kts +++ b/examples/scala/build.gradle.kts @@ -17,6 +17,8 @@ dependencies { implementation(projects.xefCore) implementation(projects.xefScala) implementation(projects.kotlinLoom) + implementation(projects.xefReasoning) + implementation(projects.xefOpenai) implementation(libs.circe.parser) implementation(libs.scala.lang) implementation(libs.logback) @@ -34,4 +36,4 @@ spotless { scala { scalafmt("3.7.3").configFile(".scalafmt.conf").scalaMajorVersion("2.13") } -} \ No newline at end of file +} diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Book.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Book.scala index 110f14ab6..f7b127057 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Book.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Book.scala @@ -5,7 +5,7 @@ import io.circe.Decoder private final case class Book(name: String, author: String, summary: String) derives SerialDescriptor, Decoder -def summarizeBook(title: String, author: String): AI[Book] = +def summarizeBook(title: String, author: String)(using conversation: ScalaConversation): Book = prompt(s"$title by $author summary.") @main def runBook: Unit = diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/BreakingNews.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/BreakingNews.scala index c0b7558f6..74a432282 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/BreakingNews.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/BreakingNews.scala @@ -1,7 +1,8 @@ package com.xebia.functional.xef.scala.auto import com.xebia.functional.xef.scala.auto.* -import com.xebia.functional.xef.scala.agents.DefaultSearch +import com.xebia.functional.xef.reasoning.serpapi.Search +import com.xebia.functional.xef.auto.llm.openai.OpenAI import io.circe.Decoder import java.text.SimpleDateFormat @@ -13,7 +14,8 @@ private final case class BreakingNewsAboutCovid(summary: String) derives SerialD conversation { val sdf = SimpleDateFormat("dd/M/yyyy") val currentDate = sdf.format(Date()) - addContext(DefaultSearch.search(s"$currentDate Covid News")) + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, summon[ScalaConversation], 3) + addContext(search.search(s"$currentDate Covid News").get()) val news = prompt[BreakingNewsAboutCovid](s"Write a paragraph of about 300 words about: $currentDate Covid News") println(news.summary) } diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/ChessAI.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/ChessAI.scala index e0790fa85..aa3496b35 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/ChessAI.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/ChessAI.scala @@ -12,7 +12,7 @@ private final case class ChessBoard(board: String) derives SerialDescriptor, Dec private final case class GameState(ended: Boolean, winner: Option[String]) derives SerialDescriptor, Decoder @tailrec -private def chessGame(moves: List[ChessMove], gameState: GameState): AI[(String, ChessMove)] = +private def chessGame(moves: List[ChessMove], gameState: GameState)(using conversation: ScalaConversation): (String, ChessMove) = if !gameState.ended then val currentPlayer = if moves.size % 2 == 0 then "Player 1 (White)" else "Player 2 (Black)" diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/DivergentTasks.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/DivergentTasks.scala index 580d5483a..2b81c893d 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/DivergentTasks.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/DivergentTasks.scala @@ -1,14 +1,16 @@ package com.xebia.functional.xef.scala.auto import com.xebia.functional.xef.scala.auto.* -import com.xebia.functional.xef.scala.agents.DefaultSearch +import com.xebia.functional.xef.auto.llm.openai.OpenAI +import com.xebia.functional.xef.reasoning.serpapi.Search import io.circe.Decoder private final case class NumberOfMedicalNeedlesInWorld(numberOfNeedles: Long) derives SerialDescriptor, Decoder @main def runDivergentTasks: Unit = conversation { - addContext(DefaultSearch.search("Estimate amount of medical needles in the world")) + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, summon[ScalaConversation], 3) + addContext(search.search("Estimate amount of medical needles in the world").get()) val needlesInWorld = prompt[NumberOfMedicalNeedlesInWorld]("Provide the number of medical needles in the world as an integer number") println(s"Needles in world: ${needlesInWorld.numberOfNeedles}") } diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Markets.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Markets.scala index ad3647e0b..30e37c198 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Markets.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Markets.scala @@ -1,6 +1,7 @@ package com.xebia.functional.xef.scala.auto -import com.xebia.functional.xef.scala.agents.DefaultSearch +import com.xebia.functional.xef.auto.llm.openai.OpenAI +import com.xebia.functional.xef.reasoning.serpapi.Search import com.xebia.functional.xef.scala.auto.* import io.circe.Decoder @@ -15,7 +16,8 @@ private final case class MarketNews(news: String, raisingStockSymbols: List[Stri conversation { val sdf = SimpleDateFormat("dd/M/yyyy") val currentDate = sdf.format(Date()) - addContext(DefaultSearch.search(s"$currentDate Stock market results, raising stocks, decreasing stocks")) + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, summon[ScalaConversation], 3) + addContext(search.search(s"$currentDate Stock market results, raising stocks, decreasing stocks").get()) val news = prompt[MarketNews]("Write a short summary of the stock market results given the provided context.") println(news) } diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/MealPlan.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/MealPlan.scala index 45d0dbccb..9fd64cf1a 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/MealPlan.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/MealPlan.scala @@ -1,7 +1,8 @@ package com.xebia.functional.xef.scala.auto -import com.xebia.functional.xef.scala.agents.DefaultSearch +import com.xebia.functional.xef.reasoning.serpapi.Search import com.xebia.functional.xef.scala.auto.* +import com.xebia.functional.xef.auto.llm.openai.OpenAI import io.circe.Decoder private final case class MealPlanRecipe(name: String, ingredients: List[String]) derives SerialDescriptor, Decoder @@ -10,7 +11,8 @@ private final case class MealPlan(name: String, recipes: List[MealPlanRecipe]) d @main def runMealPlan: Unit = conversation { - addContext(DefaultSearch.search("gall bladder stones meals")) + val search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, summon[ScalaConversation], 3) + addContext(search.search("gall bladder stones meals").get()) val mealPlan = prompt[MealPlan]("Meal plan for the week for a person with gall bladder stones that includes 5 recipes.") println(mealPlan) } diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/PDFDocument.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/PDFDocument.scala index 201a2f34d..c87538729 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/PDFDocument.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/PDFDocument.scala @@ -1,6 +1,8 @@ package com.xebia.functional.xef.scala.auto import com.xebia.functional.xef.scala.auto.* +import com.xebia.functional.xef.reasoning.pdf.PDF +import com.xebia.functional.xef.auto.llm.openai.OpenAI import io.circe.Decoder import scala.io.StdIn.readLine @@ -11,7 +13,8 @@ val pdfUrl = "https://people.cs.ksu.edu/~schmidt/705a/Scala/Programming-in-Scala @main def runPDFDocument: Unit = conversation { - addContext(pdf(resource = pdfUrl)) + val pdf = PDF(OpenAI.FromEnvironment.DEFAULT_CHAT, OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, summon[ScalaConversation]) + addContext(Array(pdf.readPDFFromUrl.readPDFFromUrl(pdfUrl).get())) while (true) { println("Enter your question: ") val line = scala.io.StdIn.readLine() diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Population.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Population.scala index b06c9fd09..60ae6b5d4 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Population.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Population.scala @@ -1,6 +1,7 @@ package com.xebia.functional.xef.scala.auto import com.xebia.functional.xef.scala.auto.* +import com.xebia.functional.xef.llm.models.images.ImagesGenerationResponse import io.circe.Decoder private final case class Population(size: Int, description: String) derives SerialDescriptor, Decoder @@ -11,7 +12,7 @@ private final case class Image(description: String, url: String) derives SerialD conversation { val cadiz: Population = prompt("Population of Cádiz, Spain.") val seattle: Population = prompt("Population of Seattle, WA.") - val imgs: List[String] = images("A hybrid city of Cádiz, Spain and Seattle, US.") - println(imgs.mkString("\n")) + val imgs: ImagesGenerationResponse = images("A hybrid city of Cádiz, Spain and Seattle, US.") + println(imgs) println(s"The population of Cádiz is ${cadiz.size} and the population of Seattle is ${seattle.size}") } diff --git a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Weather.scala b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Weather.scala index f77f0cec8..1cc1f6eb1 100644 --- a/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Weather.scala +++ b/examples/scala/src/main/scala/com/xebia/functional/xef/scala/auto/Weather.scala @@ -1,13 +1,13 @@ package com.xebia.functional.xef.scala.auto -import com.xebia.functional.xef.scala.agents.DefaultSearch -import com.xebia.functional.xef.scala.auto.* +import com.xebia.functional.xef.auto.llm.openai.OpenAI +import com.xebia.functional.xef.reasoning.serpapi.Search -private def getQuestionAnswer(question: String): String = conversation { - addContext(DefaultSearch.search("Weather in Cádiz, Spain")) +private def getQuestionAnswer(question: String)(using conversation: ScalaConversation): String = + val search: Search = Search(OpenAI.FromEnvironment.DEFAULT_CHAT, conversation, 3) + addContext(search.search("Weather in Cádiz, Spain").get()) promptMessage(question) -} @main def runWeather: Unit = val question = "Knowing this forecast, what clothes do you recommend I should wear if I live in Cádiz?" - println(getQuestionAnswer(question).mkString("\n")) + println(conversation(getQuestionAnswer(question)).mkString("\n")) diff --git a/gpt4all-kotlin/src/jvmMain/kotlin/com/xebia/functional/gpt4all/GPT4All.kt b/gpt4all-kotlin/src/jvmMain/kotlin/com/xebia/functional/gpt4all/GPT4All.kt index 38dae04ec..5943122ad 100644 --- a/gpt4all-kotlin/src/jvmMain/kotlin/com/xebia/functional/gpt4all/GPT4All.kt +++ b/gpt4all-kotlin/src/jvmMain/kotlin/com/xebia/functional/gpt4all/GPT4All.kt @@ -5,6 +5,8 @@ import ai.djl.training.util.ProgressBar import com.hexadevlabs.gpt4all.LLModel import com.xebia.functional.tokenizer.EncodingType import com.xebia.functional.tokenizer.ModelType +import com.xebia.functional.xef.auto.Conversation +import com.xebia.functional.xef.auto.PlatformConversation import com.xebia.functional.xef.llm.Chat import com.xebia.functional.xef.llm.Completion import com.xebia.functional.xef.llm.models.chat.* @@ -12,6 +14,8 @@ import com.xebia.functional.xef.llm.models.text.CompletionChoice import com.xebia.functional.xef.llm.models.text.CompletionRequest import com.xebia.functional.xef.llm.models.text.CompletionResult import com.xebia.functional.xef.llm.models.usage.Usage +import com.xebia.functional.xef.vectorstores.LocalVectorStore +import com.xebia.functional.xef.vectorstores.VectorStore import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED @@ -37,6 +41,23 @@ interface GPT4All : AutoCloseable, Chat, Completion { companion object { + @JvmSynthetic + suspend inline fun conversation( + store: VectorStore, + noinline block: suspend Conversation.() -> A + ): A = block(conversation(store)) + + @JvmSynthetic + suspend fun conversation( + block: suspend Conversation.() -> A + ): A = block(conversation(LocalVectorStore(HuggingFaceLocalEmbeddings.DEFAULT))) + + @JvmStatic + @JvmOverloads + fun conversation( + store: VectorStore = LocalVectorStore(HuggingFaceLocalEmbeddings.DEFAULT) + ): PlatformConversation = Conversation(store) + operator fun invoke( url: String, path: Path @@ -173,4 +194,5 @@ interface GPT4All : AutoCloseable, Chat, Completion { } } + } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 48735ccc3..3bcc92aba 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,6 @@ hikari = "5.0.1" dokka = "1.8.20" logback = "1.4.8" kotlinx-coroutines = "1.7.3" -kotlinx-coroutines-reactive = "1.7.3" scalaMultiversion = "2.0.4" circe = "0.14.5" catsEffect = "3.6-0142603" @@ -53,7 +52,8 @@ suspendApp-ktor = { module = "io.arrow-kt:suspendapp-ktor", version.ref = "suspe kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-json" } kotlinx-serialization-hocon = { module = "org.jetbrains.kotlinx:kotlinx-serialization-hocon", version.ref = "kotlinx-json" } kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref="kotlinx-coroutines" } -kotlinx-coroutines-reactive = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactive", version.ref="kotlinx-coroutines-reactive" } +kotlinx-coroutines-reactive = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactive", version.ref="kotlinx-coroutines" } +kotlinx-coroutines-jdk8 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8", version.ref="kotlinx-coroutines" } ktor-utils = { module = "io.ktor:ktor-utils", version.ref = "ktor" } ktor-http = { module = "io.ktor:ktor-http", version.ref = "ktor" } ktor-client ={ module = "io.ktor:ktor-client-core", version.ref = "ktor" } diff --git a/java/src/main/java/com/xebia/functional/xef/java/auto/AIDatabase.java b/java/src/main/java/com/xebia/functional/xef/java/auto/AIDatabase.java deleted file mode 100644 index 9c72b49ed..000000000 --- a/java/src/main/java/com/xebia/functional/xef/java/auto/AIDatabase.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.xebia.functional.xef.java.auto; - -import com.xebia.functional.xef.auto.Conversation; -import com.xebia.functional.xef.sql.SQL; -import com.xebia.functional.xef.sql.jdbc.JdbcConfig; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -public class AIDatabase implements AutoCloseable { - private final Conversation scope; - private SQL sql; - private ExecutionContext exec; - - public AIDatabase(JdbcConfig jdbcConfig, ExecutionContext executionContext) { - sql = SQL.Companion.fromJdbcConfigSync(jdbcConfig); - this.exec = executionContext; - this.scope = executionContext.getCoreScope(); - } - - public CompletableFuture getInterestingPromptsForDatabase() { - return exec.future(continuation -> sql.getInterestingPromptsForDatabase(scope, continuation)); - } - - public void extendContext(List textsToAdd) { - var textsArray = textsToAdd.toArray(new String[textsToAdd.size()]); - exec.future(continuation -> scope.addContext(textsArray, continuation)); - } - - public CompletableFuture> promptQuery(String input) { - return exec.future(continuation -> sql.promptQuery(scope, input, continuation)); - } - - @Override - public void close() throws Exception { - exec.close(); - } -} diff --git a/java/src/main/java/com/xebia/functional/xef/java/auto/AIScope.java b/java/src/main/java/com/xebia/functional/xef/java/auto/AIScope.java deleted file mode 100644 index b42840b35..000000000 --- a/java/src/main/java/com/xebia/functional/xef/java/auto/AIScope.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.xebia.functional.xef.java.auto; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.victools.jsonschema.generator.OptionPreset; -import com.github.victools.jsonschema.generator.SchemaGenerator; -import com.github.victools.jsonschema.generator.SchemaGeneratorConfig; -import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder; -import com.github.victools.jsonschema.generator.SchemaVersion; -import com.github.victools.jsonschema.module.jakarta.validation.JakartaValidationModule; -import com.github.victools.jsonschema.module.jakarta.validation.JakartaValidationOption; -import com.xebia.functional.xef.agents.Search; -import com.xebia.functional.xef.auto.Conversation; -import com.xebia.functional.xef.auto.PromptConfiguration; -import com.xebia.functional.xef.auto.llm.openai.OpenAI; -import com.xebia.functional.xef.embeddings.Embeddings; -import com.xebia.functional.xef.llm.Chat; -import com.xebia.functional.xef.llm.ChatWithFunctions; -import com.xebia.functional.xef.llm.Images; -import com.xebia.functional.xef.llm.models.functions.CFunction; -import com.xebia.functional.xef.llm.models.images.ImageGenerationUrl; -import com.xebia.functional.xef.llm.models.images.ImagesGenerationResponse; -import com.xebia.functional.xef.pdf.Loader; -import com.xebia.functional.xef.sql.SQL; -import com.xebia.functional.xef.textsplitters.TextSplitter; -import com.xebia.functional.xef.vectorstores.VectorStore; -import java.io.File; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import kotlin.Unit; -import kotlin.collections.CollectionsKt; -import kotlin.jvm.functions.Function1; -import kotlinx.coroutines.future.FutureKt; -import kotlinx.coroutines.reactive.ReactiveFlowKt; -import org.reactivestreams.Publisher; - -public class AIScope implements AutoCloseable { - private final Conversation scope; - private final ObjectMapper om; - private ExecutionContext exec; - private final SchemaGenerator schemaGenerator; - - public AIScope(ObjectMapper om, ExecutionContext executionContext) { - this.om = om; - this.exec = executionContext; - JakartaValidationModule module = new JakartaValidationModule( - JakartaValidationOption.NOT_NULLABLE_FIELD_IS_REQUIRED, - JakartaValidationOption.INCLUDE_PATTERN_EXPRESSIONS - ); - SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_7, OptionPreset.PLAIN_JSON) - .with(module); - SchemaGeneratorConfig config = configBuilder.build(); - this.schemaGenerator = new SchemaGenerator(config); - this.scope = executionContext.getCoreScope(); - } - - public Conversation getScope() { - return scope; - } - - public ExecutionContext getExec() { - return exec; - } - - public AIScope(ExecutionContext executionContext) { - this(new ObjectMapper(), executionContext); - } - - public AIScope() { - this(new ObjectMapper(), new ExecutionContext()); - } - - private AIScope(Conversation nested, AIScope outer) { - this.om = outer.om; - this.schemaGenerator = outer.schemaGenerator; - this.exec = outer.exec; - this.scope = nested; - } - - public CompletableFuture prompt(String prompt, Class cls) { - return prompt(prompt, cls, new OpenAI().DEFAULT_SERIALIZATION, PromptConfiguration.DEFAULTS); - } - - public CompletableFuture prompt(String prompt, Class cls, ChatWithFunctions llmModel, PromptConfiguration promptConfiguration) { - Function1 decoder = json -> { - try { - return om.readValue(json, cls); - } catch (JsonProcessingException e) { - // TODO AIError ex = new AIError.JsonParsing(json, maxAttempts, e); - throw new RuntimeException(e); - } - }; - - String schema = schemaGenerator.generateSchema(cls).toString(); - - List functions = Collections.singletonList( - new CFunction(cls.getSimpleName(), "Generated function for " + cls.getSimpleName(), schema) - ); - - return exec.future(continuation -> scope.promptWithSerializer(llmModel, prompt, functions, decoder, promptConfiguration, continuation)); - } - - public CompletableFuture promptMessage(String prompt) { - return promptMessage(new OpenAI().DEFAULT_CHAT, prompt, PromptConfiguration.DEFAULTS); - } - - public CompletableFuture promptMessage(Chat llmModel, String prompt, PromptConfiguration promptConfiguration) { - return exec.future(continuation -> scope.promptMessage(llmModel, prompt, promptConfiguration, continuation)); - } - - public CompletableFuture> promptMessages(Chat llmModel, String prompt, List functions, PromptConfiguration promptConfiguration) { - return exec.future(continuation -> scope.promptMessages(llmModel, prompt, functions, promptConfiguration, continuation)); - } - - public Publisher promptStreaming(Chat gpt4all, String line, PromptConfiguration promptConfiguration) { - return ReactiveFlowKt.asPublisher(scope.promptStreaming(gpt4all, line, Collections.emptyList(), promptConfiguration)); - } - - public CompletableFuture addContext(Iterable docs) { - return exec.future(continuation -> scope.addContext(docs, continuation)); - } - - public CompletableFuture> pdf(String url, TextSplitter splitter) { - return exec.future(continuation -> Loader.pdf(url, splitter, continuation)); - } - - public CompletableFuture> pdf(File file, TextSplitter splitter) { - return exec.future(continuation -> Loader.pdf(file, splitter, continuation)); - } - - public CompletableFuture> images(Images model, String prompt, Integer numberOfImages, String size, PromptConfiguration promptConfiguration) { - return exec.future(continuation -> scope.images(model, prompt, numberOfImages, size, promptConfiguration, continuation)) - .thenApply(response -> CollectionsKt.map(((ImagesGenerationResponse)response).getData(), ImageGenerationUrl::getUrl)); - } - - public CompletableFuture> search(String prompt) { - return exec.future(continuation -> Search.search(prompt, continuation)); - } - - public CompletableFuture getInterestingPromptsForDatabase(SQL sql) { - return exec.future(continuation -> sql.getInterestingPromptsForDatabase(scope, continuation)); - } - - @Override - public void close(){ - exec.close(); - } - -} diff --git a/java/src/main/java/com/xebia/functional/xef/java/auto/Empty.java b/java/src/main/java/com/xebia/functional/xef/java/auto/Empty.java new file mode 100644 index 000000000..82c339a6b --- /dev/null +++ b/java/src/main/java/com/xebia/functional/xef/java/auto/Empty.java @@ -0,0 +1,7 @@ +package com.xebia.functional.xef.java.auto; + +/** + * Module placeholder + */ +public class Empty { +} diff --git a/java/src/main/java/com/xebia/functional/xef/java/auto/ExecutionContext.java b/java/src/main/java/com/xebia/functional/xef/java/auto/ExecutionContext.java deleted file mode 100644 index 76758b499..000000000 --- a/java/src/main/java/com/xebia/functional/xef/java/auto/ExecutionContext.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.xebia.functional.xef.java.auto; - -import com.xebia.functional.xef.auto.Conversation; -import com.xebia.functional.xef.auto.llm.openai.OpenAI; -import com.xebia.functional.xef.auto.llm.openai.OpenAIEmbeddings; -import com.xebia.functional.xef.embeddings.Embeddings; -import com.xebia.functional.xef.vectorstores.LocalVectorStore; -import com.xebia.functional.xef.vectorstores.VectorStore; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -import kotlin.coroutines.Continuation; -import kotlin.jvm.functions.Function1; -import kotlinx.coroutines.CoroutineScope; -import kotlinx.coroutines.CoroutineScopeKt; -import kotlinx.coroutines.CoroutineStart; -import kotlinx.coroutines.ExecutorsKt; -import kotlinx.coroutines.JobKt; -import kotlinx.coroutines.future.FutureKt; -import org.jetbrains.annotations.NotNull; - -public class ExecutionContext implements AutoCloseable { - - private final ExecutorService executorService; - private final CoroutineScope coroutineScope; - private final Conversation scope; - private final VectorStore context; - - public ExecutionContext() { - this(Executors.newCachedThreadPool(new ExecutionContext.AIScopeThreadFactory()), new OpenAIEmbeddings(new OpenAI().DEFAULT_EMBEDDING)); - } - - public ExecutionContext(ExecutorService executorService) { - this(executorService, new OpenAIEmbeddings(new OpenAI().DEFAULT_EMBEDDING)); - } - - public ExecutionContext(ExecutorService executorService, Embeddings embeddings) { - this.executorService = executorService; - this.coroutineScope = () -> ExecutorsKt.from(executorService).plus(JobKt.Job(null)); - context = new LocalVectorStore(embeddings); - this.scope = new Conversation(context); - } - - protected CompletableFuture future(Function1, ? extends Object> block) { - return FutureKt.future( - coroutineScope, - coroutineScope.getCoroutineContext(), - CoroutineStart.DEFAULT, - (coroutineScope, continuation) -> block.invoke(continuation) - ); - } - - public VectorStore getContext() { - return context; - } - - @Override - public void close() { - CoroutineScopeKt.cancel(coroutineScope, null); - executorService.shutdown(); - } - - public Conversation getCoreScope() { - return scope; - } - - private static class AIScopeThreadFactory implements ThreadFactory { - private final AtomicInteger counter = new AtomicInteger(); - - @Override - public Thread newThread(@NotNull Runnable r) { - Thread t = new Thread(r); - t.setName("xef-ai-scope-worker-" + counter.getAndIncrement()); - t.setDaemon(true); - return t; - } - } -} diff --git a/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/Conversation.kt b/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/Conversation.kt deleted file mode 100644 index 8338d88be..000000000 --- a/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/Conversation.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.xebia.functional.xef.auto.llm.openai - -import com.xebia.functional.xef.auto.Conversation -import com.xebia.functional.xef.vectorstores.LocalVectorStore -import com.xebia.functional.xef.vectorstores.VectorStore - -suspend inline fun conversation( - store: VectorStore = LocalVectorStore(OpenAIEmbeddings(OpenAI().DEFAULT_EMBEDDING)), - noinline block: suspend Conversation.() -> A -): A = block(Conversation(store)) diff --git a/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/OpenAI.kt b/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/OpenAI.kt index 85de44fd6..f8b4d69ca 100644 --- a/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/OpenAI.kt +++ b/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/OpenAI.kt @@ -4,9 +4,16 @@ import arrow.core.nonEmptyListOf import com.xebia.functional.tokenizer.ModelType import com.xebia.functional.xef.AIError import com.xebia.functional.xef.auto.AutoClose +import com.xebia.functional.xef.auto.Conversation +import com.xebia.functional.xef.auto.PlatformConversation import com.xebia.functional.xef.auto.autoClose import com.xebia.functional.xef.env.getenv +import com.xebia.functional.xef.vectorstores.LocalVectorStore +import com.xebia.functional.xef.vectorstores.VectorStore import kotlin.jvm.JvmField +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic class OpenAI(internal var token: String? = null) : AutoCloseable, AutoClose by autoClose() { @@ -97,6 +104,27 @@ class OpenAI(internal var token: String? = null) : AutoCloseable, AutoClose by a DALLE_2 ) } + + companion object { + + @JvmField val FromEnvironment: OpenAI = OpenAI() + + @JvmSynthetic + suspend inline fun conversation( + store: VectorStore, + noinline block: suspend Conversation.() -> A + ): A = block(conversation(store)) + + @JvmSynthetic + suspend fun conversation(block: suspend Conversation.() -> A): A = + block(conversation(LocalVectorStore(OpenAIEmbeddings(FromEnvironment.DEFAULT_EMBEDDING)))) + + @JvmStatic + @JvmOverloads + fun conversation( + store: VectorStore = LocalVectorStore(OpenAIEmbeddings(FromEnvironment.DEFAULT_EMBEDDING)) + ): PlatformConversation = Conversation(store) + } } fun String.toOpenAIModel(token: String): OpenAIModel { diff --git a/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/OpenAIConversation.kt b/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/OpenAIConversation.kt new file mode 100644 index 000000000..0a924aab3 --- /dev/null +++ b/openai/src/commonMain/kotlin/com/xebia/functional/xef/auto/llm/openai/OpenAIConversation.kt @@ -0,0 +1,8 @@ +package com.xebia.functional.xef.auto.llm.openai + +import com.xebia.functional.xef.auto.PlatformConversation +import kotlin.coroutines.cancellation.CancellationException + +fun interface OpenAIConversation { + @Throws(CancellationException::class) fun conversation(conversation: PlatformConversation): A +} diff --git a/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.kt b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.kt new file mode 100644 index 000000000..5657b2498 --- /dev/null +++ b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.kt @@ -0,0 +1,9 @@ +package com.xebia.functional.xef.reasoning.serpapi + +import com.xebia.functional.xef.auto.Conversation +import com.xebia.functional.xef.llm.Chat +import kotlin.jvm.JvmOverloads + +expect class Search +@JvmOverloads +constructor(model: Chat, scope: Conversation, maxResultsInContext: Int = 3) : SearchTool diff --git a/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/search/Search.kt b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/SearchTool.kt similarity index 63% rename from reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/search/Search.kt rename to reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/SearchTool.kt index 922e82f41..5d66132d2 100644 --- a/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/search/Search.kt +++ b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/SearchTool.kt @@ -1,27 +1,27 @@ -package com.xebia.functional.xef.reasoning.search +package com.xebia.functional.xef.reasoning.serpapi -import com.xebia.functional.xef.auto.AutoClose import com.xebia.functional.xef.auto.Conversation -import com.xebia.functional.xef.auto.autoClose import com.xebia.functional.xef.llm.Chat import com.xebia.functional.xef.llm.models.chat.Message -import com.xebia.functional.xef.reasoning.serpapi.SerpApiClient import com.xebia.functional.xef.reasoning.tools.Tool +import kotlin.jvm.JvmSynthetic -class Search -@JvmOverloads -constructor( - private val model: Chat, - private val scope: Conversation, - private val maxResultsInContext: Int = 3, - private val client: SerpApiClient = SerpApiClient() -) : Tool, AutoCloseable, AutoClose by autoClose() { - override val name: String = "Search" +interface SearchTool : Tool { - override val description: String = - "Search the web for information. The tool input is a simple one line string" + val model: Chat + val scope: Conversation + val maxResultsInContext: Int + val client: SerpApiClient + override val name: String + get() = "Search" + + override val description: String + get() = "Search the web for information. The tool input is a simple one line string" + + @JvmSynthetic override suspend fun invoke(input: String): String { + val docs = client.search(SerpApiClient.SearchData(input)) return model .promptMessages( @@ -46,7 +46,7 @@ constructor( ?: "No results found" } - override fun close() { + fun close() { client.close() } } diff --git a/reasoning/src/jsMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.js.kt b/reasoning/src/jsMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.js.kt new file mode 100644 index 000000000..65731fe09 --- /dev/null +++ b/reasoning/src/jsMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.js.kt @@ -0,0 +1,17 @@ +package com.xebia.functional.xef.reasoning.serpapi + +import com.xebia.functional.xef.auto.Conversation +import com.xebia.functional.xef.llm.Chat + +actual class Search +actual constructor( + override val model: Chat, + override val scope: Conversation, + override val maxResultsInContext: Int, +) : SearchTool, AutoCloseable { + override val client: SerpApiClient = SerpApiClient() + + override fun close() { + client.close() + } +} diff --git a/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/pdf/ReadPDFFromUrl.kt b/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/pdf/ReadPDFFromUrl.kt index 579a8a179..9a05fd0bd 100644 --- a/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/pdf/ReadPDFFromUrl.kt +++ b/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/pdf/ReadPDFFromUrl.kt @@ -9,6 +9,11 @@ import com.xebia.functional.xef.reasoning.text.summarize.Summarize import com.xebia.functional.xef.reasoning.text.summarize.SummaryLength import com.xebia.functional.xef.reasoning.tools.Tool import io.github.oshai.kotlinlogging.KotlinLogging +import java.util.concurrent.CompletableFuture +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async +import kotlinx.coroutines.future.asCompletableFuture import kotlinx.serialization.Serializable @Serializable data class ExtractedUrl(val url: String) @@ -28,6 +33,12 @@ constructor( override val description: String = "Reads the content of a PDF as String from a URL" + private val coroutineScope = CoroutineScope(SupervisorJob()) + + fun readPDFFromUrl(url: String): CompletableFuture = + coroutineScope.async { invoke(url) }.asCompletableFuture() + + @JvmSynthetic override suspend fun invoke(input: String): String { val extracted: ExtractedUrl = model.prompt( diff --git a/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.jvm.kt b/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.jvm.kt new file mode 100644 index 000000000..020f066f2 --- /dev/null +++ b/reasoning/src/jvmMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.jvm.kt @@ -0,0 +1,30 @@ +package com.xebia.functional.xef.reasoning.serpapi + +import com.xebia.functional.xef.auto.Conversation +import com.xebia.functional.xef.llm.Chat +import java.util.concurrent.CompletableFuture +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async +import kotlinx.coroutines.future.asCompletableFuture + +actual class Search +@JvmOverloads +actual constructor( + override val model: Chat, + override val scope: Conversation, + override val maxResultsInContext: Int, +) : SearchTool, AutoCloseable { + + private val coroutineScope = CoroutineScope(SupervisorJob()) + + override val client = SerpApiClient() + + fun search(query: String): CompletableFuture> { + return coroutineScope.async { arrayOf(invoke(query)) }.asCompletableFuture() + } + + override fun close() { + client.close() + } +} diff --git a/reasoning/src/macosArm64Main/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.macosArm64.kt b/reasoning/src/macosArm64Main/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.macosArm64.kt new file mode 100644 index 000000000..65731fe09 --- /dev/null +++ b/reasoning/src/macosArm64Main/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.macosArm64.kt @@ -0,0 +1,17 @@ +package com.xebia.functional.xef.reasoning.serpapi + +import com.xebia.functional.xef.auto.Conversation +import com.xebia.functional.xef.llm.Chat + +actual class Search +actual constructor( + override val model: Chat, + override val scope: Conversation, + override val maxResultsInContext: Int, +) : SearchTool, AutoCloseable { + override val client: SerpApiClient = SerpApiClient() + + override fun close() { + client.close() + } +} diff --git a/reasoning/src/nativeMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.native.kt b/reasoning/src/nativeMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.native.kt new file mode 100644 index 000000000..65731fe09 --- /dev/null +++ b/reasoning/src/nativeMain/kotlin/com/xebia/functional/xef/reasoning/serpapi/Search.native.kt @@ -0,0 +1,17 @@ +package com.xebia.functional.xef.reasoning.serpapi + +import com.xebia.functional.xef.auto.Conversation +import com.xebia.functional.xef.llm.Chat + +actual class Search +actual constructor( + override val model: Chat, + override val scope: Conversation, + override val maxResultsInContext: Int, +) : SearchTool, AutoCloseable { + override val client: SerpApiClient = SerpApiClient() + + override fun close() { + client.close() + } +} diff --git a/scala/build.gradle.kts b/scala/build.gradle.kts index 678dddf8e..0d082b9a8 100644 --- a/scala/build.gradle.kts +++ b/scala/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(projects.xefCore) implementation(projects.xefOpenai) implementation(projects.kotlinLoom) + implementation(libs.kotlinx.coroutines.reactive) // TODO split to separate Scala library implementation(projects.xefPdf) diff --git a/scala/src/main/scala/com/xebia/functional/xef/scala/agents/DefaultSearch.scala b/scala/src/main/scala/com/xebia/functional/xef/scala/agents/DefaultSearch.scala deleted file mode 100644 index b05b74be6..000000000 --- a/scala/src/main/scala/com/xebia/functional/xef/scala/agents/DefaultSearch.scala +++ /dev/null @@ -1,9 +0,0 @@ -package com.xebia.functional.xef.scala.agents - -import com.xebia.functional.xef.agents.Search -import com.xebia.functional.loom.LoomAdapter -import scala.jdk.CollectionConverters.* - -object DefaultSearch: - def search(prompt: String): List[String] = - LoomAdapter.apply[java.util.List[String]](Search.search(prompt, _)).asScala.toList diff --git a/scala/src/main/scala/com/xebia/functional/xef/scala/auto/package.scala b/scala/src/main/scala/com/xebia/functional/xef/scala/auto/package.scala index 177316b22..5640ae8fd 100644 --- a/scala/src/main/scala/com/xebia/functional/xef/scala/auto/package.scala +++ b/scala/src/main/scala/com/xebia/functional/xef/scala/auto/package.scala @@ -1,101 +1,97 @@ package com.xebia.functional.xef.scala.auto -import com.xebia.functional.loom.LoomAdapter -import com.xebia.functional.tokenizer.ModelType import com.xebia.functional.xef.auto.llm.openai.* -import com.xebia.functional.xef.auto.{Conversation, PromptConfiguration} +import com.xebia.functional.xef.auto.{FromJson, JVMConversation, PromptConfiguration} import com.xebia.functional.xef.llm.* import com.xebia.functional.xef.llm.models.functions.{CFunction, Json} import com.xebia.functional.xef.llm.models.images.* -import com.xebia.functional.xef.pdf.Loader -import com.xebia.functional.xef.scala.textsplitters.TextSplitter -import com.xebia.functional.xef.vectorstores.LocalVectorStore +import com.xebia.functional.xef.vectorstores.{ConversationId, LocalVectorStore, VectorStore} import io.circe.Decoder import io.circe.parser.parse +import org.reactivestreams.{Subscriber, Subscription} -import java.io.File +import java.util +import java.util.UUID +import java.util.concurrent.LinkedBlockingQueue import scala.jdk.CollectionConverters.* -type AI[A] = AIScope ?=> A +class ScalaConversation(store: VectorStore, conversationId: Option[ConversationId]) extends JVMConversation(store, conversationId.orNull) -def conversation[A]( - block: AIScope ?=> A -): A = block(using AIScope.fromCore(new Conversation(LocalVectorStore(OpenAIEmbeddings(OpenAI().DEFAULT_EMBEDDING))))) +def addContext(context: Array[String])(using conversation: ScalaConversation): Unit = + conversation.addContextFromArray(context).join() def prompt[A: Decoder: SerialDescriptor]( prompt: String, - llmModel: ChatWithFunctions = OpenAI().DEFAULT_SERIALIZATION, + chat: ChatWithFunctions = OpenAI.FromEnvironment.DEFAULT_SERIALIZATION, promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS -)(using scope: AIScope): A = - LoomAdapter.apply((cont) => - scope.kt.promptWithSerializer[A]( - llmModel, - prompt, - generateCFunctions.asJava, - (json: String) => parse(json).flatMap(Decoder[A].decodeJson(_)).fold(throw _, identity), - promptConfiguration, - cont - ) - ) - -private def generateCFunctions[A: SerialDescriptor]: List[CFunction] = - val descriptor = SerialDescriptor[A].serialDescriptor - val serialName = descriptor.getSerialName - val fnName = - if (serialName.contains(".")) serialName.substring(serialName.lastIndexOf("."), serialName.length) - else serialName - List(CFunction(fnName, "Generated function for $fnName", Json.encodeJsonSchema(descriptor))) - -def addContext(docs: Iterable[String])(using scope: AIScope): Unit = - LoomAdapter.apply(scope.kt.addContext(docs.asJava, _)) +)(using + conversation: ScalaConversation +): A = + val fromJson = new FromJson[A] { + def fromJson(json: String): A = + parse(json).flatMap(Decoder[A].decodeJson(_)).fold(throw _, identity) + } + conversation.prompt(chat, prompt, generateCFunctions.asJava, fromJson, promptConfiguration).join() def promptMessage( - prompt: String, - llmModel: Chat = OpenAI().DEFAULT_CHAT, + question: String, + chat: Chat = OpenAI.FromEnvironment.DEFAULT_CHAT, promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS -)(using scope: AIScope): String = - LoomAdapter - .apply[String]( - scope.kt.promptMessage(llmModel, prompt, promptConfiguration, _) - ) +)(using conversation: ScalaConversation): String = + conversation.promptMessage(chat, question, promptConfiguration).join() def promptMessages( - prompt: String, - llmModel: Chat = OpenAI().DEFAULT_CHAT, - functions: List[CFunction] = List.empty, + question: String, + chat: Chat = OpenAI.FromEnvironment.DEFAULT_CHAT, + functions: List[CFunction] = List(), promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS -)(using scope: AIScope): List[String] = - LoomAdapter - .apply[java.util.List[String]]( - scope.kt.promptMessages(llmModel, prompt, functions.asJava, promptConfiguration, _) - ).asScala.toList - -def pdf( - resource: String | File, - splitter: TextSplitter = TextSplitter.tokenTextSplitter(ModelType.getDEFAULT_SPLITTER_MODEL, 100, 50) +)(using + conversation: ScalaConversation ): List[String] = - LoomAdapter - .apply[java.util.List[String]](count => - resource match - case url: String => Loader.pdf(url, splitter.core, count) - case file: File => Loader.pdf(file, splitter.core, count) - ).asScala.toList + conversation.promptMessages(chat, question, functions.asJava, promptConfiguration).join().asScala.toList + +def promptStreaming( + question: String, + chat: Chat = OpenAI.FromEnvironment.DEFAULT_CHAT, + functions: List[CFunction], + promptConfiguration: PromptConfiguration +)(using + conversation: ScalaConversation +): LazyList[String] = + val publisher = conversation.promptStreaming(chat, question, promptConfiguration, functions.asJava) + val queue = new LinkedBlockingQueue[String]() + publisher.subscribe(new Subscriber[String] { + // TODO change to fs2 or similar + def onSubscribe(s: Subscription): Unit = s.request(Long.MaxValue) + + def onNext(t: String): Unit = queue.add(t); () + + def onError(t: Throwable): Unit = throw t + + def onComplete(): Unit = () + }) + LazyList.continually(queue.take) def images( prompt: String, - model: Images = OpenAI().DEFAULT_IMAGES, - n: Int = 1, + images: Images = OpenAI.FromEnvironment.DEFAULT_IMAGES, + numberImages: Int = 1, size: String = "1024x1024", promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS -)(using scope: AIScope): List[String] = - LoomAdapter - .apply[ImagesGenerationResponse](cont => - scope.kt.images( - model, - prompt, - n, - size, - promptConfiguration, - cont - ) - ).getData.asScala.map(_.getUrl).toList +)(using + conversation: ScalaConversation +): ImagesGenerationResponse = + conversation.images(images, prompt, numberImages, size, promptConfiguration).join() + +def conversation[A]( + block: ScalaConversation ?=> A, + conversationId: Option[ConversationId] = Some(ConversationId(UUID.randomUUID().toString)) +): A = block(using ScalaConversation(LocalVectorStore(OpenAIEmbeddings(OpenAI.FromEnvironment.DEFAULT_EMBEDDING)), conversationId)) + +private def generateCFunctions[A: SerialDescriptor]: List[CFunction] = + val descriptor = SerialDescriptor[A].serialDescriptor + val serialName = descriptor.getSerialName + val fnName = + if (serialName.contains(".")) serialName.substring(serialName.lastIndexOf("."), serialName.length) + else serialName + List(CFunction(fnName, "Generated function for $fnName", Json.encodeJsonSchema(descriptor))) From d169fa1376029c3c374d31805079dc97a06bc3a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Raja=20Mart=C3=ADnez?= Date: Mon, 14 Aug 2023 13:47:23 +0200 Subject: [PATCH 2/2] Diff summary tool and create PR Description example (#320) * Diff summary tool and create PR Description example * Fixed copy pasta --- .../com/xebia/functional/xef/llm/Chat.kt | 9 ++ .../xef/auto/reasoning/CodeExample.kt | 7 +- .../xef/auto/reasoning/CreatePRDescription.kt | 33 +++++ .../functional/xef/reasoning/code/Code.kt | 10 +- .../xef/reasoning/code/DiffSummary.kt | 130 ++++++++++++++++++ 5 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CreatePRDescription.kt create mode 100644 reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/code/DiffSummary.kt diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt index 5005af7e6..3121c73e6 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt @@ -102,6 +102,15 @@ interface Chat : LLM { return promptMessages(prompt.toMessages(), scope, functions, promptConfiguration) } + @AiDsl + suspend fun promptMessage( + messages: List, + scope: Conversation, + promptConfiguration: PromptConfiguration = PromptConfiguration.DEFAULTS + ): String = + promptMessages(messages, scope, emptyList(), promptConfiguration).firstOrNull() + ?: throw AIError.NoResponse() + @AiDsl suspend fun promptMessages( messages: List, diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CodeExample.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CodeExample.kt index 11021ee73..d2e970943 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CodeExample.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CodeExample.kt @@ -5,7 +5,12 @@ import com.xebia.functional.xef.reasoning.code.Code suspend fun main() { OpenAI.conversation { - val code = Code(model = OpenAI().DEFAULT_CHAT, scope = this) + val code = + Code( + model = OpenAI().DEFAULT_CHAT, + serialization = OpenAI().DEFAULT_SERIALIZATION, + scope = this + ) val sourceCode = """ diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CreatePRDescription.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CreatePRDescription.kt new file mode 100644 index 000000000..fed065be8 --- /dev/null +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/reasoning/CreatePRDescription.kt @@ -0,0 +1,33 @@ +package com.xebia.functional.xef.auto.reasoning + +import com.xebia.functional.xef.auto.llm.openai.OpenAI +import com.xebia.functional.xef.llm.models.chat.Message +import com.xebia.functional.xef.reasoning.code.Code +import com.xebia.functional.xef.reasoning.tools.ReActAgent + +suspend fun main() { + OpenAI.conversation { + val code = + Code( + model = OpenAI().DEFAULT_CHAT, + serialization = OpenAI().DEFAULT_SERIALIZATION, + scope = this + ) + + val agent = + ReActAgent( + model = OpenAI().DEFAULT_SERIALIZATION, + scope = this, + tools = listOf(code.diffSummaryFromUrl) + ) + val prDescription = + agent.run( + listOf( + Message.userMessage { + "Create a PR description for https://patch-diff.githubusercontent.com/raw/xebia-functional/xef/pull/283.diff" + } + ) + ) + println(prDescription) + } +} diff --git a/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/code/Code.kt b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/code/Code.kt index 203e1d7d5..c128449b0 100644 --- a/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/code/Code.kt +++ b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/code/Code.kt @@ -2,16 +2,24 @@ package com.xebia.functional.xef.reasoning.code import com.xebia.functional.xef.auto.Conversation import com.xebia.functional.xef.llm.Chat +import com.xebia.functional.xef.llm.ChatWithFunctions import com.xebia.functional.xef.reasoning.tools.LLMTool import com.xebia.functional.xef.reasoning.tools.Tool import kotlin.jvm.JvmField import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic -class Code( +class Code +@JvmOverloads +constructor( model: Chat, + serialization: ChatWithFunctions, scope: Conversation, @JvmField + val diffSummaryFromUrl: DiffSummary = + DiffSummary(serialization = serialization, chat = model, scope = scope), + @JvmField val antiPatternDetection: LLMTool = LLMTool.create( name = "AntiPatternDetection", diff --git a/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/code/DiffSummary.kt b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/code/DiffSummary.kt new file mode 100644 index 000000000..d7a495485 --- /dev/null +++ b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/code/DiffSummary.kt @@ -0,0 +1,130 @@ +package com.xebia.functional.xef.reasoning.code + +import com.xebia.functional.xef.auto.AutoClose +import com.xebia.functional.xef.auto.Conversation +import com.xebia.functional.xef.auto.autoClose +import com.xebia.functional.xef.llm.Chat +import com.xebia.functional.xef.llm.ChatWithFunctions +import com.xebia.functional.xef.llm.models.chat.Message +import com.xebia.functional.xef.prompt.Prompt +import com.xebia.functional.xef.reasoning.text.summarize.Summarize +import com.xebia.functional.xef.reasoning.text.summarize.SummaryLength +import com.xebia.functional.xef.reasoning.tools.Tool +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import io.ktor.client.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import kotlinx.serialization.Serializable + +class DiffSummary( + private val serialization: ChatWithFunctions, + private val chat: Chat, + private val scope: Conversation +) : Tool, AutoClose by autoClose(), AutoCloseable { + + private val client = HttpClient {} + + private val summarize = + Summarize(model = chat, scope = scope, summaryLength = SummaryLength.Bound(3000)) + + private val logger: KLogger = KotlinLogging.logger {} + + override val name: String = "DiffSummary" + override val description: String = + "Summarize code diffs given a diff url and creates a PR description." + + @Serializable data class ExtractedUrl(val url: String) + + override suspend fun invoke(input: String): String { + val extracted: ExtractedUrl = + serialization.prompt( + prompt = + Prompt( + """| + |Please provide the url that you want to read a diff from + |given this input: + | + |$input + """ + .trimMargin() + ), + scope = scope, + serializer = ExtractedUrl.serializer() + ) + + logger.info { "Reading url ${extracted.url}" } + val diff = + client.request { + url(extracted.url) + method = HttpMethod.Get + } + return if (diff.status != HttpStatusCode.OK) { + "Could not read diff from url ${extracted.url}" + } else { + val content = diff.bodyAsText() + if (chat.modelType.encoding.countTokens(content) >= chat.modelType.maxContextLength / 2) { + val summary = summarize(diff.bodyAsText()) + createPRDescription(summary) + } else { + createPRDescription(content) + } + } + } + + private suspend fun createPRDescription(summary: String): String = + chat.promptMessage( + messages = + listOf( + Message.systemMessage { "Create Pull Request Description" }, + Message.assistantMessage { + "I will roleplay as an expert software engineer implementing a service to read a .diff file from a URL and create a Pull Request description with an automatically inferred user intent." + }, + Message.assistantMessage { + "I will use the following program to return a Pull Request description to the user:" + }, + systemPrompt(), + Message.userMessage { "Set Summary = $summary" }, + Message.userMessage { "CreatePRDescription()" }, + Message.assistantMessage { + "A great, concise and neutral toned Pull Request description for this summary is:" + } + ), + scope = scope + ) + + override fun close() { + client.close() + } + + companion object { + suspend fun systemPrompt(): Message = + Message.systemMessage { + // language=yaml + """ + # CreatePRDescription + PRDescriptionCreator: + Roleplay: "expert software engineer implementing a service to read a .diff file from a URL and create a PR description with an automatically inferred user intent." + DevProcess: + State: + Summary: "String" + Content: "String" + InferUserIntent: + Description: "Analyze the content to infer the user's intent for creating this PR. Must clearly articulate the reason, context, and goal." + CreatePRDescription: + Description: "Construct a PR description based on the inferred user's intent and extracted content. The description must: - Clearly articulate the inferred user's intent for creating this PR. - Provide a concise summary of the content. - Be clear, concise, and informative." + Style guide: + Favor: "clear, understandable code." + Handle: "potential errors like invalid content, intent inference issues, etc." + Description: + Validate and read the Summary for the .dff content + InferUserIntent() + CreatePRDescription() + Return the PR description + Instructions: "When asked to implement this functionality, please carefully follow the instructions above, ensuring that the user's intent is automatically inferred and added to the PR description. 🙏" + """ + .trimIndent() + } + } +}