Skip to content

Latest commit

 

History

History
482 lines (380 loc) · 12.9 KB

INTEGRATION_GUIDE.md

File metadata and controls

482 lines (380 loc) · 12.9 KB

Smart Brain Integration Guide

This guide walks through integrating the Smart Brain architecture into ThoughtOS.

Step 1: Initialize Core Components

Update AppServices.swift

Add the new services:

// In AppServices class

// Control Plane
@MainActor
private(set) lazy var safeModeMonitor = SafeModeMonitor()

@MainActor  
private(set) lazy var sessionStateManager = SessionStateManager()

// Memory Plane
private(set) lazy var topicGraph = TopicGraph(
    storageRoot: storageRoot
)

private(set) lazy var topicResolver = TopicResolver(
    topicGraph: topicGraph
)

// Smart Brain orchestrator
@MainActor
private(set) var smartBrain: SmartBrain?

// Initialize in setup
func setupSmartBrain() async {
    guard let llm = self.llm else { return }
    
    self.smartBrain = SmartBrain(
        llm: llm,
        retrievalService: retrievalService,
        memoryService: memoryService,
        policyRouter: policyRouter,
        topicGraph: topicGraph,
        safeModeMonitor: safeModeMonitor,
        notesRepository: notesRepository
    )
}

Step 2: Update ChatViewModel

Replace the manual chat flow with SmartBrain:

// In ChatViewModel.send(text:)

// OLD CODE (remove):
// Manual content features, policy routing, retrieval, prompt building

// NEW CODE:
guard let smartBrain = services.smartBrain else {
    messages.append(ChatLogMessage(role: .system, content: "Smart Brain not initialized"))
    return
}

do {
    // Process through Smart Brain
    let smartResponse = try await smartBrain.processChatMessage(
        text: text,
        battery: BatteryIndicator.load(),
        styleScores: styleScores,
        recentMessages: messages
    )
    
    // Add response to messages
    messages.append(ChatLogMessage(
        role: .assistant,
        content: smartResponse.text,
        citations: smartResponse.citations
    ))
    
    // Update last response spec for UI
    lastResponseSpec = smartResponse.spec
    
    // Save to persistence
    try services.saveConversationTurn(
        role: "assistant",
        content: smartResponse.text,
        citations: smartResponse.citations
    )
    
    // Log telemetry
    Diagnostics.shared.record(
        component: "SmartBrain",
        level: .info,
        message: "Response generated",
        metadata: [
            "intent": smartResponse.intent.rawValue,
            "confidence": "\(smartResponse.confidence)",
            "verificationPassed": "\(smartResponse.verificationPassed)"
        ]
    )
} catch {
    Diagnostics.shared.record(
        component: "Chat",
        level: .error,
        message: "Smart Brain processing failed",
        metadata: ["error": error.localizedDescription]
    )
    messages.append(ChatLogMessage(
        role: .system,
        content: "Failed to process message: \(error.localizedDescription)"
    ))
}

Step 3: Add Processing Indicators

Update ScreenQuickNote.swift

Add async processing feedback:

@StateObject private var processingIndicator = ProcessingIndicatorManager()

// In save flow
Task {
    await processingIndicator.processNote(
        stages: [.extracting, .embedding, .linking]
    ) { stage in
        switch stage {
        case .extracting:
            // Extract topics, entities, actions
            _ = try await services.contextPipeline.ingest(text: text, source: .text)
            
        case .embedding:
            // Generate embeddings (if not in safe mode)
            if !services.safeModeMonitor.shouldDisableEmbeddings {
                // Embedding generation
            }
            
        case .linking:
            // Topic graph ingestion
            if let extraction = try? services.summarizer.extract(from: text) {
                await services.topicGraph.ingestNote(
                    id: noteId,
                    topics: extraction.properNouns,
                    projectId: nil,
                    createdAt: Date()
                )
            }
            
        default:
            break
        }
    }
}

// Show indicator in UI
.overlay(alignment: .topTrailing) {
    ProcessingIndicator(status: processingIndicator.status)
        .padding()
}

Step 4: Add Safe Mode UI Indicator

