Skip to content

refactor: remove legacy message transformation pipeline#11451

Open
roomote[bot] wants to merge 12 commits intomainfrom
feature/remove-legacy-message-transforms
Open

refactor: remove legacy message transformation pipeline#11451
roomote[bot] wants to merge 12 commits intomainfrom
feature/remove-legacy-message-transforms

Conversation

@roomote
Copy link
Contributor

@roomote roomote bot commented Feb 13, 2026

Summary

Removes the legacy message transformation pipeline that predates the AI SDK migration. The AI SDK now handles all provider-specific message format conversion natively.

What changed

Removed buildCleanConversationHistory from Task.ts

This method was effectively a no-op because:

  • preserveReasoning was always true (all real providers return true from isAiSdkProvider())
  • Native format messages passed through unchanged
  • Legacy format conversion was already handled at the persistence layer by convertAnthropicToRooMessages()

Standardized all providers on sanitizeMessagesForProvider()

5 providers had ad-hoc or missing message sanitization:

  • xai: raw pass-through (no sanitization at all)
  • gemini: own reasoning filter, no field allowlist
  • bedrock: own reasoning filter, no field allowlist
  • openai-native: custom reasoning dance without base sanitization
  • openai-codex: same as openai-native

All now use sanitizeMessagesForProvider() which allowlists role/content/providerOptions and filters RooReasoningMessage items.

Deleted dead transform files (zero production callers)

  • anthropic-filter.ts - filterNonAnthropicBlocks()
  • r1-format.ts - convertToR1Format()
  • openai-format.ts - convertToOpenAiMessages(), sanitizeGeminiMessages(), consolidateReasoningDetails()
  • mistral-format.ts - convertToMistralMessages(), normalizeMistralToolCallId()

Removed convertToAiSdkMessages() from ai-sdk.ts

Imported by ~20 providers, called by zero. All providers use sanitizeMessagesForProvider() instead.

Removed unused imports

Cleaned up convertToAiSdkMessages imports from all 25 provider files.

Stats

  • 37 files changed
  • ~58 insertions, ~5,116 deletions

Testing

  • TypeScript compilation: all 14 packages pass
  • Linting: all 14 packages pass
  • Unit tests: ai-sdk.spec.ts (68 tests), openai-native-reasoning.spec.ts + sanitize-messages.spec.ts (29 tests) all pass

@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Feb 13, 2026
@roomote
Copy link
Contributor Author

roomote bot commented Feb 13, 2026

Rooviewer Clock   See task

Reviewed 0bd02ae. The error-wrapping removal is clean -- providers now throw raw AI SDK errors, and Task.ts uses extractAiSdkErrorMessage + APICallError.isInstance/RetryError.isInstance to properly extract messages and status codes. The new checkIsAiSdkContextWindowError, findDeepestApiCallError, and OpenRouter/Ollama stream-error-to-usage-error propagation all look correct. Two nits remain.

  • Stale JSDoc @link: src/core/task-persistence/converters/anthropicToRoo.ts line 7 references convertToAiSdkMessages which was deleted in this PR. Consider updating the comment.
  • Dead responseId variable: In src/core/task/Task.ts, const responseId = handler.getResponseId?.() (line 1072) is unused after both spreads were removed. The getResponseId type cast (line 1067) and the comment on line 1092 are also stale.
  • Indentation drift in backoffAndAnnounce: src/core/task/Task.ts lines 4589-4647 are indented one tab deeper than the surrounding method body. Cosmetic only but affects ~60 lines.
  • Dead handleAiSdkError export: src/api/transform/ai-sdk.ts lines 622-687 still export handleAiSdkError and HandleAiSdkErrorOptions, but no production code imports them after this commit removed all callers. Related mocks in anthropic.spec.ts and anthropic-vertex.spec.ts are also stale.
Previous reviews

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

@roomote
Copy link
Contributor Author

roomote bot commented Feb 13, 2026

Fixed in 90dc763 -- updated the stale JSDoc @link in anthropicToRoo.ts to remove the reference to the deleted convertToAiSdkMessages function.

