fix: capitalize dashboard tool labels (#1761)

* fix: capitalize tool labels in dashboard

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: sentence-case dashboard tool labels

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: Ramon Nogueira <270434257+ramon-langchain@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
(cherry picked from commit dd5b7becb4dc9c633730fb4092a3cbd0c50dae9d)

Co-authored-by: Ramon Nogueira <ramon.nogueira@langchain.dev>
This commit is contained in:
Adam Moussa 2026-07-17 16:22:38 -04:00
parent 58941d1edd
commit 3b3d45cd94
No known key found for this signature in database
5 changed files with 33 additions and 5 deletions

View file

@ -36,4 +36,14 @@ describe("formatToolDisplay", () => {
it("normalizes write_todos", () => {
expect(formatToolDisplay("write todos", "other", {}, projectPath)).toBe("Update todos");
});
it("sentence-cases raw tool names", () => {
expect(formatToolDisplay("enter_plan_mode", "other", {}, projectPath)).toBe(
"Enter plan mode",
);
expect(formatToolDisplay("save_plan", "other", {}, projectPath)).toBe("Save plan");
expect(formatToolDisplay("slack_thread_reply", "other", {}, projectPath)).toBe(
"Slack thread reply",
);
});
});

View file

@ -1,3 +1,4 @@
import { humanizeToolName } from "@/features/agents/lib/toolNames";
import type { AcpToolKind } from "@/features/agents/lib/types";
function stripProjectPath(path: string, projectPath?: string): string {
@ -27,7 +28,15 @@ function normalizedToolName(title: string): string {
}
function humanizeToolTitle(title: string): string {
return title.replace(/_/g, " ").trim() || "Tool";
const trimmed = title.trim();
if (!trimmed) return "Tool";
const [name, ...rest] = trimmed.split(/\s+/);
const suffix = rest.join(" ");
if (name && suffix && /^(?:[./~]|[a-z]+:\/\/)/i.test(suffix)) {
return `${humanizeToolName(name)} ${suffix}`;
}
return humanizeToolName(trimmed);
}
export function formatToolDisplay(

View file

@ -1,9 +1,7 @@
import { useStreamContext as useAgentThreadStream, useToolCalls } from "@langchain/react"
import { Check, Loader2, X } from "lucide-react"
function humanizeToolName(name: string): string {
return name.replace(/_/g, " ").trim() || "tool"
}
import { humanizeToolName } from "@/features/agents/lib/toolNames"
/**
* Live status for a single subagent, read straight from the SDK's scoped

View file

@ -1,5 +1,6 @@
import { AIMessage, HumanMessage, ToolMessage } from "@langchain/core/messages";
import { messageArrivalTimestamp } from "./messageTimestamps";
import { humanizeToolName } from "./toolNames";
import type { BaseMessage, ContentBlock } from "@langchain/core/messages";
import type { AssembledToolCall, SubagentDiscoverySnapshot } from "@langchain/react";
@ -39,7 +40,7 @@ function toolTitle(name: string, args: Record<string, unknown>): string {
if (typeof command === "string" && command.trim()) {
return command.trim().split("\n")[0]?.slice(0, 120) ?? "";
}
return name.replace(/_/g, " ").trim() || "Tool";
return humanizeToolName(name);
}
function parseToolArgs(raw: unknown): Record<string, unknown> {

View file

@ -0,0 +1,10 @@
export function humanizeToolName(name: string, fallback = "Tool"): string {
const normalized = name
.replace(/[_-]+/g, " ")
.trim()
.replace(/\s+/g, " ")
.toLowerCase()
if (!normalized) return fallback
return `${normalized.charAt(0).toUpperCase()}${normalized.slice(1)}`
}