Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/models/deepseek_models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
specifies: ../../providers/src/main/kotlin/com/simiacryptus/cognotik/chat/model/DeepSeekModels.kt
related:
- https://api-docs.deepseek.com/quick_start/pricing
- ../../core/src/main/kotlin/com/simiacryptus/cognotik/chat/model/ChatModel.kt
---

# [DeepSeekModels.kt](../../providers/src/main/kotlin/com/simiacryptus/cognotik/chat/model/DeepSeekModels.kt)
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pluginName=Cognotik - Open Source Agentic Power Tools
pluginRepositoryUrl=https://github.com/SimiaCryptus/Cognotik
libraryGroup=com.cognotik
libraryVersion=2.1.17
libraryVersion=2.1.18
gradleVersion=9.5.1
org.gradle.caching=true
org.gradle.configureondemand=false
Expand All @@ -19,7 +19,7 @@ kotlin.stdlib.default.dependency=false
systemProp.org.gradle.unsafe.kotlin.assignment=true
android.useAndroidX=true
android.enableJetifier=false
pluginSinceBuild=243
pluginSinceBuild=253
platformType=IU
platformVersion=2026.1
platformPlugins=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ package com.simiacryptus.cognotik.chat

import com.google.common.util.concurrent.ListeningScheduledExecutorService
import com.simiacryptus.cognotik.CoreProviders
import com.simiacryptus.cognotik.chat.model.ChatMessageModality
import com.simiacryptus.cognotik.chat.model.ChatModel
import com.simiacryptus.cognotik.chat.model.DeepSeekModels
import com.simiacryptus.cognotik.exceptions.ErrorUtil.checkError
import com.simiacryptus.cognotik.models.ModelSchema
import com.simiacryptus.cognotik.platform.model.Session
import com.simiacryptus.cognotik.util.JsonUtil
import com.simiacryptus.cognotik.util.SecureString
import org.apache.hc.core5.http.HttpRequest
import org.slf4j.LoggerFactory.getLogger
import org.slf4j.event.Level
import java.io.BufferedOutputStream
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService

