Skip to content

Commit

Permalink
some adjustments to the most recent Anthropic API (#12)
Browse files Browse the repository at this point in the history
* initial computer use support according to the latest API changes
* model represented as a separate enum
  • Loading branch information
morisil authored Oct 25, 2024
1 parent e7b14dc commit 935ee04
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 23 deletions.
20 changes: 10 additions & 10 deletions src/commonMain/kotlin/Anthropic.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,6 @@ const val ANTHROPIC_API_BASE: String = "https://api.anthropic.com/"
*/
const val DEFAULT_ANTHROPIC_VERSION: String = "2023-06-01"

/**
* The default model to be used if no model is specified.
*/
const val DEFAULT_MODEL = "claude-3-5-sonnet-20240620"

/**
* An exception thrown when API requests returns error.
*/
Expand Down Expand Up @@ -79,13 +74,13 @@ fun Anthropic(
val config = Anthropic.Config().apply(block)
val apiKey = if (config.apiKey != null) config.apiKey else envApiKey
requireNotNull(apiKey) { missingApiKeyMessage }
val defaultModel = if (config.defaultModel != null) config.defaultModel!! else DEFAULT_MODEL
return Anthropic(
apiKey = apiKey,
anthropicVersion = config.anthropicVersion,
anthropicBeta = config.anthropicBeta,
apiBase = config.apiBase,
defaultModel = defaultModel,
defaultModel = config.defaultModel.id,
defaultMaxTokens = config.defaultMaxTokens,
directBrowserAccess = config.directBrowserAccess,
logLevel = if (config.logHttp) LogLevel.ALL else LogLevel.NONE
).apply {
Expand All @@ -99,6 +94,7 @@ class Anthropic internal constructor(
val anthropicBeta: String?,
val apiBase: String,
val defaultModel: String,
val defaultMaxTokens: Int,
val directBrowserAccess: Boolean,
val logLevel: LogLevel
) {
Expand All @@ -108,7 +104,9 @@ class Anthropic internal constructor(
var anthropicVersion: String = DEFAULT_ANTHROPIC_VERSION
var anthropicBeta: String? = null
var apiBase: String = ANTHROPIC_API_BASE
var defaultModel: String? = null
var defaultModel: Model = Model.DEFAULT
var defaultMaxTokens: Int = defaultModel.maxOutput

var directBrowserAccess: Boolean = false
var logHttp: Boolean = false

Expand Down Expand Up @@ -180,7 +178,8 @@ class Anthropic internal constructor(

val request = MessageRequest.Builder(
defaultModel,
toolEntryMap = toolEntryMap
defaultMaxTokens,
toolEntryMap
).apply(block).build()

val apiResponse = client.post("/v1/messages") {
Expand Down Expand Up @@ -211,7 +210,8 @@ class Anthropic internal constructor(

val request = MessageRequest.Builder(
defaultModel,
toolEntryMap = toolEntryMap
defaultMaxTokens,
toolEntryMap
).apply {
block(this)
stream = true
Expand Down
97 changes: 97 additions & 0 deletions src/commonMain/kotlin/Models.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.xemantic.anthropic

enum class Model(
val id: String,
val contextWindow: Int,
val maxOutput: Int,
val messageBatchesApi: Boolean,
val cost: Cost
) {

CLAUDE_3_5_SONNET(
id = "claude-3-5-sonnet-latest",
contextWindow = 200000,
maxOutput = 8182,
messageBatchesApi = true,
cost = Cost(
input = 3.0,
output = 15.0
)
),

CLAUDE_3_5_SONNET_20241022(
id = "claude-3-5-sonnet-20241022",
contextWindow = 200000,
maxOutput = 8182,
messageBatchesApi = true,
cost = Cost(
input = 3.0,
output = 15.0
)
),

CLAUDE_3_5_SONNET_20240620(
id = "claude-3-5-sonnet-20240620",
contextWindow = 200000,
maxOutput = 8182,
messageBatchesApi = true,
cost = Cost(
input = 3.0,
output = 15.0
)
),

CLAUDE_3_OPUS(
id = "claude-3-opus-latest",
contextWindow = 200000,
maxOutput = 4096,
messageBatchesApi = true,
cost = Cost(
input = 15.0,
output = 75.0
)
),

CLAUDE_3_OPUS_20240229(
id = "claude-3-opus-20240229",
contextWindow = 200000,
maxOutput = 4096,
messageBatchesApi = true,
cost = Cost(
input = 15.0,
output = 75.0
)
),

CLAUDE_3_SONNET_20240229(
id = "claude-3-sonnet-20240229",
contextWindow = 200000,
maxOutput = 4096,
messageBatchesApi = true,
cost = Cost(
input = 3.0,
output = 15.0
)
),

CLAUDE_3_HAIKU_20240307(
id = "claude-3-haiku-20240307",
contextWindow = 200000,
maxOutput = 4096,
messageBatchesApi = true,
cost = Cost(
input = .25,
output = 1.25
)
);

/**
* Cost per MTok
*/
data class Cost(val input: Double, val output: Double)

companion object {
val DEFAULT: Model = CLAUDE_3_5_SONNET
}

}
17 changes: 12 additions & 5 deletions src/commonMain/kotlin/message/Messages.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.xemantic.anthropic.message

import com.xemantic.anthropic.Anthropic
import com.xemantic.anthropic.MessageResponse
import com.xemantic.anthropic.Model
import com.xemantic.anthropic.anthropicJson
import com.xemantic.anthropic.schema.JsonSchema
import com.xemantic.anthropic.tool.UsableTool
Expand Down Expand Up @@ -53,14 +54,15 @@ data class MessageRequest(

class Builder internal constructor(
private val defaultModel: String,
private val defaultMaxTokens: Int,
@PublishedApi
internal val toolEntryMap: Map<String, Anthropic.ToolEntry<out UsableTool>>
) {
var model: String? = null
var maxTokens = 1024
var messages: List<Message> = mutableListOf<Message>()
var maxTokens: Int = defaultMaxTokens
var messages: List<Message> = emptyList()
var metadata = null
val stopSequences = mutableListOf<String>()
var stopSequences: List<String> = emptyList()
var stream: Boolean? = null
internal set
var system: List<System>? = null
Expand Down Expand Up @@ -128,12 +130,17 @@ data class MessageRequest(

}

/**
* Used only in tests. Maybe should be internal?
*/
fun MessageRequest(
defaultModel: String,
model: Model = Model.DEFAULT,
block: MessageRequest.Builder.() -> Unit
): MessageRequest {
val builder = MessageRequest.Builder(
defaultModel, emptyMap()
defaultModel = model.id,
defaultMaxTokens = model.maxOutput,
toolEntryMap = emptyMap()
)
block(builder)
return builder.build()
Expand Down
33 changes: 33 additions & 0 deletions src/commonMain/kotlin/tool/computer/Computer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.xemantic.anthropic.tool.computer

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

enum class Action {
@SerialName("key")
KEY,
@SerialName("type")
TYPE,
@SerialName("mouse_move")
MOUSE_MOVE,
@SerialName("left_click")
LEFT_CLICK,
@SerialName("left_click_drag")
LEFT_CLICK_DRAG,
@SerialName("right_click")
RIGHT_CLICK,
@SerialName("middle_click")
MIDDLE_CLICK,
@SerialName("double_click")
DOUBLE_CLICK,
@SerialName("screenshot")
SCREENSHOT,
@SerialName("cursor_position")
CURSOR_POSITION,
}

@Serializable
data class Resolution(
val width: Int,
val height: Int
)
2 changes: 1 addition & 1 deletion src/commonTest/kotlin/AnthropicTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class AnthropicTest {
assertSoftly(response) {
type shouldBe "message"
role shouldBe Role.ASSISTANT
model shouldBe "claude-3-5-sonnet-20240620"
model shouldBe "claude-3-5-sonnet-20241022"
stopReason shouldBe StopReason.END_TURN
content.size shouldBe 1
content[0] shouldBe instanceOf<Text>()
Expand Down
8 changes: 3 additions & 5 deletions src/commonTest/kotlin/message/MessageRequestTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ class MessageRequestTest {
@Test
fun shouldCreateTheSimplestMessageRequest() {
// given
val request = MessageRequest(
defaultModel = "claude-3-5-sonnet-20240620"
) {
val request = MessageRequest {
+Message {
+"Hey Claude!?"
}
Expand All @@ -36,7 +34,7 @@ class MessageRequestTest {
// then
json shouldEqualJson """
{
"model": "claude-3-5-sonnet-20240620",
"model": "claude-3-5-sonnet-latest",
"messages": [
{
"role": "user",
Expand All @@ -48,7 +46,7 @@ class MessageRequestTest {
]
}
],
"max_tokens": 1024
"max_tokens": 8182
}
""".trimIndent()
}
Expand Down
4 changes: 2 additions & 2 deletions src/commonTest/kotlin/message/MessageResponseTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class MessageResponseTest {
"id": "msg_01PspkNzNG3nrf5upeTsmWLF",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"model": "claude-3-5-sonnet-20241022",
"content": [
{
"type": "tool_use",
Expand All @@ -47,7 +47,7 @@ class MessageResponseTest {
id shouldBe "msg_01PspkNzNG3nrf5upeTsmWLF"
type shouldBe "message"
role shouldBe Role.ASSISTANT
model shouldBe "claude-3-5-sonnet-20240620"
model shouldBe "claude-3-5-sonnet-20241022"
content.size shouldBe 1
content[0] shouldBe instanceOf<ToolUse>()
stopReason shouldBe StopReason.TOOL_USE
Expand Down

0 comments on commit 935ee04

Please sign in to comment.