refactor: improve write_todos tool and UI components

- Refactored the write_todos tool to enhance argument and result schemas using Zod for better validation and type safety.
- Updated the WriteTodosToolUI to streamline the rendering logic and improve loading states, ensuring a smoother user experience.
- Enhanced the Plan and TodoItem components to better handle streaming states and display progress, providing clearer feedback during task management.
- Cleaned up code formatting and structure for improved readability and maintainability.
This commit is contained in:
Anish Sarkar 2025-12-26 17:49:56 +05:30
parent 2c86287264
commit ebc04f590e
18 changed files with 833 additions and 751 deletions

View file

@ -1,5 +1,4 @@
import { z } from "zod";
import { ActionSchema } from "../shared/schema";
/**
* Todo item status
@ -11,10 +10,10 @@ export type TodoStatus = z.infer<typeof TodoStatusSchema>;
* Single todo item in a plan
*/
export const PlanTodoSchema = z.object({
id: z.string(),
label: z.string(),
status: TodoStatusSchema,
description: z.string().optional(),
id: z.string(),
label: z.string(),
status: TodoStatusSchema,
description: z.string().optional(),
});
export type PlanTodo = z.infer<typeof PlanTodoSchema>;
@ -23,12 +22,12 @@ export type PlanTodo = z.infer<typeof PlanTodoSchema>;
* Serializable plan schema for tool results
*/
export const SerializablePlanSchema = z.object({
id: z.string(),
title: z.string(),
description: z.string().optional(),
todos: z.array(PlanTodoSchema).min(1),
maxVisibleTodos: z.number().optional(),
showProgress: z.boolean().optional(),
id: z.string(),
title: z.string(),
description: z.string().optional(),
todos: z.array(PlanTodoSchema).min(1),
maxVisibleTodos: z.number().optional(),
showProgress: z.boolean().optional(),
});
export type SerializablePlan = z.infer<typeof SerializablePlanSchema>;
@ -37,31 +36,31 @@ export type SerializablePlan = z.infer<typeof SerializablePlanSchema>;
* Parse and validate a serializable plan from tool result
*/
export function parseSerializablePlan(data: unknown): SerializablePlan {
const result = SerializablePlanSchema.safeParse(data);
if (!result.success) {
console.warn("Invalid plan data:", result.error.issues);
// Try to extract basic info for fallback
const obj = (data && typeof data === "object" ? data : {}) as Record<string, unknown>;
return {
id: typeof obj.id === "string" ? obj.id : "unknown",
title: typeof obj.title === "string" ? obj.title : "Plan",
description: typeof obj.description === "string" ? obj.description : undefined,
todos: Array.isArray(obj.todos)
? obj.todos.map((t, i) => ({
id: typeof (t as any)?.id === "string" ? (t as any).id : `todo-${i}`,
label: typeof (t as any)?.label === "string" ? (t as any).label : "Task",
status: TodoStatusSchema.safeParse((t as any)?.status).success
? (t as any).status
: "pending",
description: typeof (t as any)?.description === "string" ? (t as any).description : undefined,
}))
: [{ id: "1", label: "No tasks", status: "pending" as const }],
};
}
return result.data;
}
const result = SerializablePlanSchema.safeParse(data);
if (!result.success) {
console.warn("Invalid plan data:", result.error.issues);
// Try to extract basic info for fallback
const obj = (data && typeof data === "object" ? data : {}) as Record<string, unknown>;
return {
id: typeof obj.id === "string" ? obj.id : "unknown",
title: typeof obj.title === "string" ? obj.title : "Plan",
description: typeof obj.description === "string" ? obj.description : undefined,
todos: Array.isArray(obj.todos)
? obj.todos.map((t, i) => ({
id: typeof (t as any)?.id === "string" ? (t as any).id : `todo-${i}`,
label: typeof (t as any)?.label === "string" ? (t as any).label : "Task",
status: TodoStatusSchema.safeParse((t as any)?.status).success
? (t as any).status
: "pending",
description:
typeof (t as any)?.description === "string" ? (t as any).description : undefined,
}))
: [{ id: "1", label: "No tasks", status: "pending" as const }],
};
}
return result.data;
}