Update ScreenSettings.swift

Add Safe Mode status and controls:

Section("Performance") {
    HStack {
        Label("Safe Mode", systemImage: "shield.fill")
        Spacer()
        Text(services.safeModeMonitor.currentMode.rawValue)
            .foregroundStyle(.secondary)
            .font(.caption)
    }
    
    if services.safeModeMonitor.currentMode != .normal {
        HStack(spacing: 6) {
            Image(systemName: "info.circle")
                .foregroundColor(.orange)
                .font(.caption)
            Text("AI is running in \(services.safeModeMonitor.currentMode.rawValue) to preserve stability")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding(.vertical, 4)
    }
    
    Button("Reset to Normal Mode") {
        services.safeModeMonitor.restoreNormalMode()
    }
    .disabled(services.safeModeMonitor.currentMode == .normal)
}

Step 5: Enable Topic Graph Updates

Update NoteStore.swift

Add topic graph ingestion when notes are saved:

func save(_ note: NoteRecord) async throws {
    // Save note to database
    try database.saveNote(note)
    
    // Async: Update topic graph
    Task.detached(priority: .utility) { [weak self] in
        guard let self = self else { return }
        
        // Extract topics from note
        if let extraction = try? await self.extractTopics(from: note) {
            await self.topicGraph.ingestNote(
                id: note.id,
                topics: extraction.topics,
                projectId: extraction.projectId,
                createdAt: note.createdAt
            )
        }
    }
}

private func extractTopics(from note: NoteRecord) async throws -> (topics: [String], projectId: UUID?) {
    // Use summarizer to extract topics
    let extraction = try summarizer.extract(from: note.plainText)
    return (
        topics: extraction.properNouns,
        projectId: nil // TODO: extract project from entities
    )
}

Step 6: Update RetrievalService

Enhance retrieval to use frames:

// In RetrievalService

private let frameBuilder = RetrievalFrameBuilder()

func buildContextWithFrames(
    for query: String,
    topK: Int = 6,
    maxTokens: Int = 800
) async throws -> RetrievalContextWithFrames {
    
    // Get base context
    let baseContext = try await buildContext(for: query, topK: topK, maxTokens: maxTokens)
    
    // Build frames
    var frameInputs: [(NoteRecord, String, String?)] = []
    for snippet in baseContext.noteSnippets {
        if let note = try? await notesRepository.note(id: snippet.noteId) {
            frameInputs.append((note, snippet.snippetText, snippet.label))
        }
    }
    
    let frames = frameBuilder.buildFrames(
        from: frameInputs,
        maxTokens: maxTokens
    )
    
    return RetrievalContextWithFrames(
        frames: frames,
        memoryFacts: baseContext.memoryFacts,
        suggestedTimeRange: baseContext.suggestedTimeRange
    )
}

struct RetrievalContextWithFrames {
    let frames: [RetrievalFrame]
    let memoryFacts: [MemoryFact]
    let suggestedTimeRange: DateInterval?
}

Step 7: Test Integration

Test Checklist

  1. Intent Classification

    • Send "how do you work?" → Expect META_EXPLAIN intent
    • Send "find my note about AI" → Expect SEARCH_RECALL intent
    • Send "I need to exercise" → Expect CAPTURE_TASK intent
  2. Session State

    • Send message → Check state is updated
    • Check session state header in prompt
    • Verify question budget is enforced
  3. Retrieval Frames

    • Query with relevant notes → Expect structured frames
    • Check frame format in logs
    • Verify citations are attached
  4. Output Verification

    • Set question budget to 0 → Send message
    • Check that response contains no questions
    • If it does, verify regeneration happens
  5. Safe Mode

    • Trigger memory warning (iOS simulator: Debug → Simulate Memory Warning)
    • Verify Safe Mode activates
    • Check that token budgets are reduced
    • Verify Safe Mode indicator shows in UI
  6. Topic Graph

    • Create notes with topics (e.g., "AI", "fitness")
    • Query related topics: topicGraph.relatedTopics(to: "AI")
    • Verify co-occurrence tracking works
  7. Async Processing

    • Capture note → Verify sparkle indicator appears
    • Wait for completion → Verify toast shows
    • Check that note was processed (topics extracted, etc.)

Step 8: Performance Monitoring

Add logging to track Smart Brain performance:

// In SmartBrain.processChatMessage()

let startTime = Date()

// ... processing ...

let duration = Date().timeIntervalSince(startTime)
Diagnostics.shared.record(
    component: "SmartBrain",
    level: .info,
    message: "Message processed",
    metadata: [
        "duration_ms": "\(Int(duration * 1000))",
        "intent": intent.rawValue,
        "mode": safeModeMonitor.currentMode.rawValue,
        "retrieval_frames": "\(retrievalContext.frames.count)",
        "verification_passed": "\(verification.passed)"
    ]
)

Step 9: UI Polish

Add Intent Badge (Optional)

Show detected intent in chat UI:

// In message view
if let intent = message.intent {
    HStack(spacing: 4) {
        Image(systemName: intentIcon(for: intent))
            .font(.caption2)
        Text(intent.rawValue)
            .font(.caption2.weight(.medium))
    }
    .foregroundStyle(.secondary)
    .padding(.horizontal, 8)
    .padding(.vertical, 4)
    .background(Color.accentColor.opacity(0.08))
    .clipShape(Capsule())
}

private func intentIcon(for intent: UserIntent) -> String {
    switch intent {
    case .metaExplain: return "info.circle"
    case .captureNote: return "square.and.pencil"
    case .captureTask: return "checkmark.circle"
    case .searchRecall: return "magnifyingglass"
    case .connectNotes: return "link"
    case .planNextSteps: return "list.bullet"
    case .reflectJournal: return "moon.stars"
    case .summarizePeriod: return "calendar"
    case .editRewrite: return "pencil.line"
    case .settingsModel: return "gear"
    case .troubleshoot: return "exclamationmark.triangle"
    case .clarify: return "questionmark.circle"
    }
}

Step 10: Documentation

Update user-facing docs:

Help Screen

# How ThoughtOS AI Works

ThoughtOS uses a small, on-device AI model that's been carefully designed to feel intelligent through smart scaffolding.

## Key Features

- **Intent-aware responses** — The AI understands what you're trying to do
- **Memory continuity** — Your identity and preferences are always in context
- **Smart retrieval** — Surfaces relevant notes at the right time
- **Question control** — The AI provides answers, not endless questions
- **Safe Mode** — Automatically adjusts if your device is low on memory

## Safe Mode

If you see "Battery Saver" or "Safe Mode", ThoughtOS has automatically reduced AI capabilities to maintain stability. This is normal and temporary.

To restore full capabilities:
1. Close other apps
2. Wait a moment
3. Tap "Reset to Normal Mode" in Settings

Troubleshooting

SmartBrain not initialized

Symptom: "Smart Brain not initialized" error in chat

Fix:

// In AppServices
override init() {
    super.init()
    // ... other setup ...
    Task {
        await setupSmartBrain()
    }
}

Intent always CLARIFY

Symptom: Intent router always returns CLARIFY with low confidence

Fix: Check that hard rules are being evaluated first. Add logging:

// In IntentRouter.classify()
print("🔍 Classifying:", text.prefix(50))
print("✓ Matched intent:", intent.rawValue, "confidence:", confidence)

Retrieval frames empty

Symptom: No frames in retrieval context

Fix: Ensure notes have been processed and embeddings exist:

// Check vector store has embeddings
let count = try? await vectorStore.totalChunkCount()
print("📊 Total chunks in vector store:", count ?? 0)

Safe Mode stuck

Symptom: Safe Mode won't restore to Normal

Fix: Check memory warnings haven't reset:

services.safeModeMonitor.restoreNormalMode()

Next Steps

Once integration is complete:

  1. A/B Testing — Compare responses with/without Smart Brain
  2. User Feedback — Collect feedback on intent detection accuracy
  3. Performance Tuning — Adjust token budgets based on actual usage
  4. Edge Cases — Build test suite for edge cases (very long messages, etc.)
  5. Prompt Refinement — Iterate on system prompts based on real conversations

Resources