class DeepSeekChatClient(
Expand Down Expand Up @@ -39,6 +43,51 @@ class DeepSeekChatClient(
request.addHeader(HEADER_AUTHORIZATION, "Bearer ${apiKey.decrypt}")
}

override fun getModels(): List<ChatModel> {
// Check cache first
modelsCache[apiBase]?.let { return it }

return try {
val modelsResponse = fetchAllModels()
val models = modelsResponse.map { modelInfo ->
val known = DeepSeekModels.values.values
.firstOrNull { it.name == modelInfo.id || it.modelId == modelInfo.id }
when {
known != null -> known
else -> {
log.debug("Unknown DeepSeek model: ${modelInfo.id}")
ChatModel(
name = modelInfo.id,
modelId = modelInfo.id,
maxTotalTokens = 1_000_000,
maxOutTokens = 384_000,
provider = CoreProviders.DeepSeek,
outputTokenPricePerK = 0.0, // TODO: Set actual pricing if known
inputModalities = setOf(ChatMessageModality.TEXT),
outputModalities = setOf(ChatMessageModality.TEXT)
)
}
}
}
// Cache the result
modelsCache[apiBase] = models
models
} catch (e: Exception) {
log.error("Failed to fetch DeepSeek models", e)
// Fall back to the statically-known models
DeepSeekModels.values.values.toList()
}
}

private fun fetchAllModels(): List<ModelInfo> {
require(!apiBase.isBlank())
val response = get("$apiBase/models")
checkError(response)
log.debug("DeepSeek models response: $response")
val listResponse = JsonUtil.objectMapper().readValue(response, ListModelsResponse::class.java)
return listResponse.data
}

override fun chat(
chatRequest: ModelSchema.ChatRequest,
model: ChatModel,
Expand All @@ -58,11 +107,24 @@ class DeepSeekChatClient(
}

companion object {
private val log = getLogger(DeepSeekChatClient::class.java)
private val modelsCache = ConcurrentHashMap<String, List<ChatModel>>()

const val HEADER_CONTENT_TYPE = "Content-Type"
const val HEADER_ACCEPT = "Accept"
const val HEADER_AUTHORIZATION = "Authorization"
const val APPLICATION_JSON = "application/json"

data class ModelInfo(
val id: String,
val `object`: String? = null,
val owned_by: String? = null
)

data class ListModelsResponse(
val `object`: String? = null,
val data: List<ModelInfo> = emptyList()
)

fun toDeepSeek(chatRequest: ModelSchema.ChatRequest): Map<String, Any> {
val request = mutableMapOf<String, Any>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,89 @@
package com.simiacryptus.cognotik.chat.model

import com.simiacryptus.cognotik.CoreProviders
import com.simiacryptus.cognotik.models.ModelSchema.TokenTypes

@Suppress("unused")
object DeepSeekModels {
val DeepSeekChat = ChatModel(
name = "DeepSeekChat",
modelId = "deepseek-chat",
maxTotalTokens = 64000,
maxOutTokens = 4096,
provider = CoreProviders.DeepSeek,
inputTokenPricePerK = 0.14 / 1000.0,
outputTokenPricePerK = 0.28 / 1000.0,
inputModalities = setOf(ChatMessageModality.TEXT),
outputModalities = setOf(ChatMessageModality.TEXT)

// Pricing is quoted per 1M tokens in the DeepSeek docs; convert to per-1k.
// deepseek-v4-flash:
// cache hit (Cached) input: $0.0028 / 1M
// cache miss (Prompt) input: $0.14 / 1M
// output (Completion): $0.28 / 1M
private val flashPricing = mapOf(
TokenTypes.Prompt to 0.14 / 1000.0,
TokenTypes.Cached to 0.0028 / 1000.0,
TokenTypes.Completion to 0.28 / 1000.0,
TokenTypes.Thinking to 0.28 / 1000.0,
)

// deepseek-v4-pro:
// cache hit (Cached) input: $0.003625 / 1M
// cache miss (Prompt) input: $0.435 / 1M
// output (Completion): $0.87 / 1M
private val proPricing = mapOf(
TokenTypes.Prompt to 0.435 / 1000.0,
TokenTypes.Cached to 0.003625 / 1000.0,
TokenTypes.Completion to 0.87 / 1000.0,
TokenTypes.Thinking to 0.87 / 1000.0,
)

val DeepSeekV4Flash = ChatModel(
name = "DeepSeekV4Flash",
modelId = "deepseek-v4-flash",
maxTotalTokens = 1_000_000,
maxOutTokens = 384_000,
provider = CoreProviders.DeepSeek,
tokenPricingPerK = flashPricing,
supportsReasoning = true,
inputModalities = setOf(ChatMessageModality.TEXT),
outputModalities = setOf(ChatMessageModality.TEXT)
)
val DeepSeekCoder = ChatModel(
name = "DeepSeekCoder",
modelId = "deepseek-coder",
maxTotalTokens = 64000,
maxOutTokens = 4096,
provider = CoreProviders.DeepSeek,
inputTokenPricePerK = 0.14 / 1000.0,
outputTokenPricePerK = 0.28 / 1000.0,
inputModalities = setOf(ChatMessageModality.TEXT),
outputModalities = setOf(ChatMessageModality.TEXT)

val DeepSeekV4Pro = ChatModel(
name = "DeepSeekV4Pro",
modelId = "deepseek-v4-pro",
maxTotalTokens = 1_000_000,
maxOutTokens = 384_000,
provider = CoreProviders.DeepSeek,
tokenPricingPerK = proPricing,
supportsReasoning = true,
inputModalities = setOf(ChatMessageModality.TEXT),
outputModalities = setOf(ChatMessageModality.TEXT)
)

// Deprecated aliases (removal scheduled 2026/07/24). deepseek-chat maps to the
// non-thinking mode and deepseek-reasoner to the thinking mode of deepseek-v4-flash.
val DeepSeekChat = ChatModel(
name = "DeepSeekChat",
modelId = "deepseek-chat",
maxTotalTokens = 1_000_000,
maxOutTokens = 384_000,
provider = CoreProviders.DeepSeek,
tokenPricingPerK = flashPricing,
deprecated = true,
inputModalities = setOf(ChatMessageModality.TEXT),
outputModalities = setOf(ChatMessageModality.TEXT)
)

val DeepSeekReasoner = ChatModel(
name = "DeepSeekReasoner",
modelId = "deepseek-reasoner",
maxTotalTokens = 64000,
maxOutTokens = 4096,
provider = CoreProviders.DeepSeek,
inputTokenPricePerK = 0.55 / 1000.0,
outputTokenPricePerK = 2.19 / 1000.0,
inputModalities = setOf(ChatMessageModality.TEXT),
outputModalities = setOf(ChatMessageModality.TEXT)
name = "DeepSeekReasoner",
modelId = "deepseek-reasoner",
maxTotalTokens = 1_000_000,
maxOutTokens = 384_000,
provider = CoreProviders.DeepSeek,
tokenPricingPerK = flashPricing,
supportsReasoning = true,
deprecated = true,
inputModalities = setOf(ChatMessageModality.TEXT),
outputModalities = setOf(ChatMessageModality.TEXT)
)

val values = mapOf(
"DeepSeekV4Flash" to DeepSeekV4Flash,
"DeepSeekV4Pro" to DeepSeekV4Pro,
"DeepSeekChat" to DeepSeekChat,
"DeepSeekCoder" to DeepSeekCoder,
"DeepSeekReasoner" to DeepSeekReasoner,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class PluginManager(
init {
Thread {
sleep(1000)
log.info("PluginManager initialized", RuntimeException("PluginManager init stack trace"))
root.mkdirs()
restorePlugins()
}.start()
Expand Down
Loading