feat: update version to 0.0.4 and add formatToolLabel utility for tool call label overrides

This commit is contained in:
Xi Zhang
2026-06-04 02:02:19 +01:00
parent 282352deb7
commit 2b1eae9b23
4 changed files with 92 additions and 5 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@evoscientist/webui",
"version": "0.0.3",
"version": "0.0.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@evoscientist/webui",
"version": "0.0.3",
"version": "0.0.4",
"license": "Apache-2.0",
"dependencies": {
"@langchain/core": "1.1.19",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@evoscientist/webui",
"version": "0.0.3",
"version": "0.0.4",
"description": "Web UI for EvoScientist — a self-evolving AI scientist built on DeepAgents/LangGraph.",
"author": "Xi Zhang <zacharyzhang2022@gmail.com>",
"type": "module",
+13 -2
View File
@@ -21,6 +21,7 @@ import { ToolCall, ActionRequest, ReviewConfig } from "@/app/types/types";
import { cn } from "@/lib/utils";
import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui";
import { ToolApprovalInterrupt } from "@/app/components/ToolApprovalInterrupt";
import { formatToolLabel } from "@/lib/toolLabel";
interface ToolCallBoxProps {
toolCall: ToolCall;
@@ -70,9 +71,19 @@ export const ToolCallBox = React.memo<ToolCallBoxProps>(
}, [actionRequest, autoApprove]);
const { name, args, result, status } = useMemo(() => {
// Streaming can deliver args as a (possibly partial) JSON string, not an
// object — treat it as `unknown` and only expose a real object to the
// args view, so Object.keys/entries never run on a string.
const rawArgs: unknown = toolCall.args;
return {
name: toolCall.name || "Unknown Tool",
args: toolCall.args || {},
// Targeted label overrides only (read_file/write_file/edit_file on
// /memories → "Reading memory"/"Updating memory", think_tool →
// "Reflection"); every other tool keeps its raw name.
name: formatToolLabel(toolCall.name, rawArgs),
args:
rawArgs && typeof rawArgs === "object"
? (rawArgs as Record<string, unknown>)
: {},
result: toolCall.result,
status: toolCall.status || "completed",
};
+76
View File
@@ -0,0 +1,76 @@
/**
* Targeted tool-call label overrides.
*
* We deliberately do NOT reformat tool calls in general (no `ls(path)` /
* `execute(cmd)` argument summaries). Only a few specific names are remapped;
* every other tool keeps its raw name.
*
* Overrides:
* - read_file / write_file / edit_file on a `/memories/...` path →
* "Reading memory" / "Updating memory".
* - read_file on a `/skills/<name>/...` path → "Skill: <name>".
* - think_tool → "Reflection".
* - the tool-selector's internal ToolSelectionResponse → "Adaptive tool
* selection".
*/
/** True when a virtual path targets the global memories directory. */
function isMemoryPath(path: string): boolean {
const p = (path || "").trim();
return p === "/memories" || p.startsWith("/memories/");
}
/** If a virtual path targets a skill (`/skills/<name>/...`), return `<name>`.
* An empty first segment (e.g. `/skills//x`) is treated as no match. */
function skillNameFromPath(path: string): string {
const p = (path || "").trim();
if (!p.startsWith("/skills/")) return "";
const [skill] = p.slice("/skills/".length).split("/");
return skill || "";
}
/** Coerce tool args to an object — they may stream in as a JSON string. */
function asArgsObject(args: unknown): Record<string, unknown> {
if (args && typeof args === "object") return args as Record<string, unknown>;
if (typeof args === "string") {
try {
const parsed = JSON.parse(args);
if (parsed && typeof parsed === "object") {
return parsed as Record<string, unknown>;
}
} catch {
// Partial / non-JSON args while still streaming — treat as empty.
}
}
return {};
}
export function formatToolLabel(name: string, args?: unknown): string {
if (!name) return "Unknown Tool";
// The tool-selector middleware's internal structured-output call.
if (name === "ToolSelectionResponse") return "Adaptive tool selection";
const nameLower = name.toLowerCase();
if (nameLower === "think_tool") return "Reflection";
if (
nameLower === "read_file" ||
nameLower === "write_file" ||
nameLower === "edit_file"
) {
const a = asArgsObject(args);
const path = String(a.path ?? a.file_path ?? "");
if (isMemoryPath(path)) {
return nameLower === "read_file" ? "Reading memory" : "Updating memory";
}
if (nameLower === "read_file") {
const skill = skillNameFromPath(path);
if (skill) return `Skill: ${skill}`;
}
}
// Everything else keeps its raw tool name.
return name;
}