Skip to content

Conversation

@samschooler
Copy link
Contributor

@samschooler samschooler commented Sep 25, 2025

Description

Encapsulate the root agent in a class in order to have separation of concerns in the chat streaming route.

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Release
  • Refactor
  • Other (please describe):

Testing

Verified with QA + existing tests


Important

Refactor to encapsulate root agent functionality into BaseAgent, RootAgent, and AgentStreamer classes for improved separation of concerns in chat streaming.

  • Classes:
    • Add BaseAgent class in models/base.ts with methods for system prompt and toolset management.
    • Add RootAgent class in classes/root.ts extending BaseAgent, with methods for system prompt based on ChatType and model configuration.
    • Add AgentStreamer class in streamer.ts to handle message streaming using BaseAgent.
  • Refactor:
    • Refactor streamResponse in route.ts to use RootAgent and AgentStreamer for handling chat streaming.
  • Exports:
    • Update exports in index.ts files to include new classes and modules.

This description was created by Ellipsis for 8aeab47. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • Refactor

    • Redesigned streaming flow for chat responses to improve reliability and consistency across chat types.
    • Centralized agent initialization and unified prompt selection for more consistent behavior.
    • Integrated improved telemetry and error handling during live responses.
  • New Features

    • Conversation streams now include richer metadata (timestamps, usage, finish reasons) for clearer UX and tracking.

feat: Add streamer to manage agent streaming
@vercel
Copy link

vercel bot commented Sep 25, 2025

@samschooler is attempting to deploy a commit to the Onlook Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Sep 25, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Refactors chat streaming to use a RootAgent + AgentStreamer abstraction: adds BaseAgent, RootAgent, AgentStreamer, re-exports agents from package entrypoints, replaces direct streamText usage in the API route, and moves streaming configuration, telemetry, and error handling into the new agent-based flow.

Changes

Cohort / File(s) Summary of Changes
API route refactor
apps/web/client/src/app/api/chat/route.ts
Replaces direct streamText and model/config plumbing with RootAgent.create(...) and AgentStreamer.streamText(...); migrates telemetry, usage decrement, error handling, and UI-stream callbacks into the new stream config.
Agent base model
packages/ai/src/agents/models/base.ts
Adds abstract BaseAgent with id, modelConfig, systemPrompt, toolSet, default stream config, and a unified streamText that composes system prompt, tools, headers, model and stop logic.
Agent classes: RootAgent
packages/ai/src/agents/classes/root.ts
Adds exported RootAgent extending BaseAgent; selects model and toolset by ChatType, exposes systemPrompt, and provides create() + getModelFromType() for async initialization.
Agent streaming adapter
packages/ai/src/agents/streamer.ts
Adds AgentStreamer that invokes agent.streamText, converts results to UI message stream responses, generates UUIDs for messages, and attaches metadata (createdAt, conversationId, finishReason, usage).
Agents public API surfacing
packages/ai/src/agents/index.ts
New index re-exporting models, classes, and AgentStreamer for consolidated imports.
Index re-exports (classes/models)
packages/ai/src/agents/classes/index.ts, packages/ai/src/agents/models/index.ts
Re-exports added for RootAgent and BaseAgent.
Package entrypoint expansion
packages/ai/src/index.ts
Adds export * from './agents'; to surface agents at package root.
Dependency addition
packages/ai/package.json
Adds "uuid": "^11.1.0" dependency used by AgentStreamer for message IDs.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant API as /api/chat (route.ts)
  participant AgentClass as RootAgent
  participant Streamer as AgentStreamer
  participant Provider as LLM Provider

  Client->>API: POST chat messages
  API->>AgentClass: RootAgent.create(chatType)
  API->>Streamer: new AgentStreamer(agent, conversationId)
  API->>Streamer: streamText(messages, { streamTextConfig, toUI... })
  Streamer->>AgentClass: agent.streamText(streamTextConfig + messages)
  AgentClass->>Provider: stream with model, tools, systemPrompt
  Provider-->>AgentClass: streaming parts
  AgentClass-->>Streamer: stream result
  Streamer-->>API: UI message stream response (ids, metadata)
  API-->>Client: SSE/streamed response
  Note over API,Streamer: telemetry & error handlers triggered on error/finish
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I nibble code beneath the moon,
RootAgent hums a brand-new tune,
Streamer carries parts with glee,
UUID carrots, tokens free.
From burrowed base the messages hop—hip hooray! 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description includes clear sections for the change summary, type of change, and testing steps, but it omits the required “## Related Issues” section from the repository’s template that links or notes related issue numbers. Please add a “## Related Issues” section and reference any relevant issue numbers or explicitly state “None” if there are no related issues, ensuring compliance with the repository’s description template.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly identifies the primary change by highlighting the addition of the BaseAgent, RootAgent, and AgentStreamer classes, which aligns directly with the main content of the pull request and omits unrelated details.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8aeab47 and 57e4fa2.

