Repository: assistant-ui/assistant-ui
Pull request #1911: refactor: tighten internal types and eslint fixes
Author: login: Yonom
Created: 2025-04-28T10:35:21Z | Merged: 2025-04-28T10:35:44Z


<!-- ELLIPSIS_HIDDEN -->



> [!IMPORTANT]
> Refactor to enhance type safety and add ESLint rule for unused variables.
> 
>   - **Type Refinements**:
>     - Tighten types in `ToolCallReader.ts`, `ToolExecutionStream.ts`, `ToolResponse.ts`, `tool-types.ts`, and `toolResultStream.ts` to use `ReadonlyJSONObject` and `ReadonlyJSONValue`.
>     - Update `ToolCallArgsReader`, `ToolCallReader`, and `ToolResponse` to enforce stricter type constraints.
>   - **ESLint Configuration**:
>     - Add `@typescript-eslint/no-unused-vars` rule in `index.ts` to error on unused variables, ignoring rest siblings.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=assistant-ui%2Fassistant-ui&utm_source=github&utm_medium=referral)<sup> for 0b6f52f6854c01d2e31e765dfcfcb20581b9aef7. You can [customize](https://app.ellipsis.dev/assistant-ui/settings/summaries) this summary. It will automatically update as commits are pushed.</sup>

<!-- ELLIPSIS_HIDDEN -->


Diff:
diff --git a/packages/assistant-stream/src/core/tool/ToolCallReader.ts b/packages/assistant-stream/src/core/tool/ToolCallReader.ts
index ba01c835dc..f2e873e75a 100644
--- a/packages/assistant-stream/src/core/tool/ToolCallReader.ts
+++ b/packages/assistant-stream/src/core/tool/ToolCallReader.ts
@@ -8,18 +8,24 @@ import {
   ToolCallReader,
   ToolCallResponseReader,
 } from "./tool-types";
-import { TypeAtPath, TypePath } from "./type-path-utils";
+import { DeepPartial, TypeAtPath, TypePath } from "./type-path-utils";
 import { ToolResponse } from "./ToolResponse";
+import {
+  asAsyncIterableStream,
+  AsyncIterableStream,
+  ReadonlyJSONObject,
+  ReadonlyJSONValue,
+} from "../../utils";
 
 // TODO: remove dispose
 
-function getField<T>(obj: T, fieldPath: (string | number)[]): any {
-  let current: any = obj;
+function getField<T>(obj: T, fieldPath: (string | number)[]): unknown {
+  let current: unknown = obj;
   for (const key of fieldPath) {
     if (current === undefined || current === null) {
       return undefined;
     }
-    current = current[key as string | number];
+    current = current[key as keyof typeof current];
   }
   return current;
 }
@@ -29,14 +35,14 @@ interface Handle {
   dispose(): void;
 }
 
-class GetHandle<T> implements Handle {
-  private resolve: (value: any) => void;
+class GetHandle<T, TValue> implements Handle {
+  private resolve: (value: TValue) => void;
   private reject: (reason: unknown) => void;
   private disposed = false;
   private fieldPath: (string | number)[];
 
   constructor(
-    resolve: (value: any) => void,
+    resolve: (value: TValue) => void,
     reject: (reason: unknown) => void,
     fieldPath: (string | number)[],
   ) {
@@ -58,7 +64,7 @@ class GetHandle<T> implements Handle {
       ) {
         const value = getField(args as T, this.fieldPath);
         if (value !== undefined) {
-          this.resolve(value);
+          this.resolve(value as TValue);
           this.dispose();
         }
       }
@@ -74,12 +80,12 @@ class GetHandle<T> implements Handle {
 }
 
 class StreamValuesHandle<T> implements Handle {
-  private controller: ReadableStreamDefaultController<any>;
+  private controller: ReadableStreamDefaultController<unknown>;
   private disposed = false;
   private fieldPath: (string | number)[];
 
   constructor(
-    controller: ReadableStreamDefaultController<any>,
+    controller: ReadableStreamDefaultController<unknown>,
     fieldPath: (string | number)[],
   ) {
     this.controller = controller;
@@ -118,13 +124,13 @@ class StreamValuesHandle<T> implements Handle {
 }
 
 class StreamTextHandle<T> implements Handle {
-  private controller: ReadableStreamDefaultController<any>;
+  private controller: ReadableStreamDefaultController<unknown>;
   private disposed = false;
   private fieldPath: (string | number)[];
-  private lastValue: any = undefined;
+  private lastValue: string | undefined = undefined;
 
   constructor(
-    controller: ReadableStreamDefaultController<any>,
+    controller: ReadableStreamDefaultController<unknown>,
     fieldPath: (string | number)[],
   ) {
     this.controller = controller;
@@ -165,13 +171,13 @@ class StreamTextHandle<T> implements Handle {
 }
 
 class ForEachHandle<T> implements Handle {
-  private controller: ReadableStreamDefaultController<any>;
+  private controller: ReadableStreamDefaultController<unknown>;
   private disposed = false;
   private fieldPath: (string | number)[];
   private processedIndexes = new Set<number>();
 
   constructor(
-    controller: ReadableStreamDefaultController<any>,
+    controller: ReadableStreamDefaultController<unknown>,
     fieldPath: (string | number)[],
   ) {
     this.controller = controller;
@@ -182,7 +188,7 @@ class ForEachHandle<T> implements Handle {
     if (this.disposed) return;
 
     try {
-      const array = getField(args as T, this.fieldPath) as unknown as any[];
+      const array = getField(args as T, this.fieldPath);
 
       if (!Array.isArray(array)) {
         return;
@@ -226,10 +232,12 @@ class ForEachHandle<T> implements Handle {
 }
 
 // Implementation of ToolCallReader that uses stream of partial JSON
-export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
+export class ToolCallArgsReaderImpl<T extends ReadonlyJSONObject>
+  implements ToolCallArgsReader<T>
+{
   private argTextDeltas: ReadableStream<string>;
   private handles: Set<Handle> = new Set();
-  private args: any = parsePartialJsonObject("");
+  private args: unknown = parsePartialJsonObject("");
 
   constructor(argTextDeltas: ReadableStream<string>) {
     this.argTextDeltas = argTextDeltas;
@@ -268,8 +276,12 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
   get<PathT extends TypePath<T>>(
     ...fieldPath: PathT
   ): Promise<TypeAtPath<T, PathT>> {
-    return new Promise<any>((resolve, reject) => {
-      const handle = new GetHandle<T>(resolve, reject, fieldPath);
+    return new Promise<TypeAtPath<T, PathT>>((resolve, reject) => {
+      const handle = new GetHandle<T, TypeAtPath<T, PathT>>(
+        resolve,
+        reject,
+        fieldPath,
+      );
 
       // Check if the field is already complete in current args
       if (
@@ -281,7 +293,7 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
       ) {
         const value = getField(this.args as T, fieldPath);
         if (value !== undefined) {
-          resolve(value);
+          resolve(value as TypeAtPath<T, PathT>);
           return;
         }
       }
@@ -291,11 +303,13 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
     });
   }
 
-  streamValues<PathT extends TypePath<T>>(...fieldPath: PathT): any {
+  streamValues<PathT extends TypePath<T>>(
+    ...fieldPath: PathT
+  ): AsyncIterableStream<DeepPartial<TypeAtPath<T, PathT>>> {
     // Use a type assertion to convert the complex TypePath to a simple array
     const simplePath = fieldPath as unknown as (string | number)[];
 
-    const stream = new ReadableStream<any>({
+    const stream = new ReadableStream<DeepPartial<TypeAtPath<T, PathT>>>({
       start: (controller) => {
         const handle = new StreamValuesHandle<T>(controller, simplePath);
         this.handles.add(handle);
@@ -315,15 +329,19 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
       },
     });
 
-    // For type compatibility, cast the stream to the required type
-    return stream as any;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    return asAsyncIterableStream(stream) as any;
   }
 
-  streamText<PathT extends TypePath<T>>(...fieldPath: PathT): any {
+  streamText<PathT extends TypePath<T>>(
+    ...fieldPath: PathT
+  ): TypeAtPath<T, PathT> extends string & infer U
+    ? AsyncIterableStream<U>
+    : never {
     // Use a type assertion to convert the complex TypePath to a simple array
     const simplePath = fieldPath as unknown as (string | number)[];
 
-    const stream = new ReadableStream<any>({
+    const stream = new ReadableStream<unknown>({
       start: (controller) => {
         const handle = new StreamTextHandle<T>(controller, simplePath);
         this.handles.add(handle);
@@ -343,15 +361,19 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
       },
     });
 
-    // For type compatibility, cast the stream to the required type
-    return stream as any;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    return asAsyncIterableStream(stream) as any;
   }
 
-  forEach<PathT extends TypePath<T>>(...fieldPath: PathT): any {
+  forEach<PathT extends TypePath<T>>(
+    ...fieldPath: PathT
+  ): TypeAtPath<T, PathT> extends Array<infer U>
+    ? AsyncIterableStream<U>
+    : never {
     // Use a type assertion to convert the complex TypePath to a simple array
     const simplePath = fieldPath as unknown as (string | number)[];
 
-    const stream = new ReadableStream<any>({
+    const stream = new ReadableStream<unknown>({
       start: (controller) => {
         const handle = new ForEachHandle<T>(controller, simplePath);
         this.handles.add(handle);
@@ -371,12 +393,12 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
       },
     });
 
-    // For type compatibility, cast the stream to the required type
-    return stream as any;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    return asAsyncIterableStream(stream) as any;
   }
 }
 
-export class ToolCallResponseReaderImpl<TResult>
+export class ToolCallResponseReaderImpl<TResult extends ReadonlyJSONValue>
   implements ToolCallResponseReader<TResult>
 {
   constructor(private readonly promise: Promise<ToolResponse<TResult>>) {}
@@ -386,8 +408,10 @@ export class ToolCallResponseReaderImpl<TResult>
   }
 }
 
-export class ToolCallReaderImpl<TArgs, TResult>
-  implements ToolCallReader<TArgs, TResult>
+export class ToolCallReaderImpl<
+  TArgs extends ReadonlyJSONObject,
+  TResult extends ReadonlyJSONValue,
+> implements ToolCallReader<TArgs, TResult>
 {
   public readonly args: ToolCallArgsReaderImpl<TArgs>;
   public readonly response: ToolCallResponseReaderImpl<TResult>;
diff --git a/packages/assistant-stream/src/core/tool/ToolExecutionStream.ts b/packages/assistant-stream/src/core/tool/ToolExecutionStream.ts
index 3c7457b149..51d653c7dd 100644
--- a/packages/assistant-stream/src/core/tool/ToolExecutionStream.ts
+++ b/packages/assistant-stream/src/core/tool/ToolExecutionStream.ts
@@ -5,7 +5,10 @@ import {
   AssistantMetaTransformStream,
 } from "../utils/stream/AssistantMetaTransformStream";
 import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream";
-import { ReadonlyJSONValue } from "../../utils/json/json-value";
+import {
+  ReadonlyJSONObject,
+  ReadonlyJSONValue,
+} from "../../utils/json/json-value";
 import { ToolResponse } from "./ToolResponse";
 import { withPromiseOrValue } from "../utils/withPromiseOrValue";
 import { ToolCallReaderImpl } from "./ToolCallReader";
@@ -14,13 +17,16 @@ import { ToolCallReader } from "./tool-types";
 type ToolCallback = (toolCall: {
   toolCallId: string;
   toolName: string;
-  args: unknown;
+  args: ReadonlyJSONObject;
 }) =>
   | Promise<ToolResponse<ReadonlyJSONValue>>
   | ToolResponse<ReadonlyJSONValue>
   | undefined;
 
-type ToolStreamCallback = <TArgs, TResult>(toolCall: {
+type ToolStreamCallback = <
+  TArgs extends ReadonlyJSONObject = ReadonlyJSONObject,
+  TResult extends ReadonlyJSONValue = ReadonlyJSONValue,
+>(toolCall: {
   reader: ToolCallReader<TArgs, TResult>;
   toolCallId: string;
   toolName: string;
@@ -39,7 +45,7 @@ export class ToolExecutionStream extends PipeableTransformStream<
     const toolCallPromises = new Map<string, PromiseLike<void>>();
     const toolCallControllers = new Map<
       string,
-      ToolCallReaderImpl<unknown, unknown>
+      ToolCallReaderImpl<ReadonlyJSONObject, ReadonlyJSONValue>
     >();
 
     super((readable) => {
@@ -58,7 +64,10 @@ export class ToolExecutionStream extends PipeableTransformStream<
           switch (type) {
             case "part-start":
               if (chunk.part.type === "tool-call") {
-                const reader = new ToolCallReaderImpl<unknown, unknown>();
+                const reader = new ToolCallReaderImpl<
+                  ReadonlyJSONObject,
+                  ReadonlyJSONValue
+                >();
                 toolCallControllers.set(chunk.part.toolCallId, reader);
 
                 options.streamCall({
diff --git a/packages/assistant-stream/src/core/tool/ToolResponse.ts b/packages/assistant-stream/src/core/tool/ToolResponse.ts
index 7a8b68c42b..06cde0fca8 100644
--- a/packages/assistant-stream/src/core/tool/ToolResponse.ts
+++ b/packages/assistant-stream/src/core/tool/ToolResponse.ts
@@ -23,7 +23,9 @@ export class ToolResponse<TResult> {
     this.isError = options.isError ?? false;
   }
 
-  static [Symbol.hasInstance](obj: unknown): obj is ToolResponse<unknown> {
+  static [Symbol.hasInstance](
+    obj: unknown,
+  ): obj is ToolResponse<ReadonlyJSONValue> {
     return (
       typeof obj === "object" && obj !== null && TOOL_RESPONSE_SYMBOL in obj
     );
diff --git a/packages/assistant-stream/src/core/tool/tool-types.ts b/packages/assistant-stream/src/core/tool/tool-types.ts
index 66a199cfc7..b007aeb5cc 100644
--- a/packages/assistant-stream/src/core/tool/tool-types.ts
+++ b/packages/assistant-stream/src/core/tool/tool-types.ts
@@ -1,6 +1,5 @@
 import { JSONSchema7 } from "json-schema";
-import { TypeAtPath, TypePath } from "./type-path-utils";
-import { DeepPartial } from "ai";
+import { DeepPartial, TypeAtPath, TypePath } from "./type-path-utils";
 import { AsyncIterableStream } from "../../utils";
 import type { StandardSchemaV1 } from "@standard-schema/spec";
 import { ToolResponse } from "./ToolResponse";
@@ -13,7 +12,7 @@ import { ToolResponse } from "./ToolResponse";
  *
  * @template TArgs The type of arguments being read.
  */
-export interface ToolCallArgsReader<TArgs> {
+export interface ToolCallArgsReader<TArgs extends Record<string, unknown>> {
   /**
    * Returns a promise that will resolve to the value at the given path,
    * as soon as that path is generated by the LLM.
@@ -63,7 +62,10 @@ export interface ToolCallResponseReader<TResult> {
   get: () => Promise<ToolResponse<TResult>>;
 }
 
-export interface ToolCallReader<TArgs, TResult> {
+export interface ToolCallReader<
+  TArgs extends Record<string, unknown> = Record<string, unknown>,
+  TResult = unknown,
+> {
   args: ToolCallArgsReader<TArgs>;
   response: ToolCallResponseReader<TResult>;
 
@@ -85,7 +87,10 @@ export type ToolExecuteFunction<TArgs, TResult> = (
   context: ToolExecutionContext,
 ) => TResult | Promise<TResult>;
 
-export type ToolStreamCallFunction<TArgs, TResult> = (
+export type ToolStreamCallFunction<
+  TArgs extends Record<string, unknown> = Record<string, unknown>,
+  TResult = unknown,
+> = (
   reader: ToolCallReader<TArgs, TResult>,
   context: ToolExecutionContext,
 ) => void;
diff --git a/packages/assistant-stream/src/core/tool/toolResultStream.ts b/packages/assistant-stream/src/core/tool/toolResultStream.ts
index d09f2f47ba..110361e3cd 100644
--- a/packages/assistant-stream/src/core/tool/toolResultStream.ts
+++ b/packages/assistant-stream/src/core/tool/toolResultStream.ts
@@ -3,6 +3,7 @@ import { StandardSchemaV1 } from "@standard-schema/spec";
 import { ToolResponse } from "./ToolResponse";
 import { ToolExecutionStream } from "./ToolExecutionStream";
 import { AssistantMessage } from "../utils/types";
+import { ReadonlyJSONObject, ReadonlyJSONValue } from "../../utils";
 
 const isStandardSchemaV1 = (
   schema: unknown,
@@ -16,18 +17,20 @@ const isStandardSchemaV1 = (
 };
 
 function getToolResponse(
-  tools: Record<string, Tool<any, any>> | undefined,
+  tools: Record<string, Tool> | undefined,
   abortSignal: AbortSignal,
   toolCall: {
     toolCallId: string;
     toolName: string;
-    args: unknown;
+    args: ReadonlyJSONObject;
   },
 ) {
   const tool = tools?.[toolCall.toolName];
   if (!tool || !tool.execute) return undefined;
 
-  const getResult = async (toolExecute: ToolExecuteFunction<any, any>) => {
+  const getResult = async (
+    toolExecute: ToolExecuteFunction<ReadonlyJSONObject, unknown>,
+  ): Promise<ToolResponse<ReadonlyJSONValue>> => {
     let executeFn = toolExecute;
 
     if (isStandardSchemaV1(tool.parameters)) {
@@ -45,11 +48,12 @@ function getToolResponse(
       }
     }
 
-    const result = await executeFn(toolCall.args, {
+    const result = (await executeFn(toolCall.args, {
       toolCallId: toolCall.toolCallId,
       abortSignal,
-    });
-    if (result instanceof ToolResponse) return result;
+    })) as unknown as ReadonlyJSONValue;
+    if (result instanceof ToolResponse)
+      return result as ToolResponse<ReadonlyJSONValue>;
     return new ToolResponse({
       result: result === undefined ? "<no result>" : result,
     });
@@ -58,10 +62,11 @@ function getToolResponse(
   return getResult(tool.execute);
 }
 
-function getToolStreamResponse<TArgs, TResult>(
-  tools: Record<string, Tool<any, any>> | undefined,
+function getToolStreamResponse(
+  tools: Record<string, Tool> | undefined,
   abortSignal: AbortSignal,
-  reader: ToolCallReader<TArgs, TResult>,
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  reader: ToolCallReader<any, ReadonlyJSONValue>,
   context: {
     toolCallId: string;
     toolName: string;
@@ -75,7 +80,7 @@ function getToolStreamResponse<TArgs, TResult>(
 
 export async function unstable_runPendingTools(
   message: AssistantMessage,
-  tools: Record<string, Tool<any, any>> | undefined,
+  tools: Record<string, Tool> | undefined,
   abortSignal: AbortSignal,
 ) {
   // TODO parallel tool calling
@@ -90,7 +95,7 @@ export async function unstable_runPendingTools(
               ...p,
               state: "result" as const,
               artifact: result.artifact,
-              result: result.result,
+              result: result.result as ReadonlyJSONValue,
               isError: result.isError,
             };
           }
@@ -108,7 +113,7 @@ export async function unstable_runPendingTools(
 }
 
 export function toolResultStream(
-  tools: Record<string, Tool<any, any>> | undefined,
+  tools: Record<string, Tool> | undefined,
   abortSignal: AbortSignal,
 ) {
   return new ToolExecutionStream({
diff --git a/packages/assistant-stream/src/core/tool/type-path-utils.ts b/packages/assistant-stream/src/core/tool/type-path-utils.ts
index cc5c6a86d0..0ae1cec8be 100644
--- a/packages/assistant-stream/src/core/tool/type-path-utils.ts
+++ b/packages/assistant-stream/src/core/tool/type-path-utils.ts
@@ -1,3 +1,5 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
 type AsNumber<K> = K extends `${infer N extends number}` ? N | K : never;
 type TupleIndex<T extends readonly any[]> = Exclude<keyof T, keyof any[]>;
 type ObjectKey<T> = keyof T & (string | number);
diff --git a/packages/react-edge/src/edge/EdgeModelAdapter.ts b/packages/react-edge/src/edge/EdgeModelAdapter.ts
index afca461617..8b8e9e160f 100644
--- a/packages/react-edge/src/edge/EdgeModelAdapter.ts
+++ b/packages/react-edge/src/edge/EdgeModelAdapter.ts
@@ -78,7 +78,7 @@ export type EdgeModelAdapterOptions = {
   unstable_AISDKInterop?: boolean | "v2" | undefined;
 };
 
-const toAISDKTools = (tools: Record<string, Tool<any, any>>) => {
+const toAISDKTools = (tools: Record<string, Tool>) => {
   return Object.fromEntries(
     Object.entries(tools).map(([name, tool]) => [
       name,
@@ -92,10 +92,10 @@ const toAISDKTools = (tools: Record<string, Tool<any, any>>) => {
   );
 };
 
-const getEnabledTools = (tools: Record<string, Tool<any, any>>) => {
+const getEnabledTools = (tools: Record<string, Tool>) => {
   return Object.fromEntries(
     Object.entries(tools).filter(
-      ([_, tool]) => !tool.disabled && tool.type !== "backend",
+      ([, tool]) => !tool.disabled && tool.type !== "backend",
     ),
   );
 };
@@ -138,7 +138,9 @@ export class EdgeModelAdapter implements ChatModelAdapter {
             }),
         tools:
           this.options.unstable_AISDKInterop === "v2"
-            ? (toAISDKTools(getEnabledTools(context.tools ?? {})) as any)
+            ? (toAISDKTools(
+                getEnabledTools(context.tools ?? {}),
+              ) as unknown as EdgeRuntimeRequestOptions["tools"])
             : toLanguageModelTools(getEnabledTools(context.tools ?? {})),
         unstable_assistantMessageId,
         runConfig,
diff --git a/packages/x-buildutils/eslint/index.ts b/packages/x-buildutils/eslint/index.ts
index c85fb67c46..6ca3b1ca39 100644
--- a/packages/x-buildutils/eslint/index.ts
+++ b/packages/x-buildutils/eslint/index.ts
@@ -11,6 +11,16 @@ const compat = new FlatCompat({
 
 const eslintConfig = [
   ...compat.extends("next/core-web-vitals", "next/typescript"),
+  {
+    rules: {
+      "@typescript-eslint/no-unused-vars": [
+        "error",
+        {
+          ignoreRestSiblings: true,
+        },
+      ],
+    },
+  },
 ];
 
 export default eslintConfig;


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This pull request refactors internal types and makes several ESLint fixes across the codebase. Key changes include the tightening of generic type constraints (e.g. replacing Tool<any, any> with Tool and adding ReadonlyJSON types), ESLint rule additions, and minor adjustments to improve type safety and consistency across tool-related modules.

### Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

<details>
<summary>Show a summary per file</summary>

| File | Description |
| ---- | ----------- |
| packages/x-buildutils/eslint/index.ts | Added an ESLint rule override for unused vars within the ESLint configuration. |
| packages/react-edge/src/edge/EdgeModelAdapter.ts | Updated generic types in tool conversion functions and refined type casting for AISDK interoperability. |
| packages/assistant-stream/src/core/tool/type-path-utils.ts | Minor ESLint directive addition and no-impact type changes. |
| packages/assistant-stream/src/core/tool/toolResultStream.ts | Adjusted generic types in tool response functions for better type safety. |
| packages/assistant-stream/src/core/tool/tool-types.ts | Revised generic type constraints to enforce stricter type contracts. |
| packages/assistant-stream/src/core/tool/ToolResponse.ts | Narrowed the Symbol.hasInstance implementation to the updated ReadonlyJSONValue type. |
| packages/assistant-stream/src/core/tool/ToolExecutionStream.ts | Updated generic parameters when instantiating tool call readers. |
| packages/assistant-stream/src/core/tool/ToolCallReader.ts | Refactored several helper functions and stream methods to use more explicit generic types and type assertions. |
</details>



<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**packages/react-edge/src/edge/EdgeModelAdapter.ts:143**
* Please verify that the double casting (first to unknown and then to EdgeRuntimeRequestOptions["tools"]) is necessary and safely handles the type conversion. Consider refining the types to avoid multiple casts if possible.
```
              ) as unknown as EdgeRuntimeRequestOptions["tools"]
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T10:36:32Z
user: login: greptile-apps[bot]
body: 
## PR Summary

Enhanced type safety across the assistant-ui codebase by replacing generic types with more specific readonly JSON types and improving ESLint configurations.

- Replaced `unknown`/`any` with `ReadonlyJSONObject`/`ReadonlyJSONValue` in `packages/assistant-stream/src/core/tool/ToolExecutionStream.ts` for better type guarantees
- Added `@typescript-eslint/no-unused-vars` rule in `packages/x-buildutils/eslint/index.ts` with `ignoreRestSiblings` option
- Consolidated type imports by moving `DeepPartial` from 'ai' to local `type-path-utils.ts`
- Modified `ToolResponse` class to ensure tool responses only contain readonly JSON values
- Improved type constraints in `EdgeModelAdapter.ts` by removing generic type parameters and fixing destructuring patterns

<sub>8 file(s) reviewed, 3 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=assistant-ui_1911)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T10:36:56Z
user: login: cubic-dev-ai[bot]
body: mrge found 4 issues across 8 files. View them in [mrge.io](https://mrge.io/pr/assistant-ui/assistant-ui/pull/1911)
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T10:37:56Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🔭 Outside diff range comments (2)</summary><blockquote>

<details>
<summary>packages/assistant-stream/src/core/tool/tool-types.ts (1)</summary><blockquote>

`85-88`: _🛠️ Refactor suggestion_

**`ToolExecuteFunction` should mirror the new JSON-value result constraint**

`ToolExecuteFunction<TArgs, TResult>` still allows an arbitrary `TResult`.  Other updated code (e.g. `toolResultStream.ts`) casts the result to `ReadonlyJSONValue`, so the typings are now unsound.

```diff
-export type ToolExecuteFunction<TArgs, TResult> = (
+export type ToolExecuteFunction<
+  TArgs extends ReadonlyJSONObject,
+  TResult extends ReadonlyJSONValue,
+> = (
```

This prevents accidental return of unserializable objects and removes the unsafe cast currently needed in `toolResultStream.ts`.

</blockquote></details>
<details>
<summary>packages/assistant-stream/src/core/tool/ToolCallReader.ts (1)</summary><blockquote>

`240-266`: _🛠️ Refactor suggestion_

**Potential memory leak – disposed handles stay in the `Set`**

Handles mark themselves as `disposed = true` but are never removed from `this.handles`.  
For long-running streams (or when many `get/stream*` calls are issued) this `Set` can grow without bound.

Minimal fix – prune on every update:

```diff
for (const handle of this.handles) {
-  handle.update(parsedArgs);
+  handle.update(parsedArgs);
+  if ((handle as unknown as { disposed: boolean }).disposed) {
+    this.handles.delete(handle);
+  }
}
```

A more robust approach is to let `Handle.dispose()` notify the parent reader so removal happens immediately.

</blockquote></details>

</blockquote></details>

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>packages/assistant-stream/src/core/tool/ToolCallReader.ts (1)</summary><blockquote>

`234-239`: **Interface mismatch will fail to compile after the change above**

`ToolCallArgsReaderImpl<T extends ReadonlyJSONObject> implements ToolCallArgsReader<T>` is currently *only* valid if `T` is assignable to the upper-bound declared in `tool-types.ts`.  Until that bound is fixed (see previous comment) this class will not compile.

No action required in this file once the upper-bound is aligned.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>packages/assistant-stream/src/core/tool/tool-types.ts (1)</summary><blockquote>

`152-158`: **Default type arguments can be tightened for safer ergonomics**

Developers instantiating `Tool` without generics now silently fall back to `Record<string, unknown>` / `unknown`, undermining the safety gained elsewhere.  Consider:

```diff
-export type Tool<
-  TArgs extends Record<string, unknown> = Record<string, unknown>,
-  TResult = unknown,
+export type Tool<
+  TArgs extends ReadonlyJSONObject = ReadonlyJSONObject,
+  TResult extends ReadonlyJSONValue = ReadonlyJSONValue,
 > = …
```

This keeps the defaults JSON-safe while still letting advanced callers specialise when needed.

</blockquote></details>
<details>
<summary>packages/assistant-stream/src/core/tool/ToolCallReader.ts (1)</summary><blockquote>

`344-366`: **`as any` casts defeat the stricter typing work**

All three streaming helpers drop back to `any` before returning, undoing the generics you painstakingly added.  
Once `AsyncIterableStream` is generic (it already is), you can keep the correct type without the cast:

```diff
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-return asAsyncIterableStream(stream) as any;
+return asAsyncIterableStream<
+  DeepPartial<TypeAtPath<T, PathT>>
+>(stream);
```

Apply the same pattern to `streamText` and `forEach` to regain end-to-end type safety.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 509a0f497c7fb68a02c91d25391a654b1c69ad9a and 0b6f52f6854c01d2e31e765dfcfcb20581b9aef7.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `packages/assistant-stream/src/core/tool/ToolCallReader.ts` (15 hunks)
* `packages/assistant-stream/src/core/tool/ToolExecutionStream.ts` (4 hunks)
* `packages/assistant-stream/src/core/tool/ToolResponse.ts` (1 hunks)
* `packages/assistant-stream/src/core/tool/tool-types.ts` (4 hunks)
* `packages/assistant-stream/src/core/tool/toolResultStream.ts` (7 hunks)
* `packages/assistant-stream/src/core/tool/type-path-utils.ts` (1 hunks)
* `packages/react-edge/src/edge/EdgeModelAdapter.ts` (3 hunks)
* `packages/x-buildutils/eslint/index.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>packages/assistant-stream/src/core/tool/ToolResponse.ts (3)</summary><blockquote>

<details>
<summary>packages/assistant-stream/src/core/tool/index.ts (1)</summary>

* `ToolResponse` (2-2)

</details>
<details>
<summary>packages/assistant-stream/src/utils.ts (1)</summary>

* `ReadonlyJSONValue` (10-10)

</details>
<details>
<summary>packages/assistant-stream/src/utils/json/json-value.ts (1)</summary>

* `ReadonlyJSONValue` (1-7)

</details>

</blockquote></details>
<details>
<summary>packages/assistant-stream/src/core/tool/toolResultStream.ts (3)</summary><blockquote>

<details>
<summary>packages/assistant-stream/src/core/tool/tool-types.ts (3)</summary>

* `Tool` (152-158)
* `ToolExecuteFunction` (85-88)
* `ToolCallReader` (65-78)

</details>
<details>
<summary>packages/assistant-stream/src/utils/json/json-value.ts (2)</summary>

* `ReadonlyJSONObject` (9-11)
* `ReadonlyJSONValue` (1-7)

</details>
<details>
<summary>packages/assistant-stream/src/core/tool/ToolResponse.ts (1)</summary>

* `ToolResponse` (11-33)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: mrge · AI code reviewer
* GitHub Check: Analyze (javascript-typescript)

</details>

<details>
<summary>🔇 Additional comments (12)</summary><blockquote>

<details>
<summary>packages/x-buildutils/eslint/index.ts (1)</summary>

`14-23`: **Good ESLint rule addition for better TypeScript code quality**

This configuration properly adds the `@typescript-eslint/no-unused-vars` rule with `ignoreRestSiblings: true`, which is a sensible choice. The rule will help catch unused variables (improving code cleanliness), while the option will prevent false positives when using object destructuring with rest patterns (e.g., `const { used, unused, ...rest } = obj`).

This change aligns well with the PR's overall goal of tightening TypeScript types across the codebase.

</details>
<details>
<summary>packages/assistant-stream/src/core/tool/type-path-utils.ts (1)</summary>

`1-1`: **Added ESLint directive to disable explicit `any` warnings throughout the file.**

This file contains complex recursive type definitions that legitimately need to use `any` in several places. The added ESLint directive helps maintain code clarity by suppressing warnings for these necessary uses of `any`.

</details>
<details>
<summary>packages/assistant-stream/src/core/tool/ToolResponse.ts (1)</summary>

`26-28`: **Improved type safety with more specific return type.**

The `Symbol.hasInstance` method now returns a more specific type signature using `ToolResponse<ReadonlyJSONValue>` instead of `ToolResponse<unknown>`. This change aligns with the broader effort to enforce JSON-compatible data structures throughout the tool execution system.

</details>
<details>
<summary>packages/react-edge/src/edge/EdgeModelAdapter.ts (3)</summary>

`81-81`: **Simplified tool type signature for better type inference.**

The `toAISDKTools` helper function signature has been simplified by removing generic parameters, changing from `Record<string, Tool<any, any>>` to `Record<string, Tool>`. This change improves type inference while reducing unnecessary generic parameters.

---

`95-99`: **Simplified type signature and removed redundant filter condition.**

The `getEnabledTools` helper function signature was similarly simplified, and the filter condition appears to have been updated for clarity. This makes the code more maintainable while preserving the same functionality.

---

`141-143`: **Improved type safety with explicit cast through `unknown`.**

The type cast chain has been improved from using `any` to using `unknown` as an intermediate step before the final type. This is a safer approach as it forces explicit type verification, aligning with TypeScript best practices for type assertions.

</details>
<details>
<summary>packages/assistant-stream/src/core/tool/ToolExecutionStream.ts (5)</summary>

`8-11`: **Improved imports organization for JSON value types.**

The import statements have been restructured to import JSON-related types from the utility module. This change helps maintain better type consistency throughout the codebase.

---

`20-20`: **Tightened argument type to ensure JSON compatibility.**

The `args` parameter in the `ToolCallback` type has been updated from a more permissive type to `ReadonlyJSONObject`, ensuring that tool arguments are always JSON-compatible objects. This change improves type safety and prevents potential type-related issues.

---

`26-29`: **Enhanced generic type constraints for improved type safety.**

The `ToolStreamCallback` generic type parameters have been updated to explicitly constrain arguments to `ReadonlyJSONObject` and results to `ReadonlyJSONValue`. This ensures consistent typing across the tool execution system and provides better type inference for consumers.

---

`48-48`: **Updated type parameter for tool call controllers map.**

The map type for `toolCallControllers` has been updated to use the more specific `ReadonlyJSONObject` and `ReadonlyJSONValue` types instead of more permissive types. This ensures type consistency throughout the tool execution flow.

---

`67-70`: **Improved type parameters for ToolCallReaderImpl.**

The instantiation of `ToolCallReaderImpl` now uses explicit type parameters `ReadonlyJSONObject` and `ReadonlyJSONValue`, ensuring that tool call data is properly constrained to JSON-compatible types throughout the execution flow.

</details>
<details>
<summary>packages/assistant-stream/src/core/tool/toolResultStream.ts (1)</summary>

`65-71`: **`any` slip-through in `getToolStreamResponse` defeats new strictness**

`ToolCallReader<any, ReadonlyJSONValue>` re-introduces `any`.  
You already have `ToolCallReader<ReadonlyJSONObject, ReadonlyJSONValue>` elsewhere – use it here and remove the ESLint suppression.

```diff
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-reader: ToolCallReader<any, ReadonlyJSONValue>,
+reader: ToolCallReader<ReadonlyJSONObject, ReadonlyJSONValue>,
```

[ suggest_nitpick ]

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T10:39:14Z
user: login: ellipsis-dev[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T10:39:19Z

Review comments:
diff_hunk: @@ -138,7 +138,9 @@ export class EdgeModelAdapter implements ChatModelAdapter {
             }),
         tools:
           this.options.unstable_AISDKInterop === "v2"
-            ? (toAISDKTools(getEnabledTools(context.tools ?? {})) as any)
+            ? (toAISDKTools(
+                getEnabledTools(context.tools ?? {}),
+              ) as unknown as EdgeRuntimeRequestOptions["tools"])
             : toLanguageModelTools(getEnabledTools(context.tools ?? {})),
path: packages/react-edge/src/edge/EdgeModelAdapter.ts
user: login: greptile-apps[bot]
body: style: Type assertion to unknown then to EdgeRuntimeRequestOptions["tools"] is unsafe - consider validating the shape of the data before the cast
created_at: 2025-04-28T10:36:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,11 +48,12 @@ function getToolResponse(
       }
     }
 
-    const result = await executeFn(toolCall.args, {
+    const result = (await executeFn(toolCall.args, {
       toolCallId: toolCall.toolCallId,
       abortSignal,
-    });
-    if (result instanceof ToolResponse) return result;
+    })) as unknown as ReadonlyJSONValue;
path: packages/assistant-stream/src/core/tool/toolResultStream.ts
user: login: greptile-apps[bot]
body: logic: Double type assertion here is unsafe. Consider validating the result structure before casting to ReadonlyJSONValue
created_at: 2025-04-28T10:36:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -58,10 +62,11 @@ function getToolResponse(
   return getResult(tool.execute);
 }
 
-function getToolStreamResponse<TArgs, TResult>(
-  tools: Record<string, Tool<any, any>> | undefined,
+function getToolStreamResponse(
+  tools: Record<string, Tool> | undefined,
   abortSignal: AbortSignal,
-  reader: ToolCallReader<TArgs, TResult>,
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  reader: ToolCallReader<any, ReadonlyJSONValue>,
path: packages/assistant-stream/src/core/tool/toolResultStream.ts
user: login: greptile-apps[bot]
body: style: Using `any` type here defeats the purpose of the type safety improvements. Consider using a more specific type or explaining why `any` is necessary
created_at: 2025-04-28T10:36:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -315,15 +329,19 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
       },
     });
 
-    // For type compatibility, cast the stream to the required type
-    return stream as any;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    return asAsyncIterableStream(stream) as any;
path: packages/assistant-stream/src/core/tool/ToolCallReader.ts
user: login: Copilot
body: [nitpick] Consider replacing the 'as any' casting after converting with asAsyncIterableStream with a more explicit type annotation to improve type safety and clarity.
```suggestion
    return asAsyncIterableStream(stream) as AsyncIterableStream<DeepPartial<TypeAtPath<T, PathT>>>;
```
created_at: 2025-04-28T10:36:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -343,15 +361,19 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
       },
     });
 
-    // For type compatibility, cast the stream to the required type
-    return stream as any;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    return asAsyncIterableStream(stream) as any;
path: packages/assistant-stream/src/core/tool/ToolCallReader.ts
user: login: Copilot
body: [nitpick] Consider replacing the 'as any' casting after converting with asAsyncIterableStream with a more explicit type annotation to improve type safety and clarity in this streamText method.
```suggestion
    return asAsyncIterableStream(stream) as AsyncIterableStream<
      TypeAtPath<T, PathT> extends string & infer U ? U : never
    >;
```
created_at: 2025-04-28T10:36:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -371,12 +393,12 @@ export class ToolCallArgsReaderImpl<T> implements ToolCallArgsReader<T> {
       },
     });
 
-    // For type compatibility, cast the stream to the required type
-    return stream as any;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    return asAsyncIterableStream(stream) as any;
   }
path: packages/assistant-stream/src/core/tool/ToolCallReader.ts
user: login: Copilot
body: [nitpick] Consider replacing the 'as any' casting after converting with asAsyncIterableStream with a more explicit type annotation to ensure the forEach method returns the properly inferred AsyncIterableStream type.
```suggestion
    return asAsyncIterableStream(stream) as AsyncIterableStream<
      TypeAtPath<T, PathT> extends Array<infer U> ? U : never
    >;
```
created_at: 2025-04-28T10:36:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,13 +17,16 @@ import { ToolCallReader } from "./tool-types";
 type ToolCallback = (toolCall: {
   toolCallId: string;
   toolName: string;
-  args: unknown;
+  args: ReadonlyJSONObject;
path: packages/assistant-stream/src/core/tool/ToolExecutionStream.ts
user: login: cubic-dev-ai[bot]
body: <!-- metadata:{"confidence":7} -->
Type change from 'unknown' to 'ReadonlyJSONObject' could break existing implementations that pass non-JSON-object arguments
created_at: 2025-04-28T10:37:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,18 +8,24 @@ import {
   ToolCallReader,
   ToolCallResponseReader,
 } from "./tool-types";
-import { TypeAtPath, TypePath } from "./type-path-utils";
+import { DeepPartial, TypeAtPath, TypePath } from "./type-path-utils";
 import { ToolResponse } from "./ToolResponse";
+import {
+  asAsyncIterableStream,
+  AsyncIterableStream,
+  ReadonlyJSONObject,
+  ReadonlyJSONValue,
+} from "../../utils";
 
 // TODO: remove dispose
 
-function getField<T>(obj: T, fieldPath: (string | number)[]): any {
-  let current: any = obj;
+function getField<T>(obj: T, fieldPath: (string | number)[]): unknown {
+  let current: unknown = obj;
   for (const key of fieldPath) {
     if (current === undefined || current === null) {
       return undefined;
     }
-    current = current[key as string | number];
+    current = current[key as keyof typeof current];
path: packages/assistant-stream/src/core/tool/ToolCallReader.ts
user: login: cubic-dev-ai[bot]
body: <!-- metadata:{"confidence":7} -->
The type assertion could fail at runtime with non-object types
created_at: 2025-04-28T10:37:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -58,10 +62,11 @@ function getToolResponse(
   return getResult(tool.execute);
 }
 
-function getToolStreamResponse<TArgs, TResult>(
-  tools: Record<string, Tool<any, any>> | undefined,
+function getToolStreamResponse(
path: packages/assistant-stream/src/core/tool/toolResultStream.ts
user: login: cubic-dev-ai[bot]
body: <!-- metadata:{"confidence":8} -->
Function signature changes from generic to specific types but still uses 'any' in ToolCallReader parameter
created_at: 2025-04-28T10:37:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -58,10 +62,11 @@
   return getResult(tool.execute);
 }
 
-function getToolStreamResponse<TArgs, TResult>(
-  tools: Record<string, Tool<any, any>> | undefined,
+function getToolStreamResponse(
+  tools: Record<string, Tool> | undefined,
   abortSignal: AbortSignal,
-  reader: ToolCallReader<TArgs, TResult>,
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
path: packages/assistant-stream/src/core/tool/toolResultStream.ts
user: login: cubic-dev-ai[bot]
body: <!-- metadata:{"confidence":9} -->
ESLint disable comment contradicts the PR's 'tighten internal types' goal
created_at: 2025-04-28T10:37:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,7 +12,7 @@ import { ToolResponse } from "./ToolResponse";
  *
  * @template TArgs The type of arguments being read.
  */
-export interface ToolCallArgsReader<TArgs> {
+export interface ToolCallArgsReader<TArgs extends Record<string, unknown>> {
   /**
    * Returns a promise that will resolve to the value at the given path,
    * as soon as that path is generated by the LLM.
path: packages/assistant-stream/src/core/tool/tool-types.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Generic constraint is incompatible with downstream `ReadonlyJSONObject` usage – causes compilation errors**

`ToolCallArgsReader<TArgs extends Record<string, unknown>>` (and the similar constraints added further below) conflict with the new `ToolCallArgsReaderImpl<T extends ReadonlyJSONObject>` implementation in `ToolCallReader.ts`.  
Because a type with a *readonly* index signature (`ReadonlyJSONObject`) is **not** assignable to `Record<string, unknown>`, `ToolCallReaderImpl` can no longer satisfy the interface contract, resulting in a TS2322/TS2417 error once the files are compiled together.

Proposed fix – tighten the upper-bound everywhere to the same JSON-compatible type:

```diff
-import { DeepPartial, TypeAtPath, TypePath } from "./type-path-utils";
+import { DeepPartial, TypeAtPath, TypePath } from "./type-path-utils";
+import { ReadonlyJSONObject } from "../../utils";
…
-export interface ToolCallArgsReader<TArgs extends Record<string, unknown>> {
+export interface ToolCallArgsReader<
+  TArgs extends ReadonlyJSONObject = ReadonlyJSONObject,
+> {
```

The same `ReadonlyJSONObject` bound (with the default) should be applied to **all** generic parameters introduced in this file (`ToolCallReader`, `ToolStreamCallFunction`, `ToolExecuteFunction`, `Tool`, …) to restore internal consistency.




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
import { DeepPartial, TypeAtPath, TypePath } from "./type-path-utils";
import { ReadonlyJSONObject } from "../../utils";

export interface ToolCallArgsReader<
  TArgs extends ReadonlyJSONObject = ReadonlyJSONObject,
> {
  /**
   * Returns a promise that will resolve to the value at the given path,
   * as soon as that path is generated by the LLM.
   */
  // …
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-28T10:39:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -16,18 +17,20 @@ const isStandardSchemaV1 = (
 };
 
 function getToolResponse(
-  tools: Record<string, Tool<any, any>> | undefined,
+  tools: Record<string, Tool> | undefined,
   abortSignal: AbortSignal,
   toolCall: {
     toolCallId: string;
     toolName: string;
-    args: unknown;
+    args: ReadonlyJSONObject;
   },
path: packages/assistant-stream/src/core/tool/toolResultStream.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Unsafe cast can be removed once `ToolExecuteFunction` is tightened**

The `as unknown as ReadonlyJSONValue` cast and the `instanceof ToolResponse` branch are only necessary because `ToolExecuteFunction` currently allows arbitrary return types.  
After constraining `TResult` to `ReadonlyJSONValue` (see earlier comment), the cast disappears and the `instanceof` check can be simplified to a single overload-guard:

```diff
-const result = (await executeFn(toolCall.args, { … })) as unknown as ReadonlyJSONValue;
-if (result instanceof ToolResponse) return result;
-return new ToolResponse({ result: result ?? "<no result>" });
+const result = await executeFn(toolCall.args, { … });
+return result instanceof ToolResponse
+  ? result
+  : new ToolResponse({ result: result ?? "<no result>" });
```

This keeps runtime logic unchanged while leveraging the stronger types.




> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-28T10:39:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,18 +8,24 @@ import {
   ToolCallReader,
   ToolCallResponseReader,
 } from "./tool-types";
-import { TypeAtPath, TypePath } from "./type-path-utils";
+import { DeepPartial, TypeAtPath, TypePath } from "./type-path-utils";
 import { ToolResponse } from "./ToolResponse";
+import {
+  asAsyncIterableStream,
+  AsyncIterableStream,
+  ReadonlyJSONObject,
+  ReadonlyJSONValue,
+} from "../../utils";
 
 // TODO: remove dispose
 
-function getField<T>(obj: T, fieldPath: (string | number)[]): any {
-  let current: any = obj;
+function getField<T>(obj: T, fieldPath: (string | number)[]): unknown {
+  let current: unknown = obj;
   for (const key of fieldPath) {
     if (current === undefined || current === null) {
       return undefined;
     }
-    current = current[key as string | number];
+    current = current[key as keyof typeof current];
path: packages/assistant-stream/src/core/tool/ToolCallReader.ts
user: login: ellipsis-dev[bot]
body: Using `current = current[key as keyof typeof current]` accesses properties on an unknown value. It might be safer to add type guards to ensure `current` is an object before property access to avoid runtime errors.
```suggestion
    current = (typeof current === "object" && current !== null) ? current[key as keyof typeof current] : undefined;
```

created_at: 2025-04-28T10:39:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -315,15 +329,19 @@
       },
     });
 
-    // For type compatibility, cast the stream to the required type
-    return stream as any;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    return asAsyncIterableStream(stream) as any;
path: packages/assistant-stream/src/core/tool/ToolCallReader.ts
user: login: ellipsis-dev[bot]
body: Several stream methods cast the result to `any` after calling `asAsyncIterableStream`. It would be better to ensure proper typing throughout instead of using `any` with ESLint disable comments.
```suggestion
    return asAsyncIterableStream(stream) as AsyncIterableStream<DeepPartial<TypeAtPath<T, PathT>>>;
```

created_at: 2025-04-28T10:39:19Z
author_association: CONTRIBUTOR

PR comments:
user: login: entelligence-ai-pr-reviews[bot]
created_at: 2025-04-28T10:35:24Z
body: ⚠️ **Trial Period Expired** ⚠️

Your trial period has expired. To continue using this feature, please upgrade to a paid plan [here](https://entelligence.ai/manage/billing) or [book a time to chat here](https://calendly.com/aiswarya-qlnt).
author_association: CONTRIBUTOR
user: login: changeset-bot[bot]
created_at: 2025-04-28T10:35:25Z
body: ###  ⚠️  No Changeset found

Latest commit: 0b6f52f6854c01d2e31e765dfcfcb20581b9aef7

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/assistant-ui/assistant-ui/new/sf-branch-79?filename=.changeset/mighty-dodos-remain.md&value=---%0A%22assistant-stream%22%3A%20patch%0A%22%40assistant-ui%2Freact-edge%22%3A%20patch%0A%22%40assistant-ui%2Fx-buildutils%22%3A%20patch%0A---%0A%0Arefactor%3A%20tighten%20internal%20types%20and%20eslint%20fixes%0A)


author_association: NONE
user: login: promptless[bot]
created_at: 2025-04-28T10:35:46Z
body: ✅ No documentation updates required.
author_association: CONTRIBUTOR
user: login: trag-bot[bot]
created_at: 2025-04-28T10:35:47Z
body: ### Pull request summary

- Added `DeepPartial` type to improve type safety in various function signatures.
- Changed the return type of `getField` function from `any` to `unknown` for better type inference.
- Updated `GetHandle` and related classes to use generic types for `resolve` and `controller` methods, enhancing type specificity.
- Modified `ToolCallArgsReaderImpl` to enforce `ReadonlyJSONObject` for argument types, ensuring immutability.
- Refined `ToolCallReaderImpl` and `ToolExecutionStream` to utilize specific types instead of `unknown`, improving type clarity.
- Enhanced type definitions in `ToolStreamCallback` and `ToolExecuteFunction` to ensure they accept only valid types.
- Updated utility functions to handle `ReadonlyJSONValue` for consistent type handling across the codebase.
- Added ESLint rules to prevent unused variable warnings while allowing rest siblings, improving code quality during development.
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-04-28T10:39:10Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The changes across the codebase focus on enforcing stricter and more consistent type safety, particularly regarding tool call arguments and results. Generic parameters for tool-related classes, interfaces, and functions are now constrained to `ReadonlyJSONObject` for arguments and `ReadonlyJSONValue` for results, replacing previous usage of `unknown`, `any`, or unconstrained generics. Method signatures and type aliases have been updated accordingly, ensuring that only JSON-compatible data structures are used throughout the tool execution and streaming logic. Some helper functions and internal logic have also been updated to reflect these stricter types, including changes to type casting and the use of explicit type annotations. Additionally, minor code style and linting adjustments were made, such as disabling ESLint rules for explicit `any` usage in one file and refining ESLint configuration for unused variables. No core logic or control flow has been altered as a result of these changes.

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEejqANiS4USAMzRj8FLuKKwaWeBhoUMaC0hcWW5pdAx6aQtvXEg7eAAPaQMAOWxmAUouAEYATmzsgwBVRCzIAE18DBYjfWNwKDJ6fDscAmIyZRp6JlZ2Ll5+YVFxKRl5JiUqVXUtHTqTKDhUVEw2wlJyKm6FPp8bNAB3SER05jQKeTkFKZU1TW1dMEN60wNuRwBrNFJEAHo0IhEEhcJhcGBELhbGhmH9EBQGH8mLY/gR8BY/tB8OiAMIBCwAJRIaCmGlwiA4BgARDSDABiOmQACCAEkOltqHQTmcLvIWoxYJhfmZYCQBUKwjEKIpsAwxZDpRhSHxgtxvERwvR4MxePgpPRVfK0HYSMEgrBpdh3Pg8OaxQADLG4/FEkmUMmIe2QZgyqwaGCiyCbSjwBg8C4w02USCOr3eGNOix4iwWJkUIiIV1TFk6ixezD0R3YpMu4nZ3Nx5DVY5MDAK7TkA34GNZqoWWQAKQAygB5FI94Zie0AGk1CaJpwsuC9BBbZbbnd7KQAagFsCR7f64A7SLgAGLwEgWWheuzYDBieBVADkyFsuGwfiCITFqAYgqVXLs0uYMcwshnZt7XPT5qwwEcx0+Eh5EcOUgRQZBDXoH1bB4WwGCQI95GwYElRjKDZH5Q1+QYR9bB8TcAwdABxU0AAkCysL0GAsQFkFQbBuFoTkm0gTIg06ChQ34KQ+AEfBcFgBN8wiO1IFsRB0X1SAJDXMVDQTVcLHXCDDnUKTkQU7gqlodVn1CCFjTFBSlLFBh8QEL5EH9JkUznEkVCsLsoWJZgABF7DQbApxxKooXRKwKDjOtQQvMILnUl96EBGMQLA6LITLfhWntf9KO3GNWMhLSdJQLB7W86FmGgEgElwBiIiYhDzK5FL7QVMyAB9IHPJR4kbDKaBJbK/wwACt0De1dwg9qfJhErpBmhVfJquqIILGM7GcABRRxYC9NhJMUdjysTZNU3TTMy0oHNuDzSBq0gQUpEgWq7tDdR5NNR8sENO97CsS88MksU3pGLk/sgKZJC/H85ODISw1jMcQfDSSWsQUdvBY7BTLwnCxX5e0ApIbgAAULnEAJT2cE45rYehVO06QJvlen/ofPwuWuEHSm9U1YGO9BUMOKhuFCeg9PR3LECZRBZAvFlfE8khKt8mm+FrYFMp8dB5YvFBlYEKwyt8Bw4NZhNi3OidjLrEgsxuisxyLZ0U0dihbvuli2Pi1DOO4nZZy1qEGzteA+AR4SPioQ7KEQoDWwwdtuz7BaZMLJOU6XAcRCHL7EFCS8pHbf0e1E/FR1R98JWQLbSOQKpXowD8MOBl8FAodDcHIeCNq1kEyAYeQpcF20Al8Mzq+cMULHwIhhNp2twsCOw58ODQjCMBlmSnLorzrIJm1RpQfe2A/G9aN7nB2WnuGwY3hPYdRD0QWpIBxIrkHtWj6sYjcyoxg+Awb4vwARAhBGCCE9M4QIiRDPVExZMTW1LG6CgHovQBx4kfASWxhI/3ov/YA0BRzQAWnoL0o8Yw2QsFIGcSU9YxgABRM3XFwMhakACUkAAC8egVL4HgCeTeUBP5sStm7C6GYPZe2ikAr4PxpDgO1rFcEy0YSwMRMiEgiD0TIMkR7DB4pPz0GBEQfwnN1JATOviNM0jrqe1zMQ16dUmjICzouPsucRgULHIdQWtBv7TVHLNKqC1PQhPUdVWq05RwbXtFtCgu13yYK4tgqhKExS8FEJhXBIYwz3h+hjMqEglJmUdC+JkuAKaSRmiTcmlN4DUzibJWsplxBVACBjERH8v4SJLO7aQdtSgyOdvGe0wDQFKLYpAnw0CqqaPgSiNEGIbGDLQUYmuJiTjwHMdQR8Vj+k2yGVUEZDivbEInMFWIMS3GQA8anFcakKE9LEfBV2AzCTnLGeVSZij/gzMhFAqJiztG6NWSg9ZpJyTMQ/KQUxuyLEHJwR845aCLnQDscgW5ER3HzmTp4/sg5cCkKuVOFxnhAn3PxdnNOzzNzUlpAYCAYAjB/LAYC1R8zfKgoQSs/RFhtpJFIh0jAasYQekpDSKk9JGSsnZF0LkpxWC8hGls4UBgCrqslD4aUtBZRs0Rr4cyZlElH3RIwfEwsiDpHYMgDaClrnjC+ndRwU9AxRyRmlfAhxwItUgFQ+0Dyc7Eo1ta21Ph7WySDTSwl6c4i00dVOe1DBpTwVRqihyXwZqJnFcwc6jkQHrVkjESg/hAiZqhU7O6mDECKImisC82klDICweIYGgYNIx0jL4E6ckPl5oLdmvJiMdl7MsSjQM3ggU+CaaKkalavnoudqPeMGbExCtEHgA+ebmJf0tj6Uy8R7KiuxXWZFklqDmsCPZNyFwbVsEjcLMUARDhoFkMgR5Qw87kjHEm39CVIBftYdILGOppQSCnh3OtJozT91OYPC88g12Bg/LQaIeEiLFktW5QOaBzSWmtLaVGUS0L4DgrhDUc8F4ME3lvHerllanpwSfUQrFz6nJGtfCgt8+D30fmGZ+4hkjMsgEUNJQcO4BCad/NZFhC2fC9N2uOfBcqXVPHDYCGBQI+r9bOGNJIFyPO8UOHp4nA4Qyk9ENqub6ZDqLTg/i+HPUBv0lDQKTqYzEKxRSu5wavHEt4dSwzBLjPEtJdIa5vncXBdoEZpcC0gv+aeczF5omcRwohp2juRMVnnVCrqiK8cDpoG4CpNSfaPmbpFTu+mzFTlQllAQPg34WBHNQeWO6wBvW+tHD1jAvj9NydGV15LJmSWxfi3S1LDLpW1DeOy6ZECZ1qJgfCLRfKkGJltqckgkrGUyoY2yBG2DlXnEuGqzLb9NWBiBeIMM/jFAxgANpdlkBkdEGhBSIBZDFTAcoAC6cj13Fh2/bPd4i33IEPfAeIENmzasNohDu2TTInust9J8GlWu/g+WD0o3XtNgUG9Y0HJz7bAGS+Q/KgZHsIvHciqHLiPgRFhm1/DlGrBgAw1kiMKmcH4eYNc+A3PvBikSecWIAhbT3gbFlo0bBwyxyjHwJh9pBBCC4P1+0nDLalr8F06ji9Wiozp19c406eoXky7QUckJ4BuXfKIT4ZlYdyW8PfWIGuRjNXw9UDAYAMDBUCN7sQCgfANndQ6aAPYewABkAD6BJtpdjJn2Ls20E9dnKAAWQAELx6U9KUIPHZB0e3nKveHHD6zlY2fagF8uMJGMjxrkd8H7REE7O4T12oDeQbw9gWT2zFIv9hJugXBIAGEgDGO7wlXvvfEhYL7gJfszsB0wjXWuie6c4VwDXzVtvk4J/13xU+Z+ACTCGf0/Z+gnu5ABfH3l/fbX7FDfW+rc6d9Xv79h+yeF120p1jUeWp2vxn3jB9neSPwAPB0AQmQUQ5WWy5RBXWyWR0X5WgOGT2xhQO3m1ZXeAQKWxUWBTWzgTBX5RWTAD+n2zmyOwVW2CVR5AuxIiuxFFfB1Bvjpk5EfV/UOEoDslOXRCEWwWuF6DUAwDMiFynHgDunYJb1/Rx0gCpA0FRBfDAA+EkjAG3QsEQCpFNmbA53VBNh50thcy7T5xVwTCxWB0mjkyxQ9mijNkcDFCZxDioDF14hxUzlEGcFoGAA6iVD6x319QoVHDIESTbg1EvViHvQjViEhiF0hD4kJmJVFygn9C7G1AdwuHbCrlsMhSXSmEcMoHNhcJSjbXh3QFoHoD6iCnJRc2UxVyjR6Aa3cNOmsJwS8LnGRD8ICKICCK/wGxp2j2LEHXxD3HPEvCqHoVCHQGs2QCZwCEUm9EUFh0PF4mxmbTFFqKiwaIsN7THDcLDkdA6NnC6JjR6P8KhHVAGOJ2GL5m1ROnCn1TlDpiNWjFVDMiOJiGaPcwcF2MEmjn2PjgTTElNGNQ0hg1NBglaQQx1mHnQFTXwHTU7Ww1vQsDAFsFYh2H11KN+OoK3llV3iY0b1r0DFPnYwb0435G414x4A7yfm71fnfnM2wVxOcPaxTHsIcW80uhJ05KkSujQV5IzGiypSJEuL6NuN0z0F8XGUWwBSQJIIWVQPIKQUoOoJwKgFZJxJ8BKI5MXQ9hFMxhgDJVwH5MNJ5MxUujFLxUlOuMCM/2JySx8IoF6IdP6KdJlIi0nFiB4S9JCLkXgJAX+WUVmVWxVLIM2z0Q1JfGci1LE3HwNCsxk36TGJTAmIvFFWNJ9OuQtNsyqnOkzKmIwGNNtOpXtKEkdNPxdMrJuIDIG1zPJX9NPyDIVLDJW25Q0VVOjIxFjNCHjM9FwOZQW0IMVOILmRQKjOWXVP/2uTzRoNpDoJOx2DO1VRYNrjYOMV+FNj1QNTeNDAhJCDMmuFsFdUiJHXwTyhdm12KQyRnhOCLjWKRjG1DVBOvRw0CFiJ4N+IMzi1CwSzUjDX/UQgtBtCI3iMmhWTNIXJhRWP1T9CohjBWU9CVx7Q+I7lQGBFzDWLZ1xwlN8KuKrM9MTGAH/BaVkFlMAm6MIqlJgGLBeSQqmlNEwN21PEmPnUejek70sHkEhgzRWWqzwAATPCzIPkYVglJnbQ1D/KmyJR/Qzi+k5gkLwnw3tDJh/EwmIX/ywKAJC1pRS3XCostn/QXVqi3RoD3D9TfEBHiMTmAMAuZi9EyESRcLFjVDwnjDUtYtgLd0+lQGqBiIsGhFoD5HIEtmYtwALN8nxxEo4oPlvGoQcSU2BL4FQCQkYUtOFPIsmwAumyMoglsB9Egzwj2OV17X9F+1Sj+2NhIATwoHPDJiaHVETAiX7X/Q0H/UrEtSSP0ypyAoDVFCwDbSj35iBEUSV3JFZgeKuyCF2Q8DIH9ShLg0tFkkeTAF6A0PgFqqhmoA50azEAOTrlphWXDR/L/Ui2TVcyOnHir1Gu0UgCNzDCXjCmlFXnXjowYyrypJr2PnJLYwjGYxpObxvjbz4wZK73EB73fmLPnRH32THws1oC4ASXiqbl3B8tKCYRQq4AIrdKIvrNIpyv/FlMgG6l6nsA8LiXEh40yL2QsC4CZBptwDpvLSrgKK4AAG9r1zoWRkaDylQABua9FISMLgPo4W+9CkBs4WgAX04S9Cv1RrEvRpYp0t22xuLGlrxvdOItIQYrJqtz6ipvQGZtZoCEZrNsRQCHZskS5p5vxD5vFo9OFpWVFrYGduIslsulxocoCx/TloVtE1hvEvhonQqP5uVpLIEiitGPplipzNNMuvNM1vRG1tdN1sJuLDIrGgotJvJtZ36joGppvnNoZuZCtvptHBCrKGGytKxSbPNNHGXhiXtry0dv5olpFrFoFqIEDsVs2jRqwAxrjqqlitTp0N9rrMdMTH4QLuNsbBLtputvLqZtLpXurocXYQKKNJyv6tS2brClbsgG5vbpTCdt7tduLHdusEvsgHlvtGDqHrHVHzFAjpRtpL1gVjDFEujvPCBVqvqsauaqVFaqYTYHGtIEZqVJ8Bz2kDrVIFtsnorNoo9P1vRBztkDzrnqNspsXtNvXvpstsIfLQVsgCVs/sBG/riGfv/tBEAYaowCaoiBaq1vAfgcUWgYnNwDgcgZICQfTunpIoNvnrweLoIeXqIYrpIYCCDqgBDqbjDuRXfpjE/t/vnWguTrzQnsEdQb1vooweJrGnztwaLttwkZZpXuIckdIYHvtDUefs0d9O0ZxpQfxrotnsNoprMaXssakbXpsdkcfrmxHPwPbM5WVJ5R7JnJjLUI0NgC0PEB0MXMOzlWO0ElOyYL5FaEeK3MRxJCpT9xIGOBTzjxiChgjhGBhl2B4JjD+AACpXpEAMNwRTI60dqAABP6VNGQ8EKIGIP4aoMAbij6cEf8SAepv4fMSCqxMrIiQMeIRC5YZANplWUCsUaAF8LsHp7gWIUp8phqk2aIgNC4Q+H4BsXqwMAmEaDNa8jSSSQjMeGZ5ucQVCRZvbSAFIZsCSUUTWRQQmFUDuBel+TjPg1CGHOHWgcvIkxjfeTjMk7YgG6vS+ZnUG5ocGgTF5l+ETL58K4cllNlMcv4aEMQYZ2gUgRZOgCl7aclkgHPf51MbiXZ90ckKVJctJ+gzJlVZgnJ1gm7RKWYzAQKn646lUKCrWlK8q6MFDMUUUCwEvGhlWw+e0AgVkLsPyAAaVasUsiu2n8FqtoG1YDX4J2Rwshb4nkCKr1FGrKvQooBNPVVNU0x1oJpnuzqMcootJdY8YYsoiZGqJBfLVyMAQzUYYOiH2aBN0mhpdIHpaUEZdK18AhyBDyIEIwFEmBCbhpP1asCbDTpY0DFZEgHVY1dBMl0GsWq0wAasATzVc1d+18HwG4G6vtCpAkAACYqRKFyikzOjm8eLcB2wernnTLct8A62tW2Hdw9WVZDW2GNAF3OEyH4h7W7LqrBjFKQZfogIY2HZzxxA2AiQABHdcSEHsXZi+Z7KkFCqkIHTe880a7JSDG0ZAeyXqoCPKBtV9zLdAOwE0MQdZk1VS1pWysyeZqxfN0PWID4IELkGVr6E96QKXRQK4W0OLMIQKuYiEq51nCgdsMyJ6/gP5wrd6n1aFr6kk+Fv6xF+vIGq+EG1vdF+kzFoTZkp+pVl+hGt+pMlGkOI+CdsB1x71tBgxiwTB7BshpWvj1VlkEtwTrWqevRrO9EPQMh+Uolklvp2lyl7T3duNo8f1xNlloc+R5+pRxGniXjhrGOmdg1+TtOxT9xkTom3O8IT1yTmMPj6dnNugez5B4T/R2etT35DT4kUlqlnRVAiLv4PThlwz5l9BHAkJglggkMsBBIMAaXB3fVJJ/4fpnwP4bwJQBIFJmF9JjkVcrJy7Tc/lyAfZ3WWseIG1avYWKgeQex5pmIArJr7qrw7mGCAN1Sh6Yp+SYKQmUSISJQb9EYL96bsPUoX9DNLpuMnZvpzrgroZ88AmWgMAVSe1r0Q5w5VtygaUCgLtk4EgUSdQGElojAJro6w2a6qSVGJt+de0bmxFGeCcSxx+JUaWxrMUWWmnVAV78S6dA6xbwMFpj45sT7/2IPUoRmC4JpWq0Cq9QDBSWIYEX70U+MFtCHx8MyGZcxH80cCF2QUanY8lTIZ6K8PgcDq3bb8rISNZ0bk2VdX6QMTZ0IbZoSXZurrsMp3WO6K0bwcjyvSj36uSCkwGxvYG+QsG5jzvLF6G0TL57csIBF9zWjuX+jhXpj/jZX1j6QV2wMT+jrlp7r3ZA7kgC3GvW7SMJ78IU2MtLpCF0MH6nBdQe1Lud9cPTwKa/F54V4RoWSfkIKdoFcrkXoHgg4Y4Nci7UQ/56Ye4OYJ4QwRYGp9QBPIRRAeqy7w8Pg2gBPIFHjdPkPyAAABgEAADY7AABWdtuwGvgADnr4ABYGBK/shaB22SAABmbIEgAAdhr/r9oDsAYEn4EHbcr/r5b+yAEFyDQHsGH/L4MEz5j+z9z/z8g2KboATyaHT6AA -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=assistant-ui/assistant-ui&utm_content=1911):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR