Add Ditto harness provider integration - #1
Conversation
📝 WalkthroughWalkthroughIntroduces Ditto as a new local AI provider backed by a native Node.js binding ("Ditto Harness"). The change adds Effect-based schemas, an RPC surface, a native runtime loader, an Effect service layer, a full provider driver/adapter stack, text generation, WebSocket RPC handlers, server layer wiring, and a web settings UI for configuration and status. ChangesDitto Local AI Provider Integration
Sequence Diagram(s)sequenceDiagram
participant WebApp as Web App
participant LocalApi as localApi.ts
participant WsRpcClient as WsRpcClient
participant WsHandler as ws.ts
participant Service as DittoHarnessService
participant Runtime as DittoHarnessRuntime
participant Native as NativeHarness
rect rgba(30, 100, 160, 0.5)
note over WebApp,Native: Status check
WebApp->>LocalApi: getDittoHarnessStatus()
LocalApi->>WsRpcClient: server.getDittoHarnessStatus()
WsRpcClient->>WsHandler: transport.request(dittoHarnessStatus, {})
WsHandler->>Service: status
Service->>Runtime: opener(settings)
Runtime->>Native: harness.open(dbPath)
Native-->>Runtime: OpenHarness
Runtime-->>Service: status result
Service-->>WsHandler: DittoHarnessStatus
WsHandler-->>WebApp: DittoHarnessStatus
end
rect rgba(160, 80, 30, 0.5)
note over WebApp,Native: Turn with prompt context
WebApp->>WsHandler: sendTurn(prompt)
WsHandler->>Service: buildPromptContext(input)
Service->>Native: searchMemories(query)
Native-->>Service: memories[]
Service-->>WsHandler: contextString
WsHandler->>Native: harness.chat(contextString + prompt)
Native-->>WsHandler: DittoHarnessChatTurnResult
WsHandler-->>WebApp: runtime events (content delta, turn complete)
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/server/src/dittoHarness/DittoHarnessRuntime.ts`:
- Around line 177-187: The cacheKey function is missing the chat and action
model settings from its serialized key, causing cache hits even when these
settings change. Add the chat* and action* settings fields from input.settings
to the JSON object being stringified in the cacheKey function to ensure that
different model configurations produce different cache keys and prevent stale
settings from being reused.
In `@apps/server/src/provider/Layers/DittoAdapter.ts`:
- Around line 343-345: The turnId generation in DittoAdapter.ts at lines 343-345
is based on state.turnCount, which gets reset during rollback operations,
causing turn IDs to be reused. Since the interruptedTurnIds set persists across
rollbacks, reused turn IDs can be falsely marked as interrupted in later turns.
Replace the turnId generation mechanism to use a monotonically increasing
counter that does not get reset during rollbacks (rather than relying on
state.turnCount which is reset to snapshot length). This same fix must be
applied at the two other affected locations in DittoAdapter.ts at lines 460 and
545 where turn IDs are similarly generated using state.turnCount.
- Around line 354-381: The issue is that if options.opener(settings) fails after
turn.started and item.started events have been emitted, the code exits without
properly finalizing the turn state, leaving the session in a running state with
an active turn and no terminal events. To fix this, add error handling after the
options.opener(settings) call that catches the ProviderAdapterRequestError and
ensures failTurn is invoked before the error propagates, so the turn lifecycle
is properly closed and session state is correctly finalized when the opener
fails.
In `@apps/server/src/provider/Layers/DittoProvider.ts`:
- Around line 88-93: The modelOptions function checks if
trimToUndefined(settings.chatModel) and trimToUndefined(settings.chatBaseUrl)
exist as a presence check, but then passes the raw untrimmed values from
settings.chatModel and settings.chatBaseUrl to the returned object. This causes
leading/trailing whitespace to survive and potentially break URL or model
resolution. Fix this by using the trimmed values returned from trimToUndefined()
directly in the model and baseUrl properties instead of the raw settings values,
so that whitespace is properly removed before being passed to the harness.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: fdb1caca-f72d-48bd-9df0-96ca98ee6af7
📒 Files selected for processing (40)
apps/server/integration/OrchestrationEngineHarness.integration.tsapps/server/src/dittoHarness/DittoHarnessRuntime.test.tsapps/server/src/dittoHarness/DittoHarnessRuntime.tsapps/server/src/dittoHarness/DittoHarnessService.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.test.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.tsapps/server/src/provider/Drivers/DittoDriver.tsapps/server/src/provider/Layers/DittoAdapter.tsapps/server/src/provider/Layers/DittoProvider.tsapps/server/src/provider/Layers/ProviderRegistry.test.tsapps/server/src/provider/builtInDrivers.tsapps/server/src/server.test.tsapps/server/src/server.tsapps/server/src/textGeneration/DittoTextGeneration.tsapps/server/src/textGeneration/TextGeneration.tsapps/server/src/ws.tsapps/web/src/components/Icons.tsxapps/web/src/components/KeybindingsToast.browser.tsxapps/web/src/components/chat/providerIconUtils.tsapps/web/src/components/settings/ProviderModelsSection.tsxapps/web/src/components/settings/ProviderSettingsForm.test.tsapps/web/src/components/settings/ProviderSettingsForm.tsxapps/web/src/components/settings/SettingsPanels.tsxapps/web/src/components/settings/providerDriverMeta.tsapps/web/src/environments/runtime/connection.test.tsapps/web/src/environments/runtime/service.addSavedEnvironment.test.tsapps/web/src/environments/runtime/service.savedEnvironments.test.tsapps/web/src/environments/runtime/service.threadSubscriptions.test.tsapps/web/src/localApi.test.tsapps/web/src/localApi.tsapps/web/src/modelSelection.tsapps/web/src/session-logic.tspackages/client-runtime/src/wsRpcClient.tspackages/contracts/src/dittoHarness.tspackages/contracts/src/index.tspackages/contracts/src/ipc.tspackages/contracts/src/model.tspackages/contracts/src/rpc.tspackages/contracts/src/settings.test.tspackages/contracts/src/settings.ts
0b36fde to
2f57981
Compare
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
2f57981 to
9c0dd9a
Compare
9c0dd9a to
d6d1a9c
Compare
d6d1a9c to
20c8881
Compare
Summary
mainwithout restoring removed runtime or local-API abstractionsBehavior
Validation
vp check(passes; repository has pre-existing warnings)vp run typecheckvp test run apps/server/src/dittoHarness/DittoHarnessRuntime.test.ts packages/contracts/src/settings.test.ts(60 tests)Stack
Base layer of stack pingdotgg#4. Merge this before the universal-chat contract and adapter layers.