📒 Files selected for processing (4)
  • apps/web/client/src/app/api/chat/route.ts (3 hunks)
  • packages/ai/package.json (1 hunks)
  • packages/ai/src/agents/classes/root.ts (1 hunks)
  • packages/ai/src/agents/models/base.ts (1 hunks)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@samschooler samschooler changed the title Add classes for BaseAgent and RootAgent Add classes for BaseAgent and RootAgent + add Agent Streamer Sep 25, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/client/src/app/api/chat/route.ts (1)

41-41: Await streamResponse so errors are caught by the surrounding try/catch.

Without await, thrown errors inside streamResponse won’t be caught by the POST handler’s catch block.

Apply this diff:

-        return streamResponse(req, user.id);
+        return await streamResponse(req, user.id);
🧹 Nitpick comments (6)
packages/ai/src/agents/streamer.ts (2)

24-33: Stabilize createdAt per streamed message

createdAt changes per part; set it once to keep a consistent timestamp for the message.

-        const result = await this.agent.streamText(convertToStreamMessages(messages), streamTextConfig);
+        const result = await this.agent.streamText(convertToStreamMessages(messages), streamTextConfig);
+        const createdAt = new Date();
@@
-            messageMetadata: ({ part }) => {
+            messageMetadata: ({ part }) => {
                 return {
-                    createdAt: new Date(),
+                    createdAt,
                     conversationId,
                     context: [],
                     checkpoints: [],
                     finishReason: part.type === 'finish-step' ? part.finishReason : undefined,
                     usage: part.type === 'finish-step' ? part.usage : undefined,
                 } satisfies ChatMetadata;
             },

5-5: Nit: remove unused import

UIMessageStreamOnFinishCallback isn’t used.

-import type { streamText, UIMessageStreamOnFinishCallback, UIMessageStreamOptions } from "ai";
+import type { streamText, UIMessageStreamOptions } from "ai";
packages/ai/src/agents/models/base.ts (1)

2-2: Nit: drop unused type import

Tool isn’t referenced in this file.

-import { type ModelMessage, type Tool, type ToolSet } from 'ai';
+import { type ModelMessage, type ToolSet } from 'ai';
apps/web/client/src/app/api/chat/route.ts (1)

3-5: Remove unused imports.

convertToStreamMessages and ChatMetadata are not used in this file.

Apply this diff:

-import { AgentStreamer, convertToStreamMessages, RootAgent } from '@onlook/ai';
+import { AgentStreamer, RootAgent } from '@onlook/ai';
...
-import { ChatType, type ChatMessage, type ChatMetadata } from '@onlook/models';
+import { ChatType, type ChatMessage } from '@onlook/models';
packages/ai/src/agents/classes/root.ts (2)

1-1: Remove unused type imports from 'ai'.

JSONValue, LanguageModel, ModelMessage, and ToolSet are not used in this file.

Apply this diff:

-import { type JSONValue, type LanguageModel, type ModelMessage, type ToolSet } from 'ai';

8-17: Optional: Mark id as override if BaseAgent declares it.

If BaseAgent defines id, use override readonly id = 'root-agent' for stricter typing.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a189467 and 8aeab47.

📒 Files selected for processing (8)
  • apps/web/client/src/app/api/chat/route.ts (3 hunks)
  • packages/ai/src/agents/classes/index.ts (1 hunks)
  • packages/ai/src/agents/classes/root.ts (1 hunks)
  • packages/ai/src/agents/index.ts (1 hunks)
  • packages/ai/src/agents/models/base.ts (1 hunks)
  • packages/ai/src/agents/models/index.ts (1 hunks)
  • packages/ai/src/agents/streamer.ts (1 hunks)
  • packages/ai/src/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • packages/ai/src/agents/index.ts
  • packages/ai/src/index.ts
  • packages/ai/src/agents/classes/index.ts
  • apps/web/client/src/app/api/chat/route.ts
  • packages/ai/src/agents/models/base.ts
  • packages/ai/src/agents/models/index.ts
  • packages/ai/src/agents/streamer.ts
  • packages/ai/src/agents/classes/root.ts
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • packages/ai/src/agents/index.ts
  • packages/ai/src/index.ts
  • packages/ai/src/agents/classes/index.ts
  • apps/web/client/src/app/api/chat/route.ts
  • packages/ai/src/agents/models/base.ts
  • packages/ai/src/agents/models/index.ts
  • packages/ai/src/agents/streamer.ts
  • packages/ai/src/agents/classes/root.ts
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/app/api/chat/route.ts
apps/web/client/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Files:

  • apps/web/client/src/app/api/chat/route.ts
🧠 Learnings (2)
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/server/api/root.ts : Export all tRPC routers from apps/web/client/src/server/api/root.ts

Applied to files:

  • packages/ai/src/agents/classes/index.ts
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/server/api/root.ts : Export all tRPC routers from src/server/api/root.ts

Applied to files:

  • packages/ai/src/agents/classes/index.ts
🧬 Code graph analysis (4)
apps/web/client/src/app/api/chat/route.ts (4)
packages/ai/src/agents/classes/root.ts (1)
  • RootAgent (7-57)
packages/ai/src/agents/streamer.ts (1)
  • AgentStreamer (7-37)
apps/web/client/src/app/api/chat/helpers/stream.ts (1)
  • repairToolCall (47-87)
apps/web/client/src/app/api/chat/helpers/usage.ts (1)
  • decrementUsage (67-89)
packages/ai/src/agents/models/base.ts (2)
packages/models/src/llm/index.ts (1)
  • ModelConfig (34-39)
packages/ai/src/agents/streamer.ts (1)
  • streamText (16-36)
packages/ai/src/agents/streamer.ts (3)
packages/models/src/chat/message/message.ts (2)
  • ChatMessage (18-18)
  • ChatMetadata (6-13)
packages/ai/src/agents/models/base.ts (1)
  • streamText (31-52)
packages/ai/src/stream/index.ts (1)
  • convertToStreamMessages (5-23)
packages/ai/src/agents/classes/root.ts (5)
packages/models/src/llm/index.ts (1)
  • ModelConfig (34-39)
packages/ai/src/tools/toolset.ts (1)
  • getToolSetFromType (66-68)
packages/ai/src/prompt/provider.ts (2)
  • getCreatePageSystemPrompt (29-33)
  • getAskModeSystemPrompt (41-45)
packages/ai/src/agents/models/base.ts (1)
  • getSystemPrompt (15-17)
packages/ai/src/chat/providers.ts (1)
  • initModel (14-51)
🔇 Additional comments (9)
packages/ai/src/agents/models/index.ts (1)

1-1: LGTM: clean public re‑export

Exposing BaseAgent via the models index makes sense and keeps imports tidy.

packages/ai/src/agents/classes/index.ts (1)

1-1: LGTM: targeted re‑export

Re-exporting RootAgent from the classes index keeps consumer imports clean.

packages/ai/src/index.ts (1)

1-1: Verify no export name collisions from new export-star

Adding export * from './agents' widens the public API and might collide with existing exports (types or values) from other modules already re-exported here. Please verify no duplicate export names and consider a minor version bump if API surface expands.

Run this read-only check to surface potential duplicate exported identifiers across packages/ai/src (heuristic; may include false positives due to re-exports):

packages/ai/src/agents/index.ts (1)

1-3: Public agents API re-export looks good.

Consolidated exports are clear and minimal. No issues spotted.

packages/ai/src/agents/classes/root.ts (3)

23-33: Confirm desired prompt for ChatType.FIX.

FIX is mapped to a CREATE/FIX model below but falls into the default system prompt path here. Verify whether FIX should use a specialized prompt (e.g., EDIT) for consistency.


35-38: Factory method is appropriate.

Asynchronous create neatly encapsulates model selection and agent construction.


40-55: OPEN_AI_GPT_5 model and token configuration verified
OPENROUTER_MODELS.OPEN_AI_GPT_5 is defined in the enum and MODEL_MAX_TOKENS has a 400 000 entry for it.

apps/web/client/src/app/api/chat/route.ts (2)

78-126: Import path stability confirmed: The @onlook/ai package’s top-level export re-exports both RootAgent and AgentStreamer, so the import path remains stable.


71-73: Runtime supports Array.prototype.findLast
package.json requires Node.js ≥18.0.0 and Array.prototype.findLast is available since Node 18.

@Kitenite Kitenite merged commit a6ff330 into onlook-dev:main Sep 25, 2025
1 of 4 checks passed
This was referenced Sep 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants