Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions aurelo_analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Aurelo AI Integration Analysis & Code Review

This document provides an analysis of the integration of the `aurelo.ai` provider as seen in PR #89 (PearAI-Roo-Code) and PR #309 (pearai-submodule).

## 1. How the Integration Works

Both pull requests integrate the Aurelo AI SDK (`aurelo.ai` via npm) to act as a custom LLM provider in their respective codebases.

### Architecture (`AureloSDK` Callback Pattern)
The core feature of the Aurelo SDK is its context management strategy, designed to optimize bandwidth and minimize repetitive payload transmission to the gateway.
- Instead of the client sending the entire conversational history (the full array of messages) on *every* request, the Aurelo SDK relies on two callbacks provided during instantiation:
1. `getFullContext()`: Called when a new session is created on the gateway, when an existing session expires, or when the gateway requires a "rotation" (e.g., the system prompt has fundamentally changed). This callback reconstructs and sends the entire message history up to the current point.
2. `getLatestInput()`: Called on subsequent (ongoing) turns in an active session. It only transmits the most recent user action (e.g., the newest message or tool result).
- The gateway caches the full conversation state, drastically reducing the payload size for back-and-forth chat.

### Implementation in PearAI-Roo-Code (PR #89)
- **File modified:** `src/api/providers/aurelo.ts`
- **Context Management:** The class `AureloHandler` intercepts Anthropic-style `MessageParam[]` messages. It saves `this.lastMessages` and tracks changes to `this.lastSystemPrompt`.
- **Rotation Trigger:** If the `systemPrompt` changes, it signals `rotateReason: "system_prompt_changed"` via metadata in `streamResponse`. The gateway will respond by demanding a full session rotation, triggering the `getFullContext` callback.
- **Content Mapping (`buildAureloContent`):**
- Text, `tool_use`, and `tool_result` blocks are flattened/grouped into plain `text` strings *unless* there is an image block present.
- If images (`image`) are present, it passes them native to the gateway so the gateway can dynamically convert them to standard vision payloads.

### Implementation in pearai-submodule (PR #309)
- **File modified:** `core/llm/llms/Aurelo.ts` (extending `BaseLLM`)
- **Context Management:** The class `Aurelo` intercepts Continue.dev-style `ChatMessage[]` messages. It updates `this.lastMessages`.
- **Content Mapping:**
- Messages are converted to arrays of parts `{ type: "text", text: ... }` or `{ type: "image_url", image_url: { url: ... } }`.
- Tool calls (`m.toolCalls`) from the `assistant` are intercepted. The arguments string is JSON-parsed into an object and appended as a `{ type: "tool_use" }` part.
- Tool results (messages with `role: "tool"`) are converted into a message with `role: "user"` containing a part `{ type: "tool_result" }`.

---

## 2. Code Review & Architectural Feedback

While the integration correctly utilizes the Aurelo SDK pattern, there are several architectural issues and potential bugs across both implementations.

### Concurrency and State Management (Critical)
Both implementations rely on instance variables to store the current conversation state (`this.lastMessages` and `this.lastSystemPrompt`):
```typescript
// in PR 89
this.lastMessages = messages;
// in PR 309
this.lastMessages = messages;
```
Because the `getFullContext` and `getLatestInput` callbacks execute asynchronously (and potentially much later, driven by SDK events), storing the messages globally on the handler instance creates a critical race condition.
- **The Bug:** If two requests (e.g., two different chat tabs, or a background task vs. foreground chat) stream simultaneously using the same provider instance, `this.lastMessages` will be overwritten by the second request. When the SDK triggers `getFullContext` or `getLatestInput` for the *first* request, it will incorrectly read the messages from the *second* request.
- **The Fix:** The SDK should ideally allow passing context or state variables per-request rather than relying on class-level instance variables. Alternatively, the handlers should track state mapped by a unique request/session ID.

### Tool Call JSON Parsing (PR #309)
In `pearai-submodule`'s implementation, the tool arguments parsing uses an empty `catch` block:
```typescript
let inputArgs = {};
try { inputArgs = JSON.parse(tc.function?.arguments || "{}"); } catch {}
```
- **The Bug:** If the LLM generates malformed JSON for a tool call (which happens frequently), the error is silently swallowed, and `inputArgs` becomes an empty object `{}`. The gateway or the local tool executor will then receive an empty payload, failing ambiguously without clear logs.
- **The Fix:** The catch block should at minimum log the error or gracefully handle the string representation, so the failure can be traced.

### Inconsistent Payload Flattening (PR #89 vs PR #309)
- **PR 89 (Roo-Code):** Tool usage blocks are stringified into raw text (`[Tool Call: name] ...`) if no images are present in the payload. This means the gateway receives text instead of strict `tool_use` JSON objects. If the gateway relies on strict schema matching to forward tool calls, this stringification will break the integration.
- **PR 309 (pearai-submodule):** Tool calls are properly mapped to `{ type: "tool_use", ... }` objects.

### Hardcoded URLs and Fallbacks
In PR #309:
```typescript
baseUrl: options.apiBase ?? "https://aurelo.tech",
```
If the user explicitly sets an empty string for `apiBase`, this will evaluate to `""` instead of the fallback because `??` only checks for `null` or `undefined`. It would be safer to use `options.apiBase || "https://aurelo.tech"`.

### Missing Event Handlers for Usage Metrics
In PR #309's `_streamChat` method (partially visible in diff), the `usage` object is initialized but the parsing of usage chunks from the Aurelo Stream Event seems incomplete or disconnected from Continue.dev's standard metrics reporting. PR #89 handles this better via `parseUsageObject`.

---

## 3. Summary
The integration successfully implements Aurelo's bandwidth-saving caching pattern. However, to ensure stability in production, the developers must fix the concurrency/race-condition issue with `this.lastMessages`, improve JSON error handling for tool calls, and ensure tool payloads are not arbitrarily converted to plain text in the Roo-Code implementation.