this.apiConversationHistory.push({
...message,
...(responseId ? { id: responseId } : {}),
ts: message.ts ?? Date.now(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: with both responseId spreads removed, const responseId = handler.getResponseId?.() (line 1072) is now unused, and the getResponseId entry in the handler type cast (line 1067) is also dead. The comment on line 1092 ("with responseId and timestamp") is stale too. Minor cleanup left behind by this refactoring.

Fix it with Roo Code or mention @roomote and request a fix.

roomote and others added 8 commits February 13, 2026 08:24
- Remove buildCleanConversationHistory from Task.ts (effectively a no-op
  since all providers are AI SDK providers and preserveReasoning is always true)
- Standardize all providers on sanitizeMessagesForProvider for message
  sanitization (xai, gemini, bedrock, openai-native, openai-codex)
- Delete dead transform files with zero production callers:
  anthropic-filter.ts, r1-format.ts, openai-format.ts, mistral-format.ts
- Remove convertToAiSdkMessages function and its unused imports from ~20
  providers (imported by all, called by none)
- Remove reasoning-preservation.test.ts (tested removed code)
- Clean up stale comments referencing removed functions
- Backward compatibility with old conversation histories is already handled
  by convertAnthropicToRooMessages at the persistence layer
…ng messages

Remove the provider response ID (uid=502(hrudolph) gid=20(staff) groups=20(staff),101(access_bpf),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),333(piavpn),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae)) field from RooAssistantMessage type
and stop propagating it through anthropicToRoo converter and Task.ts.
Update corresponding tests to no longer assert on the removed field.
Parse error.metadata.raw (OpenRouter/provider wrapper format) as the
primary error source. Remove silent fallback to generic statusText
('Bad Request'). When structured extraction fails, surface the raw
responseBody instead of swallowing it. Add 17 tests covering all
error format paths.
…null

When NativeToolCallParser.parseToolCall() returns null in the
'tool_call' handler, push a fallback ToolUse block to
assistantMessageContent instead of silently breaking. This ensures
the didToolUse check at the end of the request sees a tool_use block,
preventing the false '[ERROR] You did not use a tool' injection.
The fallback block flows into presentAssistantMessage()'s existing
unknown-tool handler which reports the actual error to the model.
Add 4 tests covering the fallback behavior.
@hannesrudolph hannesrudolph force-pushed the feature/remove-legacy-message-transforms branch from b04c931 to 21172d5 Compare February 13, 2026 15:29
…oints to 1

- Move applyCacheBreakpoints() from Task.ts into each provider's createMessage()
  (anthropic, bedrock, openrouter, requesty, anthropic-vertex, roo)
- Aligns with AI SDK best practice: cache control is a call-site concern
- Remove stripCacheProviderOptions() from saveRooMessages() save path
  (no longer needed since cache markers don't contaminate persistent history)
- Change maxBreakpoints default from 2 to 1 (3-point strategy: 1 system, 1 tool, 1 message)
…ssages

- Remove handleAiSdkError() wrapping from all 24 provider files — typed AI SDK
  errors (APICallError, RetryError, TooManyRequestsError) now flow through directly
- Update Task.ts to use APICallError.isInstance/RetryError.isInstance for typed
  error handling, extractAiSdkErrorMessage() for user display
- Add findDeepestApiCallError() for recursive .cause/.lastError/.errors[] traversal
- Fix processAiSdkStreamPart error case to use extractAiSdkErrorMessage instead of
  flattening to error.message (was losing responseBody with actual error details)
- Fix extractMessageFromResponseBody to handle OpenRouter metadata.raw with nested
  Anthropic error format (rawObj.error.message)
- Add checkIsAiSdkContextWindowError() for structured context window detection
- Update backoffAndAnnounce() to read .statusCode from APICallError natively
- Fix 25 provider error tests, add 8 new error extraction tests
- Remove dead handleOpenAIError export from error-handler.ts
- Add lastStreamError recovery to OpenRouter and native-ollama providers
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels Feb 13, 2026
Comment on lines +4589 to 4647
const statusCode = APICallError.isInstance(error)
? error.statusCode
: RetryError.isInstance(error) && APICallError.isInstance(error.lastError)
? error.lastError.statusCode
: (error as any)?.status

// Prefer RetryInfo on 429 if present
if (statusCode === 429) {
// Try direct errorDetails (legacy Vertex), then try parsing from responseBody
let retryDelaySec: number | undefined
const errorDetails = (error as any)?.errorDetails
if (errorDetails) {
const retryInfo = errorDetails.find(
(d: any) => d["@type"] === "type.googleapis.com/google.rpc.RetryInfo",
)
const match = retryInfo?.retryDelay?.match?.(/^(\d+)s$/)
if (match) {
retryDelaySec = Number(match[1]) + 1
}
}
// Also try extracting from APICallError responseBody for Vertex errors
if (!retryDelaySec) {
const responseBody = APICallError.isInstance(error)
? error.responseBody
: RetryError.isInstance(error) && APICallError.isInstance(error.lastError)
? error.lastError.responseBody
: undefined
if (responseBody) {
try {
const parsed = JSON.parse(responseBody)
const retryInfo = parsed?.error?.details?.find(
(d: any) => d["@type"] === "type.googleapis.com/google.rpc.RetryInfo",
)
const match = retryInfo?.retryDelay?.match?.(/^(\d+)s$/)
if (match) {
retryDelaySec = Number(match[1]) + 1
}
} catch {
// responseBody not parseable, skip
}
}
}
if (retryDelaySec) {
exponentialDelay = retryDelaySec
}
}

const finalDelay = Math.max(exponentialDelay, rateLimitDelay)
if (finalDelay <= 0) {
return
}

// Build header text; fall back to error message if none provided
let headerText: string
if (statusCode) {
headerText = `${statusCode}\n${extractAiSdkErrorMessage(error)}`
} else {
headerText = extractAiSdkErrorMessage(error)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This new block (lines 4589-4647) is indented one tab level deeper than the surrounding code in backoffAndAnnounce. It looks like the replacement was pasted at the wrong indent depth. The logic is correct but ~60 lines sitting at 4 tabs instead of 3 makes the method harder to scan.

Fix it with Roo Code or mention @roomote and request a fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants