Skip to content

Create Spring AI ChatModel provider for Claude Code CLI #39

Description

@Lewik

Feature Request

Implement a Spring AI ChatModel provider for Claude Code CLI to unify AI model access in Gromozeka.

Motivation

  • Gromozeka already uses Spring AI for TTS/STT services
  • Currently Claude Code CLI is called directly via ProcessBuilder
  • Spring AI provides unified interface for different AI models
  • Would enable easy switching between Claude API and Claude Code CLI

Current State

Spring AI already supports:

  • Anthropic Claude API directly via spring-ai-anthropic-spring-boot-starter
  • OpenAI, Ollama, and other providers
  • But NO support for Claude Code CLI

There is a community project claude-code-openai-wrapper that makes Claude Code OpenAI-compatible, but native integration would be better.

Proposed Implementation

1. Create ClaudeCodeChatModel

class ClaudeCodeChatModel(
    private val claudeCodePath: String = "claude",
    private val options: ClaudeCodeOptions = ClaudeCodeOptions()
) : ChatModel, StreamingChatModel {
    
    override fun call(prompt: Prompt): ChatResponse {
        val command = buildCommand(prompt, options)
        val process = ProcessBuilder(command).start()
        
        val output = process.inputStream.bufferedReader().readText()
        val messages = parseClaudeOutput(output)
        
        return ChatResponse(messages)
    }
    
    override fun stream(prompt: Prompt): Flux<ChatResponse> {
        return Flux.create { sink ->
            val process = startClaudeProcess(prompt)
            val reader = process.inputStream.bufferedReader()
            
            reader.lineSequence().forEach { line ->
                parseStreamLine(line)?.let { message ->
                    sink.next(ChatResponse(listOf(message)))
                }
            }
            sink.complete()
        }
    }
}

2. Configuration Options

data class ClaudeCodeOptions(
    val outputFormat: OutputFormat = OutputFormat.JSON,
    val resume: String? = null,
    val allowedTools: List<String>? = null,
    val mcpConfig: String? = null,
    val maxTokens: Int? = null
)

3. Spring Boot Auto-Configuration

@Configuration
@ConditionalOnClass(ChatModel::class)
@EnableConfigurationProperties(ClaudeCodeProperties::class)
class ClaudeCodeAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    fun claudeCodeChatModel(properties: ClaudeCodeProperties): ChatModel {
        return ClaudeCodeChatModel(
            claudeCodePath = properties.path,
            options = properties.toOptions()
        )
    }
}

4. Usage Example

@Service
class AiService(
    @Qualifier("claudeCode") private val claudeCode: ChatModel,
    @Qualifier("anthropic") private val claudeApi: ChatModel
) {
    fun processWithClaudeCode(prompt: String): String {
        return claudeCode.call(prompt).result.output.content
    }
    
    fun processWithClaudeApi(prompt: String): String {
        return claudeApi.call(prompt).result.output.content
    }
}

Benefits

  1. Unified Interface - Switch between models without changing code
  2. Spring AI Features - Automatic retry, observability, chat memory
  3. Testing - Easy to mock/stub for tests
  4. Configuration - Standard Spring Boot config management
  5. Future-proof - Can easily add other models later

Implementation Steps

  1. Create ClaudeCodeChatModel class implementing Spring AI interfaces
  2. Add configuration properties for Claude Code CLI options
  3. Create parser for Claude Code JSONL output
  4. Add streaming support for real-time responses
  5. Write tests with mock Claude Code process
  6. Document usage and configuration

Questions for Spring AI Team

Should reach out to Josh Long (@starbuxman) or the Spring AI team to ask:

  • Are there plans for official Claude Code CLI support?
  • Would they accept a PR for this provider?
  • Best practices for process-based ChatModel implementations?

Alternative Approach

If custom provider is too complex, could use the OpenAI wrapper approach:

  1. Use claude-code-openai-wrapper
  2. Configure Spring AI OpenAI provider to point to wrapper
  3. Less code but adds another dependency

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions