Skip to content

Commit ad3ae41

Browse files
improvement: add detailed output metadata to frontend tools (#6720)
## 📝 Summary <!-- Provide a concise summary of what this pull request is addressing. If this PR fixes any issues, list them here by number (e.g., Fixes #123). --> This adds detailed output to the frontend tools framework mirroring the output structure of tools in the backend. ## 🔍 Description of Changes <!-- Detail the specific changes made in this pull request. Explain the problem addressed and how it was resolved. If applicable, provide before and after comparisons, screenshots, or any relevant details to help reviewers understand the changes easily. --> - Introduces ToolOutputBase interface and schema to align frontend tools with the backend's SuccessResult pattern from marimo/_ai/_tools/types.py. - Updates sample-tool to use this structure - Updates tests to use this structure so they won't break ## 📋 Checklist - [x] I have read the [contributor guidelines](https://github.com/marimo-team/marimo/blob/main/CONTRIBUTING.md). - [ ] For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on [Discord](https://marimo.io/discord?ref=pr), or the community [discussions](https://github.com/marimo-team/marimo/discussions) (Please provide a link if applicable). - [x] I have added tests for the changes made. - [x] I have run the code and verified that it works as expected. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 185a1f0 commit ad3ae41

File tree

3 files changed

+80
-11
lines changed

3 files changed

+80
-11
lines changed

frontend/src/core/ai/tools/__tests__/registry.test.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,22 @@ describe("FrontendToolRegistry", () => {
1313

1414
it("invokes a tool with valid args and validates input/output", async () => {
1515
const registry = new FrontendToolRegistry([new TestFrontendTool()]);
16-
const ok = await registry.invoke("test_frontend_tool", { name: "Alice" });
17-
expect(ok).toEqual({ message: "Hello: Alice" });
16+
const result = await registry.invoke("test_frontend_tool", {
17+
name: "Alice",
18+
});
19+
20+
expect(result).toMatchObject({
21+
status: "success",
22+
data: {
23+
greeting: "Hello: Alice",
24+
},
25+
next_steps: expect.arrayContaining([expect.any(String)]),
26+
});
27+
28+
// Verify timestamp is present and valid
29+
const output = result as { data: { timestamp: string } };
30+
expect(output.data.timestamp).toBeDefined();
31+
expect(typeof output.data.timestamp).toBe("string");
1832
});
1933

2034
it("returns a structured error on invalid args", async () => {

frontend/src/core/ai/tools/base.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,43 @@
11
/* Copyright 2024 Marimo. All rights reserved. */
22

3-
import type { z } from "zod";
3+
import { z } from "zod";
44
import type { CopilotMode } from "./registry";
55

6+
/**
7+
* Status value for tool responses, mirroring status value in marimo/_ai/_tools/types.py
8+
*/
9+
export type StatusValue = "success" | "error" | "warning";
10+
11+
/**
12+
* Base interface for tool output responses.
13+
* Mirrors the SuccessResult dataclass from marimo/_ai/_tools/types.py
14+
*
15+
* Tool outputs should extend this interface to include standardized
16+
* metadata like next_steps, messages, and status information.
17+
*/
18+
export interface ToolOutputBase {
19+
status?: StatusValue;
20+
auth_required?: boolean;
21+
next_steps?: string[];
22+
action_url?: string;
23+
message?: string;
24+
meta?: Record<string, unknown>;
25+
}
26+
27+
/**
28+
* Base Zod schema for tool outputs.
29+
*
30+
* Tool output schemas should extend this using .extend() to add their specific fields.
31+
*/
32+
export const toolOutputBaseSchema = z.object({
33+
status: z.enum(["success", "error", "warning"]).optional(),
34+
auth_required: z.boolean().optional(),
35+
next_steps: z.array(z.string()).optional(),
36+
action_url: z.string().optional(),
37+
message: z.string().optional(),
38+
meta: z.record(z.string(), z.unknown()).optional(),
39+
});
40+
641
/**
742
* Contract for a frontend tool.
843
*
Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* Copyright 2024 Marimo. All rights reserved. */
22

33
import { z } from "zod";
4-
import type { AiTool } from "./base";
4+
import { type AiTool, type ToolOutputBase, toolOutputBaseSchema } from "./base";
55
import type { CopilotMode } from "./registry";
66

77
const description = `
@@ -11,26 +11,46 @@ Args:
1111
- name (string): The name to include in the greeting.
1212
1313
Returns:
14-
- { message: string } — The greeting message, e.g., "Hello: Alice".
14+
- Output with data containing the greeting message.
1515
`;
1616

1717
interface Input {
1818
name: string;
1919
}
2020

21-
interface Output {
22-
message: string;
21+
interface GreetingData {
22+
greeting: string;
23+
timestamp: string;
2324
}
2425

25-
/** A sample frontend tool that returns "hello world" */
26+
interface Output extends ToolOutputBase {
27+
data: GreetingData;
28+
}
29+
30+
/** A sample frontend tool that demonstrates real tool output structure */
2631
export class TestFrontendTool implements AiTool<Input, Output> {
2732
readonly name = "test_frontend_tool";
2833
readonly description = description;
2934
readonly schema = z.object({ name: z.string() });
30-
readonly outputSchema = z.object({ message: z.string() });
35+
readonly outputSchema = toolOutputBaseSchema.extend({
36+
data: z.object({
37+
greeting: z.string(),
38+
timestamp: z.string(),
39+
}),
40+
});
3141
readonly mode: CopilotMode[] = ["ask"];
3242

33-
async handler({ name }: Input) {
34-
return { message: `Hello: ${name}` };
43+
async handler({ name }: Input): Promise<Output> {
44+
return {
45+
status: "success",
46+
data: {
47+
greeting: `Hello: ${name}`,
48+
timestamp: new Date().toISOString(),
49+
},
50+
next_steps: [
51+
"You can now proceed with your next task",
52+
"Try calling another tool if needed",
53+
],
54+
};
3555
}
3656
}

0 commit comments

Comments
 (0)