This document describes the model capability detection system implemented in the Thea Code project. The system provides a standardized way to detect and use model capabilities across the codebase, making it more maintainable and provider-agnostic.
The capability detection system consists of two main components:
- Property-based capability detection (
model-capabilities.ts): Functions that check for capabilities based on properties in theModelInfoobject. - Pattern-based capability detection (
model-pattern-detection.ts): Functions that detect capabilities based on model ID patterns and set properties on theModelInfoobject.
This approach replaces hardcoded model ID checks with a more maintainable and centralized system for detecting model capabilities.
The model-capabilities.ts file provides utility functions for detecting model capabilities based on properties in the ModelInfo object:
import type { ModelInfo } from "../schemas"
// Check if a model supports computer use (tool use)
export function supportsComputerUse(modelInfo: ModelInfo): boolean {
return !!modelInfo.supportsComputerUse
}
// Check if a model supports prompt caching
export function supportsPromptCaching(modelInfo: ModelInfo): boolean {
return !!modelInfo.supportsPromptCache
}
// Check if a model supports image input
export function supportsImages(modelInfo: ModelInfo): boolean {
return !!modelInfo.supportsImages
}
// Check if a model supports thinking/reasoning
export function supportsThinking(modelInfo: ModelInfo): boolean {
return !!modelInfo.thinking
}
// Check if a model supports temperature adjustment
export function supportsTemperature(modelInfo: ModelInfo): boolean {
return modelInfo.supportsTemperature !== false
}
// Get the appropriate max tokens for a model
export function getMaxTokens(modelInfo: ModelInfo, defaultMaxTokens: number = 4096): number {
return modelInfo.maxTokens ?? defaultMaxTokens
}
// Get the reasoning effort level for a model
export function getReasoningEffort(modelInfo: ModelInfo): "low" | "medium" | "high" | undefined {
return modelInfo.reasoningEffort
}
// Check if a model supports a specific capability
export function hasCapability(
modelInfo: ModelInfo,
capability: "computerUse" | "promptCache" | "images" | "thinking" | "temperature"
): boolean {
switch (capability) {
case "computerUse":
return supportsComputerUse(modelInfo)
case "promptCache":
return supportsPromptCaching(modelInfo)
case "images":
return supportsImages(modelInfo)
case "thinking":
return supportsThinking(modelInfo)
case "temperature":
return supportsTemperature(modelInfo)
default:
return false
}
}
// Get the context window size for a model
export function getContextWindowSize(modelInfo: ModelInfo, defaultContextWindow: number = 8192): number {
return modelInfo.contextWindow ?? defaultContextWindow
}The model-pattern-detection.ts file provides utility functions for detecting model capabilities based on model ID patterns:
import type { ModelInfo } from "../schemas"
// Detect if a model ID represents a Claude model
export function isClaudeModel(modelId: string): boolean {
return modelId.includes("claude")
}
// Detect if a model ID represents a thinking-enabled model
export function isThinkingModel(modelId: string): boolean {
return modelId.includes(":thinking") || modelId.endsWith("-thinking")
}
// Set capability properties on a ModelInfo object based on model ID patterns
export function setCapabilitiesFromModelId(modelId: string, modelInfo: ModelInfo): ModelInfo {
// Create a copy of the modelInfo object to avoid mutating the original
const updatedModelInfo: ModelInfo = { ...modelInfo }
// Set thinking capability
if (isThinkingModel(modelId)) {
updatedModelInfo.thinking = true
}
// Set capabilities for Claude models
if (isClaudeModel(modelId)) {
// All Claude models support prompt caching
updatedModelInfo.supportsPromptCache = true
// Set cache pricing based on model tier
if (isClaudeOpusModel(modelId)) {
updatedModelInfo.cacheWritesPrice = 18.75
updatedModelInfo.cacheReadsPrice = 1.5
} else if (isClaudeHaikuModel(modelId)) {
updatedModelInfo.cacheWritesPrice = 1.25
updatedModelInfo.cacheReadsPrice = 0.1
} else {
// Default cache pricing for sonnet and other models
updatedModelInfo.cacheWritesPrice = 3.75
updatedModelInfo.cacheReadsPrice = 0.3
}
// Set computer use capability for models that support it
if (isClaude3SonnetModel(modelId) && !modelId.includes("20240620")) {
updatedModelInfo.supportsComputerUse = true
}
// Set max tokens based on model tier and thinking capability
if (isClaude37Model(modelId)) {
updatedModelInfo.maxTokens = updatedModelInfo.thinking ? 64_000 : 8192
updatedModelInfo.supportsComputerUse = true
} else if (isClaude35Model(modelId)) {
updatedModelInfo.maxTokens = 8192
}
}
// Set capabilities for O3 Mini models
if (isO3MiniModel(modelId)) {
updatedModelInfo.supportsTemperature = false
}
// Set capabilities for DeepSeek R1 models
if (isDeepSeekR1Model(modelId)) {
updatedModelInfo.reasoningEffort = "high"
}
return updatedModelInfo
}
// Extract the base model ID from a model ID with variants
export function getBaseModelId(modelId: string): string {
// Remove thinking suffix
if (modelId.includes(":thinking")) {
return modelId.split(":")[0]
}
return modelId
}Here are examples of how to use the capability detection system in provider files:
import { supportsTemperature } from "../../utils/model-capabilities"
// Instead of hardcoded model ID checks:
// if (modelId.startsWith("o3-mini")) {
// // Handle models that don't support temperature
// }
// Use capability detection:
if (!supportsTemperature(modelInfo)) {
// Handle models that don't support temperature
}import { supportsPromptCaching } from "../../utils/model-capabilities"
// Instead of direct property checks:
// if (modelInfo.supportsPromptCache === true) {
// // Apply prompt caching
// }
// Use capability detection:
if (supportsPromptCaching(modelInfo)) {
// Apply prompt caching
}import { getBaseModelId, isThinkingModel } from "../../utils/model-pattern-detection"
// Instead of hardcoded model ID checks:
// if (id.includes(":thinking")) {
// id = id.split(":")[0]
// }
// Use pattern detection:
if (isThinkingModel(id)) {
id = getBaseModelId(id)
}import { setCapabilitiesFromModelId } from "../../utils/model-pattern-detection"
// Instead of hardcoded model ID checks:
// if (rawModel.id.includes("claude")) {
// modelInfo.supportsPromptCache = true
// if (rawModel.id.includes("opus")) {
// modelInfo.cacheWritesPrice = 18.75
// modelInfo.cacheReadsPrice = 1.5
// }
// }
// Use pattern detection:
const baseModelInfo = {
contextWindow: 16384,
supportsPromptCache: false,
}
const modelInfo = setCapabilitiesFromModelId(rawModel.id, baseModelInfo)The capability detection system provides several benefits:
- Maintainability: Centralizes capability detection logic, making it easier to update and maintain.
- Provider-agnostic: Focuses on capabilities rather than specific model IDs, making the code more adaptable to new models.
- Consistency: Ensures consistent capability detection across the codebase.
- Testability: Makes it easier to test capability detection logic in isolation.
- Extensibility: Makes it easier to add new capabilities and detection patterns.
To add a new capability to the system:
- Add the capability property to the
ModelInfointerface inschemas/index.ts. - Add a capability detection function to
model-capabilities.ts. - Update the
hasCapabilityfunction to include the new capability. - Add pattern detection logic to
setCapabilitiesFromModelIdinmodel-pattern-detection.ts. - Add tests for the new capability detection functions.
The capability detection system is thoroughly tested:
model-capabilities.test.ts: Tests for property-based capability detection functions.model-pattern-detection.test.ts: Tests for pattern-based capability detection functions.
These tests ensure that the capability detection system works correctly with various model configurations and IDs.