-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
User Story
As a conflict resolution system, I want to leverage Claude's understanding to intelligently resolve Git conflicts, so that developers can automate complex merge conflict resolution with high confidence.
Acceptance Criteria
- Integrate Claude API with proper authentication and rate limiting
- Build comprehensive prompts with safety guidelines and context
- Include full function/class context in conflict resolution requests
- Support multiple Claude models with fallback (Opus → Sonnet → Haiku)
- Implement response caching for similar conflicts
- Parse and validate Claude's responses for code correctness
- Handle API failures gracefully with retry logic
- Track token usage and costs per resolution
- Support custom prompt templates per language/framework
- Generate explanation for each resolution decision
Technical Implementation
Claude Integration Service
// pkg/claude/service.go
package claude
import (
"context"
"github.com/anthropics/anthropic-sdk-go"
)
type ClaudeService struct {
client *anthropic.Client
config *ClaudeConfig
rateLimiter *RateLimiter
costManager *CostManager
cache *ResponseCache
metrics *MetricsCollector
}
type ClaudeConfig struct {
APIKey string
DefaultModel string
FallbackModels []string
MaxTokens int
Temperature float64
MaxRetries int
CacheEnabled bool
CacheTTL time.Duration
SafetyMode bool
}
type ConflictResolutionRequest struct {
Conflict Conflict
Context ConflictContext
Repository RepositoryInfo
Guidelines []string
Examples []ResolutionExample
}
type ConflictResolutionResponse struct {
Resolution string
Explanation string
Confidence float64
Model string
TokensUsed TokenUsage
CacheHit bool
}
func (s *ClaudeService) ResolveConflict(ctx context.Context, req ConflictResolutionRequest) (*ConflictResolutionResponse, error) {
// Check cache first
if s.config.CacheEnabled {
if cached := s.cache.Get(req.Conflict); cached != nil {
s.metrics.RecordCacheHit()
return cached, nil
}
}
// Check rate limits and budget
if err := s.rateLimiter.Wait(ctx); err != nil {
return nil, fmt.Errorf("rate limit exceeded: %w", err)
}
estimatedTokens := s.estimateTokens(req)
if err := s.costManager.CheckBudget(ctx, estimatedTokens); err != nil {
return nil, fmt.Errorf("budget exceeded: %w", err)
}
// Build prompt
prompt := s.buildPrompt(req)
// Try models in order
var lastErr error
models := append([]string{s.config.DefaultModel}, s.config.FallbackModels...)
for _, model := range models {
response, err := s.callClaude(ctx, model, prompt)
if err == nil {
// Parse and validate response
resolution, err := s.parseResponse(response, req.Conflict)
if err == nil {
// Track usage
s.costManager.RecordUsage(model, response.Usage)
s.metrics.RecordResolution(model, true)
// Cache if enabled
if s.config.CacheEnabled {
s.cache.Put(req.Conflict, resolution, s.config.CacheTTL)
}
return resolution, nil
}
}
lastErr = err
s.metrics.RecordResolution(model, false)
}
return nil, fmt.Errorf("all models failed: %w", lastErr)
}Prompt Building
// pkg/claude/prompt_builder.go
type PromptBuilder struct {
templates map[string]*PromptTemplate
safety *SafetyGuidelines
}
type PromptTemplate struct {
System string
User string
Examples []Example
}
func (b *PromptBuilder) BuildConflictPrompt(req ConflictResolutionRequest) *anthropic.MessageRequest {
template := b.getTemplate(req.Conflict.FileType)
systemPrompt := b.formatSystemPrompt(template.System, req)
userPrompt := b.formatUserPrompt(template.User, req)
// Add safety guidelines
if req.Guidelines != nil {
systemPrompt = b.safety.ApplyGuidelines(systemPrompt, req.Guidelines)
}
messages := []anthropic.Message{
{
Role: "user",
Content: userPrompt,
},
}
// Add examples if available
for _, example := range template.Examples {
messages = append(messages,
anthropic.Message{Role: "user", Content: example.Input},
anthropic.Message{Role: "assistant", Content: example.Output},
)
}
return &anthropic.MessageRequest{
Model: req.Model,
System: systemPrompt,
Messages: messages,
MaxTokens: req.MaxTokens,
Temperature: req.Temperature,
}
}
func (b *PromptBuilder) formatSystemPrompt(template string, req ConflictResolutionRequest) string {
return fmt.Sprintf(`You are helping resolve git merge conflicts in a secure environment.
Your solutions must:
1. Preserve the intent of both conflicting changes
2. Maintain code correctness and safety
3. Not introduce security vulnerabilities
4. Follow the existing code style and patterns
5. Be syntactically valid for %s
IMPORTANT SAFETY RULES:
- Never include code that executes system commands
- Never include code that evaluates dynamic strings
- Never include hardcoded credentials or secrets
- Never include code that accesses external resources without validation
- Always validate user input
- Always handle errors appropriately
Language: %s
Repository: %s
Branch Context: Merging '%s' into '%s'
%s`,
req.Conflict.FileType,
req.Conflict.FileType,
req.Repository.Name,
req.Context.CurrentBranch,
req.Context.TargetBranch,
template,
)
}
func (b *PromptBuilder) formatUserPrompt(template string, req ConflictResolutionRequest) string {
contextLines := 50 // Configurable
return fmt.Sprintf(`File: %s
Conflict Location: Line %d-%d
Context before conflict (%d lines):
\`\`\`%s
%s
\`\`\`
CONFLICT SECTION:
\`\`\`
<<<<<<< %s
%s
=======
%s
>>>>>>> %s
\`\`\`
Context after conflict (%d lines):
\`\`\`%s
%s
\`\`\`
Function/Class Context:
- Function: %s
- Class: %s
Please provide:
1. A resolved version that merges both changes appropriately
2. A brief explanation of your resolution approach
3. Any potential issues to watch for
Format your response as:
RESOLUTION:
\`\`\`%s
[resolved code here]
\`\`\`
EXPLANATION:
[Your explanation here]
CONFIDENCE: [HIGH/MEDIUM/LOW]`,
req.Conflict.FilePath,
req.Conflict.CurrentBlock.StartLine,
req.Conflict.CurrentBlock.EndLine,
contextLines,
req.Conflict.FileType,
req.Context.BeforeContext,
req.Context.CurrentBranch,
req.Conflict.CurrentBlock.Content,
req.Conflict.IncomingBlock.Content,
req.Context.TargetBranch,
contextLines,
req.Conflict.FileType,
req.Context.AfterContext,
req.Context.FunctionName,
req.Context.ClassName,
req.Conflict.FileType,
)
}Response Parsing and Validation
// pkg/claude/response_parser.go
type ResponseParser struct {
validators map[string]CodeValidator
}
func (p *ResponseParser) ParseResponse(response *anthropic.MessageResponse, conflict Conflict) (*ConflictResolutionResponse, error) {
content := response.Content[0].Text
// Extract resolution code
resolutionPattern := regexp.MustCompile(`(?s)RESOLUTION:\s*\` + "`" + `{3}[a-zA-Z]*\n(.*?)\` + "`" + `{3}`)
matches := resolutionPattern.FindStringSubmatch(content)
if len(matches) < 2 {
return nil, fmt.Errorf("could not extract resolution from response")
}
resolution := matches[1]
// Extract explanation
explanationPattern := regexp.MustCompile(`(?s)EXPLANATION:\s*\n(.*?)(?:\n\nCONFIDENCE:|$)`)
explanationMatches := explanationPattern.FindStringSubmatch(content)
explanation := ""
if len(explanationMatches) >= 2 {
explanation = strings.TrimSpace(explanationMatches[1])
}
// Extract confidence
confidencePattern := regexp.MustCompile(`CONFIDENCE:\s*(HIGH|MEDIUM|LOW)`)
confidenceMatches := confidencePattern.FindStringSubmatch(content)
confidence := 0.5
if len(confidenceMatches) >= 2 {
switch confidenceMatches[1] {
case "HIGH":
confidence = 0.9
case "MEDIUM":
confidence = 0.7
case "LOW":
confidence = 0.4
}
}
// Validate the resolution
validator := p.validators[conflict.FileType]
if validator != nil {
if err := validator.Validate(resolution); err != nil {
return nil, fmt.Errorf("invalid resolution code: %w", err)
}
}
return &ConflictResolutionResponse{
Resolution: resolution,
Explanation: explanation,
Confidence: confidence,
Model: response.Model,
TokensUsed: TokenUsage{
Prompt: response.Usage.InputTokens,
Completion: response.Usage.OutputTokens,
Total: response.Usage.InputTokens + response.Usage.OutputTokens,
},
CacheHit: false,
}, nil
}Cost Management
// pkg/claude/cost_manager.go
type CostManager struct {
pricing map[string]PricingTier
budgets *BudgetManager
usage *UsageTracker
}
type PricingTier struct {
InputCostPer1K float64
OutputCostPer1K float64
}
var DefaultPricing = map[string]PricingTier{
"claude-3-opus": {InputCostPer1K: 0.015, OutputCostPer1K: 0.075},
"claude-3-sonnet": {InputCostPer1K: 0.003, OutputCostPer1K: 0.015},
"claude-3-haiku": {InputCostPer1K: 0.00025, OutputCostPer1K: 0.00125},
}
func (m *CostManager) CheckBudget(ctx context.Context, estimatedTokens int) error {
userID := getUserID(ctx)
budget := m.budgets.GetUserBudget(userID)
estimatedCost := m.estimateCost("claude-3-opus", estimatedTokens)
currentUsage := m.usage.GetMonthlyUsage(userID)
if currentUsage + estimatedCost > budget {
return fmt.Errorf("monthly budget exceeded: used $%.2f of $%.2f", currentUsage, budget)
}
return nil
}
func (m *CostManager) RecordUsage(model string, usage anthropic.Usage) {
cost := m.calculateCost(model, usage)
m.usage.Record(UsageRecord{
Model: model,
InputTokens: usage.InputTokens,
OutputTokens: usage.OutputTokens,
Cost: cost,
Timestamp: time.Now(),
})
}Response Caching
// pkg/claude/cache.go
type ResponseCache struct {
storage CacheStorage
hasher *ConflictHasher
similarity *SimilarityChecker
minSimilarity float64
}
func (c *ResponseCache) Get(conflict Conflict) *ConflictResolutionResponse {
// Generate hash for exact match
hash := c.hasher.Hash(conflict)
if exact := c.storage.GetExact(hash); exact != nil {
return exact
}
// Try similarity matching
similar := c.storage.FindSimilar(conflict, c.minSimilarity)
if len(similar) > 0 {
// Return most similar
return similar[0].Response
}
return nil
}
func (c *ResponseCache) Put(conflict Conflict, response *ConflictResolutionResponse, ttl time.Duration) {
hash := c.hasher.Hash(conflict)
entry := CacheEntry{
Hash: hash,
Conflict: conflict,
Response: response,
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(ttl),
Embeddings: c.generateEmbeddings(conflict),
}
c.storage.Store(entry)
}Architecture References
Claude Integration in Architecture
Reference: /docs/02-system-components.md:383-411
The Claude Integration Service is designed with cost management and safety:
graph TD
A[Claude Service] --> B[Cost Manager]
B --> C[Rate Limiter]
C --> D[Request Queue]
D --> E[Priority Manager]
E --> F[Claude SDK]
F --> G[Response Cache]
G --> H[Result Validator]
H --> I[Audit Logger]
Conflict Resolution with Claude
Reference: /docs/02-system-components.md:269-307
async def resolve_with_validation(self, conflicts: List[Conflict], target_branch: str) -> List[ResolvedConflict]:
"""Resolve conflicts with multiple validation layers"""
resolved = []
for conflict in conflicts:
# Get Claude's suggestion
claude_solution = await self._get_claude_resolution(conflict)
# Validate the solution
validation_result = await self.validator.validate_resolution(
conflict, claude_solution
)Prompt Templates
Reference: /docs/02-system-components.md:412-456
Enhanced prompt templates with safety guidelines:
conflict_resolution:
system: |
You are helping resolve git merge conflicts in a secure environment.
IMPORTANT: Do not include any code that:
- Executes system commands
- Evaluates dynamic code
- Accesses external resources without validation
- Contains hardcoded credentials or secretsCost Tracking Schema
Reference: /docs/02-system-components.md:629-644
CREATE TABLE claude_api_usage (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
model VARCHAR(50) NOT NULL,
operation_type VARCHAR(50),
prompt_tokens INTEGER,
completion_tokens INTEGER,
total_tokens INTEGER,
cost_usd DECIMAL(10, 6),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Dependencies
- Anthropic Go SDK: Claude API integration
- PostgreSQL: Usage tracking and caching
- Redis: Response cache indexing
- Prometheus: Metrics collection
Definition of Done
- Unit tests cover prompt building and response parsing with 90%+ coverage
- Integration tests verify Claude API calls with mock responses
- Safety validation catches dangerous code patterns
- Cost tracking accurately calculates usage
- Cache hit rate >40% for similar conflicts
- Documentation includes prompt engineering guide
- Metrics dashboard shows resolution success rates
Effort Estimate
13 Story Points - Complex integration with multiple validation layers
Labels
- backend
- claude
- integration
- epic-5