diff --git a/docs/models/deepseek_models.md b/docs/models/deepseek_models.md new file mode 100644 index 000000000..e0fdc925c --- /dev/null +++ b/docs/models/deepseek_models.md @@ -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) diff --git a/gradle.properties b/gradle.properties index 548396d95..1bb13d303 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 @@ -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= diff --git a/providers/src/main/kotlin/com/simiacryptus/cognotik/chat/DeepSeekChatClient.kt b/providers/src/main/kotlin/com/simiacryptus/cognotik/chat/DeepSeekChatClient.kt index 2a4909cfe..3e1c2e750 100644 --- a/providers/src/main/kotlin/com/simiacryptus/cognotik/chat/DeepSeekChatClient.kt +++ b/providers/src/main/kotlin/com/simiacryptus/cognotik/chat/DeepSeekChatClient.kt @@ -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( @@ -39,6 +43,51 @@ class DeepSeekChatClient( request.addHeader(HEADER_AUTHORIZATION, "Bearer ${apiKey.decrypt}") } + override fun getModels(): List { + // 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 { + 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, @@ -58,11 +107,24 @@ class DeepSeekChatClient( } companion object { + private val log = getLogger(DeepSeekChatClient::class.java) + private val modelsCache = ConcurrentHashMap>() + 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 = emptyList() + ) fun toDeepSeek(chatRequest: ModelSchema.ChatRequest): Map { val request = mutableMapOf( diff --git a/providers/src/main/kotlin/com/simiacryptus/cognotik/chat/model/DeepSeekModels.kt b/providers/src/main/kotlin/com/simiacryptus/cognotik/chat/model/DeepSeekModels.kt index 9151612aa..f821bf57e 100644 --- a/providers/src/main/kotlin/com/simiacryptus/cognotik/chat/model/DeepSeekModels.kt +++ b/providers/src/main/kotlin/com/simiacryptus/cognotik/chat/model/DeepSeekModels.kt @@ -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, ) diff --git a/webui/src/main/kotlin/com/simiacryptus/cognotik/platform/PluginManager.kt b/webui/src/main/kotlin/com/simiacryptus/cognotik/platform/PluginManager.kt index d2ec49782..8557feb6a 100644 --- a/webui/src/main/kotlin/com/simiacryptus/cognotik/platform/PluginManager.kt +++ b/webui/src/main/kotlin/com/simiacryptus/cognotik/platform/PluginManager.kt @@ -59,7 +59,6 @@ class PluginManager( init { Thread { sleep(1000) - log.info("PluginManager initialized", RuntimeException("PluginManager init stack trace")) root.mkdirs() restorePlugins() }.start()