Skip to content

Commit

Permalink
better tools support, initial attempt WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
morisil committed Oct 9, 2024
1 parent 0438562 commit 6c01e9f
Show file tree
Hide file tree
Showing 9 changed files with 473 additions and 134 deletions.
46 changes: 25 additions & 21 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,22 @@ repositories {
}

kotlin {
//explicitApi() // check with serialization?
jvm {
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
// set up according to https://jakewharton.com/gradle-toolchains-are-rarely-a-good-idea/
compilerOptions {
apiVersion = kotlinTarget
languageVersion = kotlinTarget
jvmTarget = JvmTarget.fromTarget(javaTarget)
freeCompilerArgs.add("-Xjdk-release=$javaTarget")
progressiveMode = true
}
}

jvm {}

linuxX64()
// linuxX64()

sourceSets {

Expand All @@ -70,6 +82,7 @@ kotlin {

jvmTest {
dependencies {
implementation(kotlin("test-junit5"))
runtimeOnly(libs.log4j.slf4j2)
runtimeOnly(libs.log4j.core)
runtimeOnly(libs.jackson.databind)
Expand All @@ -78,28 +91,17 @@ kotlin {
}
}

nativeTest {
dependencies {
// on mac/ios it should be rather Darwin
implementation(libs.ktor.client.curl)
}
}
// nativeTest {
// dependencies {
// // on mac/ios it should be rather Darwin
// implementation(libs.ktor.client.curl)
// }
// }

}

}

// set up according to https://jakewharton.com/gradle-toolchains-are-rarely-a-good-idea/
tasks.withType<KotlinJvmCompile> {
compilerOptions {
apiVersion = kotlinTarget
languageVersion = kotlinTarget
jvmTarget = JvmTarget.fromTarget(javaTarget)
freeCompilerArgs.add("-Xjdk-release=$javaTarget")
progressiveMode = true
}
}

fun isNonStable(version: String): Boolean {
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) }
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
Expand All @@ -113,7 +115,7 @@ tasks.withType<DependencyUpdatesTask> {
}
}

tasks.withType<Test>() {
tasks.withType<Test> {
testLogging {
events(
TestLogEvent.PASSED,
Expand All @@ -123,13 +125,15 @@ tasks.withType<Test>() {
showStackTraces = true
exceptionFormat = TestExceptionFormat.FULL
}
enabled = true
}

@Suppress("OPT_IN_USAGE")
powerAssert {
functions = listOf(
"kotlin.assert",
"kotlin.test.assertTrue",
"kotlin.test.assertFalse",
"kotlin.test.assertEquals",
"kotlin.test.assertNull"
)
Expand Down
41 changes: 37 additions & 4 deletions src/commonMain/kotlin/Anthropic.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.xemantic.anthropic

import com.xemantic.anthropic.event.Event
import com.xemantic.anthropic.event.Usage
import com.xemantic.anthropic.message.Error
import com.xemantic.anthropic.message.ErrorResponse
import com.xemantic.anthropic.message.MessageRequest
import com.xemantic.anthropic.message.MessageResponse
import com.xemantic.anthropic.message.UsableTool
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
Expand All @@ -26,7 +28,14 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.serializer
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.typeOf

const val ANTHROPIC_API_BASE: String = "https://api.anthropic.com/"

Expand All @@ -45,6 +54,11 @@ val anthropicJson: Json = Json {
allowSpecialFloatingPointValues = true
explicitNulls = false
encodeDefaults = true
// serializersModule = SerializersModule {
// //contextual(UsableTool::class, UsableToolSerializer::class)
//// polymorphic(UsableTool::class) {
//// }
// }
}

fun Anthropic(
Expand All @@ -60,7 +74,8 @@ fun Anthropic(
anthropicBeta = config.anthropicBeta,
apiBase = config.apiBase,
defaultModel = defaultModel,
directBrowserAccess = config.directBrowserAccess
directBrowserAccess = config.directBrowserAccess,
context = config.context
)
}

Expand All @@ -70,7 +85,9 @@ class Anthropic internal constructor(
val anthropicBeta: String?,
val apiBase: String,
val defaultModel: String,
val directBrowserAccess: Boolean
val directBrowserAccess: Boolean,
val tools: MutableList<KClass<out UsableTool>> = mutableListOf<KClass<out UsableTool>>(),
val context: Context?
) {

class Config {
Expand All @@ -80,8 +97,26 @@ class Anthropic internal constructor(
var apiBase: String = ANTHROPIC_API_BASE
var defaultModel: String? = null
var directBrowserAccess: Boolean = false
var tools: MutableList<KClass<out UsableTool>>? = null
var context: Context? = null
}

interface Context {

fun <T> service(type: KType): T

}

companion object {
val EMPTY_CONTEXT: Context = object : Context {
override fun <T> service(type: KType): T {
throw UnsupportedOperationException("No services available")
}
}
}

inline fun <reified T> Context.service(): T = service(typeOf<T>())

private val client = HttpClient {
install(ContentNegotiation) {
json(anthropicJson)
Expand Down Expand Up @@ -157,5 +192,3 @@ class Anthropic internal constructor(

}

inline fun <reified T> anthropicTypeOf(): String =
T::class.qualifiedName!!.replace('.', '_')
26 changes: 18 additions & 8 deletions src/commonMain/kotlin/event/Events.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonClassDiscriminator

// reference https://docs.spring.io/spring-ai/reference/_images/anthropic-claude3-events-model.jpg

@Serializable
@JsonClassDiscriminator("type")
@OptIn(ExperimentalSerializationApi::class)
sealed class Event

@Serializable
@SerialName("message_start")
data class MessageStart(
data class MessageStartEvent(
val message: MessageResponse
) : Event()

@Serializable
@SerialName("message_delta")
data class MessageDelta(
val delta: MessageDelta.Delta,
data class MessageDeltaEvent(
val delta: Delta,
val usage: Usage
) : Event() {

Expand All @@ -37,21 +39,23 @@ data class MessageDelta(

@Serializable
@SerialName("message_stop")
class MessageStop : Event() {
class MessageStopEvent : Event() {
override fun toString(): String = "MessageStop"
}

// TODO error event is missing, should we rename all of these to events?

@Serializable
@SerialName("content_block_start")
data class ContentBlockStart(
data class ContentBlockStartEvent(
val index: Int,
@SerialName("content_block")
val contentBlock: ContentBlock
) : Event()

@Serializable
@SerialName("content_block_stop")
data class ContentBlockStop(
data class ContentBlockStopEvent(
val index: Int
) : Event()

Expand All @@ -66,17 +70,23 @@ sealed class ContentBlock {
val text: String
) : ContentBlock()

@Serializable
@SerialName("tool_use")
class ToolUse(
val text: String // TODO tool_id
) : ContentBlock()
// TODO missing tool_use
}

@Serializable
@SerialName("ping")
class Ping: Event() {
class PingEvent: Event() {
override fun toString(): String = "Ping"
}

@Serializable
@SerialName("content_block_delta")
data class ContentBlockDelta(
data class ContentBlockDeltaEvent(
val index: Int,
val delta: Delta
) : Event()
Expand Down
Loading

0 comments on commit 6c01e9f

Please sign in to comment.