Extending Cognotik
Cognotik provides a flexible extension system based on the DynamicEnum pattern,
allowing you to add new providers, tools, runtimes, cognitive modes, and task types
without modifying the core codebase.
DynamicEnum Pattern
The DynamicEnum class provides a runtime-extensible alternative to Java enums. It supports:
- Registration: New values can be registered at runtime using
DynamicEnum.register() - Lookup: Values can be retrieved by name using
valueOf()or listed usingvalues() - Serialization: Built-in Jackson serializers and deserializers for JSON support
// Example: Creating a custom DynamicEnum
class MyExtension(name: String) : DynamicEnum<MyExtension>(name) {
companion object {
val Default = MyExtension("Default")
init {
register(MyExtension::class.java, Default)
}
fun values() = values(MyExtension::class.java)
fun valueOf(name: String) = valueOf(MyExtension::class.java, name)
}
}
API Providers
API providers define connections to AI services. The APIProvider class supports
chat, embeddings, image generation, and transcription capabilities.
Built-in Providers
| Provider | Base URL | Features |
|---|---|---|
| OpenAI | https://api.openai.com/v1 |
Chat, Embeddings, Images, Transcription |
| Anthropic | https://api.anthropic.com/v1 |
Chat (Claude models) |
| Gemini | https://generativelanguage.googleapis.com |
Chat, Images |
| AWS | https://api.openai.aws |
Chat (Bedrock models) |
| Groq | https://api.groq.com/openai/v1 |
Chat, Transcription |
| Perplexity | https://api.perplexity.ai |
Chat |
| Mistral | https://api.mistral.ai/v1 |
Chat |
| DeepSeek | https://api.deepseek.com |
Chat |
| ModelsLab | https://modelslab.com/api/v6 |
Chat |
| Ollama | http://localhost:11434 |
Chat, Embeddings (local) |
| SearchAPI | https://api.searchapi.com |
Search functionality |
| Search engine ID | Google Search | |
| Github | https://api.github.com |
GitHub API |
Creating a Custom Provider
val CustomProvider = object : APIProvider("CustomProvider", "https://api.custom.com") {
override fun getChatModels(key: SecureString, baseUrl: String): List<ChatModel> {
// Return available chat models
return listOf(/* your models */)
}
override fun getChatClient(
key: SecureString,
base: String,
workPool: ExecutorService,
logLevel: Level,
logStreams: MutableList<BufferedOutputStream>,
scheduledPool: ListeningScheduledExecutorService
): ChatClientInterface {
return YourChatClient(key, base, workPool, scheduledPool)
}
// Optional: Override for custom authorization
override fun authorize(request: HttpRequest, key: String, apiBase: String) {
request.addHeader("X-Custom-Auth", key)
}
// Optional: Support embeddings
override fun getEmbeddingModels(key: SecureString, baseUrl: String): List<EmbeddingModel> {
return listOf(/* your embedding models */)
}
// Optional: Support image generation
override fun getImageModels(key: SecureString, baseUrl: String): List<ImageModel> {
return listOf(/* your image models */)
}
}
// Register the provider
APIProvider.register(APIProvider::class.java, CustomProvider)
Tool Providers
Tool providers define external executables that can be discovered and used by the system.
Built-in Tool Providers
| Category | Tools |
|---|---|
| Version Control | Git |
| Languages | Python, Node, Ruby, PHP, Go, Rust, Haskell, OCaml, Julia, Scala |
| JVM | Jdk (java, javac, jdb, javap, jlink, jarsigner, javadoc, jshell, jcmd, jconsole, jstat, jmap, jhat, jinfo, jstack) |
| Build Tools | Gradle, Maven, Ant, Make, Cmake |
| Shells | Bash, Zsh, Powershell |
| Containers | Docker, Kubectl |
| Cloud | Gcloud, Aws, Terraform |
| Document Processing | Latex, Pandoc |
| Media | Ffmpeg, Gnuplot, Dot (Graphviz) |
| Math/Science | Octave, Maxima, Sage, Gap, Singular, PariGP |
| Theorem Provers | Z3, CVC5, Lean, Coq, Isabelle, Agda |
| Logic | Prolog |
| Compilers | Gcc |
| Remote | SSH |
| Language Servers | pylsp, typescript-language-server, kotlin-language-server, jdtls, clangd, gopls, rust-analyzer, bash-language-server, docker-langserver, texlab, yaml-language-server |
Creating a Custom Tool Provider
val CustomTool = object : ToolProvider("CustomTool") {
override fun getExecutables() = listOf("custom-tool", "custom-tool.exe")
override fun getVersion(path: String): String? {
return runCommand(listOf(path, "--version"))
}
// Optional: Custom validation
override fun validate(path: String): Boolean {
return try {
val version = getVersion(path)
version != null && version.contains("CustomTool")
} catch (e: Exception) {
false
}
}
}
// Register the tool
ToolProvider.register(ToolProvider::class.java, CustomTool)
Tool Discovery
// Discover all tools from PATH
val tools = ToolProvider.discoverAllToolsFromPath()
// Scan a directory recursively
val projectTools = ToolProvider.scanRecursive(File("/path/to/project"), depth = 3)
// Resolve a specific tool
val pythonPath = ToolProvider.Python.resolve("/usr/local")
Code Runtimes
Code runtimes execute code in various languages.
Built-in Runtimes
| Runtime | Extension | Description |
|---|---|---|
| KotlinRuntime | .kts |
Execute Kotlin code with full JVM access |
| GroovyRuntime | .groovy |
Execute Groovy code with dynamic scripting |
| BashRuntime | .sh |
Execute Bash shell scripts (Unix/Linux/Mac) |
| PowerShellRuntime | .ps1 |
Execute PowerShell scripts (Windows/Cross-platform) |
| CmdRuntime | .bat |
Execute Windows Command Prompt scripts |
| PythonRuntime | .py |
Execute Python scripts |
| NodeJsRuntime | .js |
Execute Node.js JavaScript code |
| RubyRuntime | .rb |
Execute Ruby scripts |
| PerlRuntime | .pl |
Execute Perl scripts |
| RRuntime | .R |
Execute R scripts |
| PhpRuntime | .php |
Execute PHP scripts |
| LuaRuntime | .lua |
Execute Lua scripts |
| GoRuntime | .go |
Execute Go code |
| RustRuntime | .rs |
Execute Rust code (via rust-script) |
| ScalaRuntime | .scala |
Execute Scala scripts |
Creating a Custom Runtime
val CustomRuntime = CodeRuntimes("CustomRuntime", "Execute custom scripts", "custom")
CodeRuntimes.registerConstructor(CustomRuntime) { defs ->
ProcessCodeRuntime(
timeoutMinutes = defs["timeoutMinutes"]?.toString()?.toLongOrNull() ?: 15L,
workingDir = defs["workingDir"]?.toString()?.let { File(it) } ?: File("."),
env = defs["env"]?.let { it as Map<String, String> },
lang = "custom",
command = listOf("custom-interpreter")
)
}
Using Runtimes
val runtime = CodeRuntimes.getRuntime(
CodeRuntimes.PythonRuntime,
mapOf(
"timeoutMinutes" to 30L,
"workingDir" to "/path/to/project"
)
)
val result = runtime.execute("print('Hello, World!')")
Cognitive Modes
Cognitive modes define different AI interaction patterns.
Built-in Modes
| Mode | Config Class | Description |
|---|---|---|
| Chat | ConversationalModeConfig |
Standard conversational interaction |
| Adaptive | AdaptivePlanningConfig |
Dynamic planning with cognitive schema strategies |
| Waterfall | WaterfallModeConfig |
Sequential task execution |
| Hierarchical | CognitiveModeConfig |
Hierarchical task decomposition |
| Parallel | ParallelModeConfig |
Concurrent task execution |
| Protocol | ProtocolModeConfig |
Protocol-driven interaction |
| Council | CouncilModeConfig |
Multi-agent deliberation |
| PersonaChat | PersonaChatConfig |
Persona-based conversation |
| Coding | CodingModeConfig |
Code-focused interaction |
Creating a Custom Cognitive Mode
// 1. Define the config class
class CustomModeConfig : CognitiveModeConfig() {
var customSetting: String = "default"
}
// 2. Define the mode type
val CustomMode = CognitiveModeType(
"CustomMode",
CustomModeConfig::class.java,
inputCnt = 1
)
// 3. Implement the mode
class CustomCognitiveMode(
config: OrchestrationConfig,
session: Session,
user: User
) : CognitiveMode<CustomModeConfig>(config, session, user) {
override fun run(messages: List<String>): String {
// Implement your cognitive mode logic
return "Result"
}
}
// 4. Register (done in CognitiveModeType companion object)
Cognitive Schema Strategies
Schema strategies define how the adaptive planning mode maintains and updates its cognitive state.
Built-in Strategies
| Strategy | Description |
|---|---|
| ProjectManager | Standard goal-oriented planning with short/long-term goals |
| ScientificMethod | Hypothesis-driven investigation with evidence tracking |
| AgileDeveloper | Iterative TDD with user stories and acceptance criteria |
| CriticalAuditor | Security and logic validation with risk assessment |
| CreativeWriter | Narrative generation with theme and audience tracking |
Creating a Custom Strategy
// 1. Define your state class
data class CustomState(
val objective: String? = null,
val progress: MutableList<String>? = null,
val metrics: MutableMap<String, Double>? = null
)
// 2. Implement the strategy
class CustomStrategy : CognitiveSchemaStrategy(
"Custom Strategy",
"Description of your strategy"
) {
override fun initialize(
userMessage: String,
contextData: List<String>,
orchestrationConfig: OrchestrationConfig,
task: SessionTask,
describer: TaskContextYamlDescriber
): Any {
return ParsedAgent(
name = "CustomInitializer",
resultClass = CustomState::class.java,
exampleInstance = CustomState(
objective = "Example objective",
progress = mutableListOf("Step 1"),
metrics = mutableMapOf("completion" to 0.0)
),
prompt = "Initialize the custom state based on the user request...",
model = orchestrationConfig.defaultSmart.getChildClient(task),
parsingChatter = orchestrationConfig.defaultFast.getChildClient(task),
temperature = orchestrationConfig.temperature,
describer = describer
).answer(listOf(userMessage) + contextData).obj
}
override fun update(
currentState: Any,
completedTasks: List<AdaptivePlanningMode.ExecutionRecord>,
userMessage: String?,
contextData: List<String>,
orchestrationConfig: OrchestrationConfig,
task: SessionTask,
describer: TaskContextYamlDescriber
): Any {
// Update logic
return currentState
}
override fun formatState(state: Any): String {
return JsonUtil.toJson(state)
}
override fun getTaskSelectionGuidance(state: Any): String {
return "Select tasks based on current metrics and progress."
}
}
// 3. Register the strategy
DynamicEnum.register(CognitiveSchemaStrategy::class.java, CustomStrategy())
Task Types
Task types define discrete operations that can be executed within plans.
Built-in Task Categories
| Category | Tasks |
|---|---|
| Reasoning | AbductiveReasoning, AbstractionLadder, AdversarialReasoning, AnalogicalReasoning, CausalInference, ChainOfThought, ConstraintRelaxation, ConstraintSatisfaction, CounterfactualAnalysis, DecisionTree, DecompositionSynthesis, DialecticalReasoning, EthicalReasoning, FiniteStateMachine, FunctorialMapping, GameTheory, GeneticOptimization, IsomorphismDiscovery, LateralThinking, MathematicalReasoning, MetaCognitiveReflection, MultiPerspectiveAnalysis, NeuralNetworkLayer, ProbabilisticReasoning, SocraticDialogue, StructuralInvariantAnalysis, SystemsThinking, TemporalReasoning |
| Writing | ArticleGeneration, BusinessProposal, ComicBookGeneration, EmailCampaign, InteractiveStory, NarrativeGeneration, PersuasiveEssay, ReportGeneration, ResearchPaperGeneration, Scriptwriting, SoftwareDesignDocument, TechnicalExplanation, TutorialGeneration |
| File Operations | FileAppend, FileModification, FileSearch, ReadDocuments, WriteHtml |
| Code Execution | AutoFix, LanguageServer, RunCode, RunTool, SingleFix, SymbolsDbCode |
| Image | GenerateImage, GenerateQRImage, GenerateSpriteSheet, IllustrateDocument, ImageDecomposition, ImageTable, ImageVariation, SegmentedImageGeneration, TiledImageGeneration |
| Data | DataIngest, DataTableCompilation, TableCompilation, OCR, PdfForm |
| Online | CrawlerAgent, GitHubSearch, MCPTool |
| Session | CommandSession, JdbcSession |
| Social | Brainstorming, Discussion, JournalismReasoning, LLMExperiment, LLMPollSimulation, PoliticalOptimization |
| Games | GameEconomy, GameLevelDesign, GameMechanicsDesign, GameNarrativeDesign |
| Planning | SubPlan, IterativeGraphGeneration, GeneratePresentation |
Creating a Custom Task Type
// 1. Define the execution config
data class CustomTaskConfig(
override var task_type: String? = CustomTask.name,
override var task_description: String? = null,
var customParameter: String? = null
) : TaskExecutionConfig()
// 2. Define the task settings
data class CustomTaskSettings(
override var task_type: String? = CustomTask.name,
override var enabled: Boolean = true,
var defaultValue: String = "default"
) : TaskTypeConfig()
// 3. Implement the task
class CustomTaskImpl(
settings: OrchestrationConfig,
task: CustomTaskConfig?
) : AbstractTask<CustomTaskConfig, CustomTaskSettings>(settings, task) {
override fun promptSegment(): String {
return """
CustomTask - Performs custom operations
** Specify the custom parameter
""".trimIndent()
}
override fun run(
agent: PlanCoordinator,
messages: List<String>,
task: SessionTask,
api: API,
resultFn: (String) -> Unit,
api2: OpenAIClient,
planSettings: PlanSettings
): TaskResult {
// Implementation
return TaskResult("Custom task completed")
}
companion object {
val CustomTask = TaskType(
name = "CustomTask",
category = "Custom",
taskClass = CustomTaskImpl::class.java,
executionConfigClass = CustomTaskConfig::class.java,
taskSettingsClass = CustomTaskSettings::class.java,
description = "Performs custom operations"
)
}
}
// 4. Register (done via registerConstructor in TaskType companion object)
Best Practices
- Use meaningful names: Choose descriptive names for your extensions that clearly indicate their purpose.
- Provide documentation: Include descriptions and tooltips where supported.
- Handle errors gracefully: Implement proper error handling and validation.
- Follow existing patterns: Study the built-in implementations for guidance.
- Register early: Register your extensions during application initialization.
- Test thoroughly: Verify your extensions work correctly with the rest of the system.
Serialization
All DynamicEnum-based extensions support JSON serialization:
// Custom serializer (usually auto-generated)
class MyExtensionSerializer : DynamicEnumSerializer<MyExtension>(MyExtension::class.java)
class MyExtensionDeserializer : DynamicEnumDeserializer<MyExtension>(MyExtension::class.java)
// Apply to your class
@JsonDeserialize(using = MyExtensionDeserializer::class)
@JsonSerialize(using = MyExtensionSerializer::class)
class MyExtension(name: String) : DynamicEnum<MyExtension>(name)