Skip to content

feat: metadata_assistant agent, tool call display, and agent selector UI#1057

Merged
hotlong merged 7 commits into
mainfrom
copilot/ai-agent
Apr 2, 2026
Merged

feat: metadata_assistant agent, tool call display, and agent selector UI#1057
hotlong merged 7 commits into
mainfrom
copilot/ai-agent

Conversation

Copilot AI commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

The AI Agent architecture (Agent → Skill → Tool) and metadata tools exist but aren't wired to a usable agent, the chat panel doesn't render tool calls, and requiresConfirmation has no frontend implementation.

New metadata_assistant agent

  • Binds all 6 metadata tools: create_object, add_field, modify_field, delete_field, list_metadata_objects, describe_metadata_object
  • System prompt tuned for schema design: enforces snake_case, verifies existing schemas before mutation, warns on destructive ops
  • react planning with 10 iterations and replan enabled for multi-step schema conversations

Agent and metadata tools auto-registration in AIServicePlugin

  • AIServicePlugin.start() now registers both the metadata_assistant agent and all 6 metadata tools via the metadata service
  • Metadata tools only require IMetadataService (not IDataEngine), so they are registered independently of data tools
  • Agent is stored as metadata via metadataService.register('agent', ...), making it modifiable at runtime without redeployment

Agent list endpoint and agent selector UI

  • New GET /api/v1/ai/agents endpoint returns all active agents with { name, label, role } summaries
  • AgentRuntime.listAgents() reads from the metadata service and filters to active agents only
  • AiChatPanel header includes an agent selector dropdown that dynamically switches the chat transport URL:
    • General ChatPOST /api/v1/ai/chat
    • Data AssistantPOST /api/v1/ai/agents/data_chat/chat
    • Metadata AssistantPOST /api/v1/ai/agents/metadata_assistant/chat
  • Agent selection persisted to localStorage (objectstack:ai-chat-agent)
  • Persisted agent selection is validated against the fetched agent list on load — automatically falls back to General Chat if the previously selected agent is no longer available or inactive
  • Conversation is automatically cleared when switching agents to avoid context confusion
  • Selector is disabled during streaming

requiresConfirmation deferred

  • requiresConfirmation is intentionally kept at false (default) on create_object and delete_field because the server-side tool-call loop (AIService.chatWithTools/streamChatWithTools) executes tool calls immediately via ToolRegistry.executeAll without checking this flag
  • The flag should only be set once server-side approval gating is implemented to avoid a false sense of safety
  • Read-only and additive tools remain unconfirmed as before

AiChatPanel tool invocation rendering

Renders Vercel AI SDK v6 dynamic-tool message parts with state-driven UI:

State Visual
input-streaming / input-available Spinner + tool name + arg summary
approval-requested Yellow card with Approve/Deny buttons
output-available Green success + truncated result
output-error Red error + message
output-denied Muted denied indicator
  • formatToolArgs() and formatToolOutput() are guarded against JSON.stringify edge cases (undefined return, bigint throws) with try/catch and String() fallback to prevent runtime crashes

Approval flow uses addToolApprovalResponse from useChat:

addToolApprovalResponse({ id: approvalId, approved: true })
// or
addToolApprovalResponse({ id: approvalId, approved: false, reason: 'User denied the operation' })

Tests

  • 17 new service-ai tests: agent definition, tool references, guardrails, requiresConfirmation flags, listAgents filtering, agent list route
  • 12 new studio tests: tool invocation part persistence round-trip, chatApiUrl routing, agent selection persistence

@vercel

vercel Bot commented Apr 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectstack-play Ready Ready Preview, Comment Apr 2, 2026 8:41am
spec Ready Ready Preview, Comment Apr 2, 2026 8:41am

Request Review

…el tool display

- Create metadata_assistant agent with 6 metadata tools and system prompt
- Add requiresConfirmation: true to create_object and delete_field tools
- Enhance AiChatPanel to render tool invocation parts (calling/success/error/denied)
- Implement approval-requested UI with Approve/Deny buttons using addToolApprovalResponse
- Add tests for new agent, tool flags, and tool part persistence
- Update CHANGELOG.md

Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/8a978a0a-5a20-45f0-b841-51f0a8857945

Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
… extract output formatter

Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/8a978a0a-5a20-45f0-b841-51f0a8857945

Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Copilot AI changed the title [WIP] 完善 AI Agent 模式以支持对话创建和管理对象 feat: metadata_assistant agent, tool call display, and confirmation UI Apr 2, 2026
Copilot AI requested a review from xuyushun441-sys April 2, 2026 07:24
@xuyushun441-sys

Copy link
Copy Markdown
Contributor

@copilot 评估是否完成了任务中要求的所有开发

Copilot AI commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot 评估是否完成了任务中要求的所有开发

Issue 要求的 3 项开发任务均已完成:

1. 创建 Agent 绑定元数据工具

  • 新建 metadata_assistant agent (metadata-assistant-agent.ts),绑定全部 6 个元数据工具(create_objectadd_fieldmodify_fielddelete_fieldlist_metadata_objectsdescribe_metadata_object
  • 包含 10 条 system prompt 指引(snake_case 命名、先查后改、破坏性操作警告等)
  • 配置 react 规划策略(maxIterations: 10, allowReplan: true)

2. 前端展示工具调用过程

  • AiChatPanel 新增 ToolInvocationDisplay 组件,渲染 Vercel AI SDK v6 的 dynamic-tool 消息部分
  • 支持 5 种状态:调用中(Spinner)、待确认(黄色卡片)、成功(绿色)、失败(红色)、已拒绝(灰色)
  • 展示工具参数摘要和返回结果截断显示

3. 实现操作确认机制

  • create_objectdelete_field 设置 requiresConfirmation: true
  • 前端 approval-requested 状态渲染确认卡片,包含 Approve/Deny 按钮
  • 使用 addToolApprovalResponse 将用户决策发送回服务端

测试覆盖:新增 14 个测试(11 service-ai + 3 studio),全部通过。service-ai 中 6 个预存失败(ai-service.test.ts 缺少 manifest mock)与本次改动无关。

@hotlong

hotlong commented Apr 2, 2026

Copy link
Copy Markdown
Collaborator

我在前端要怎么使用这个agent,还有agent是否应该按照元数据的方式来定义? @copilot

Copilot AI commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

我在前端要怎么使用这个agent,还有agent是否应该按照元数据的方式来定义? @copilot

前端使用方式:

Agent 通过 REST 端点调用,路由已在 buildAgentRoutes() 中注册:

POST /api/v1/ai/agents/metadata_assistant/chat
{
  "messages": [{ "role": "user", "content": "创建一个 contracts 表" }],
  "context": { "objectName": "contract" }
}

当前 AiChatPanel 使用的是通用 /api/v1/ai/chat 端点。要切换到 agent 模式,只需修改 transport URL:

const transport = useMemo(
  () => new DefaultChatTransport({ api: `${baseUrl}/api/v1/ai/agents/metadata_assistant/chat` }),
  [baseUrl],
);

后续可以在 AiChatPanel 中加一个 agent 选择器(dropdown),动态切换 transport URL。

Agent 是否按元数据方式定义:

是的,Agent 已经按照元数据模式定义。METADATA_ASSISTANT_AGENT 常量只是默认初始值,在 plugin 启动时通过 metadataService.register('agent', ...) 注册到元数据服务中。AgentRuntime.loadAgent() 运行时从元数据服务读取,因此管理员可以通过元数据服务修改 agent 配置(instructions、tools、model 等),无需改代码重新部署。

之前的代码有一个遗漏:只注册了 data_chat agent,没有注册 metadata_assistant 和它的 metadata tools。已在 e7bc0b9 中修复 — AIServicePlugin.start() 现在同时注册 metadata tools 和 metadata_assistant agent。

@hotlong

hotlong commented Apr 2, 2026

Copy link
Copy Markdown
Collaborator

@copilot agent 选择器 现在就得加上,并进行完整的功能测试,确保我可用。

Copilot AI commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot agent 选择器 现在就得加上,并进行完整的功能测试,确保我可用。

已在 ed14650 中实现 agent 选择器。

改动:

  1. 后端:新增 GET /api/v1/ai/agents 端点(返回 { agents: [{ name, label, role }] }),由 AgentRuntime.listAgents() 从元数据服务读取所有 active agent。

  2. 前端:AiChatPanel header 下方添加 Select 下拉框(data-testid="agent-selector"),选项包含:

    • General Chat → /api/v1/ai/chat
    • Data Assistant → /api/v1/ai/agents/data_chat/chat
    • Metadata Assistant → /api/v1/ai/agents/metadata_assistant/chat
  3. 行为

    • 选择持久化到 localStorage(key: objectstack:ai-chat-agent
    • 切换 agent 时自动清空对话(避免上下文混乱)
    • Streaming 期间禁用切换

测试:新增 15 个测试(6 service-ai + 9 studio),全部通过。

Copilot AI changed the title feat: metadata_assistant agent, tool call display, and confirmation UI feat: metadata_assistant agent, tool call display, agent selector, and confirmation UI Apr 2, 2026
@hotlong
hotlong marked this pull request as ready for review April 2, 2026 08:17
Copilot AI review requested due to automatic review settings April 2, 2026 08:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR wires the existing Agent → Tool architecture into a usable metadata_assistant agent, exposes an agent listing endpoint for the Studio UI, and enhances the Studio AiChatPanel to support agent selection and rendering of Vercel AI SDK v6 tool-invocation message parts.

Changes:

  • Add metadata_assistant built-in agent and export/register it through service-ai.
  • Introduce GET /api/v1/ai/agents and AgentRuntime.listAgents() for an agent selector UI.
  • Update Studio AiChatPanel to support agent selection + tool invocation rendering, and add corresponding tests.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/services/service-ai/src/tools/delete-field.tool.ts Marks delete_field as requiresConfirmation.
packages/services/service-ai/src/tools/create-object.tool.ts Marks create_object as requiresConfirmation.
packages/services/service-ai/src/routes/agent-routes.ts Adds GET /api/v1/ai/agents route.
packages/services/service-ai/src/plugin.ts Registers metadata tools + metadata_assistant agent when metadata service is available.
packages/services/service-ai/src/index.ts Re-exports METADATA_ASSISTANT_AGENT.
packages/services/service-ai/src/agents/metadata-assistant-agent.ts Adds built-in metadata_assistant agent definition.
packages/services/service-ai/src/agents/index.ts Exports METADATA_ASSISTANT_AGENT.
packages/services/service-ai/src/agent-runtime.ts Adds listAgents() for active agent summaries.
packages/services/service-ai/src/tests/metadata-tools.test.ts Tests requiresConfirmation flags and tool metadata expectations.
packages/services/service-ai/src/tests/chatbot-features.test.ts Adds tests for listAgents() and GET agents route + metadata assistant agent spec.
CHANGELOG.md Documents new agent, UI tool-call rendering, and confirmation flags.
apps/studio/test/ai-chat-panel.test.tsx Adds tests for tool-part persistence and agent selection routing/persistence.
apps/studio/src/components/AiChatPanel.tsx Implements agent selector, tool invocation rendering, and approval UI wiring.

'Use this when the user wants to create a new entity, table, or data model.',
category: 'data',
builtIn: true,
requiresConfirmation: true,

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requiresConfirmation: true is set here, but the AI service currently executes tool calls immediately in AIService.chatWithTools/streamChatWithTools via ToolRegistry.executeAll without checking this flag, and the stream encoder never emits an approval/deny state. This means create_object will still run without any user confirmation. Either implement a server-side approval gate for tools marked requiresConfirmation (emit an approval-requested part and wait for client response) or remove the flag until it’s enforced end-to-end.

Suggested change
requiresConfirmation: true,

Copilot uses AI. Check for mistakes.
'Use this when the user explicitly wants to remove an attribute or column from a table.',
category: 'data',
builtIn: true,
requiresConfirmation: true,

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as create_object: requiresConfirmation: true is declared but not enforced anywhere in the service’s tool-call loop or streaming protocol, so delete_field will execute immediately when the model requests it. Please add server-side confirmation handling for requiresConfirmation tools (so the UI can approve/deny) or drop the flag to avoid a false sense of safety.

Suggested change
requiresConfirmation: true,

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +69
function formatToolArgs(input: unknown): string {
if (!input || typeof input !== 'object') return '';
const entries = Object.entries(input as Record<string, unknown>);
if (entries.length === 0) return '';
return entries
.slice(0, 4)
.map(([k, v]) => {
const val = typeof v === 'string' ? v : JSON.stringify(v);
const display = val.length > 30 ? val.slice(0, 30) + '…' : val;
return `${k}: ${display}`;
})
.join(', ');

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatToolArgs() can throw at runtime: JSON.stringify(v) returns undefined for undefined/functions (and throws for bigint), but the code assumes val is always a string and accesses val.length. Consider coercing non-stringified values via String(v) (and catching stringify errors) so a single unexpected arg doesn’t crash the chat panel render.

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +85
function formatToolOutput(output: unknown, maxLen = 80): string {
const raw = typeof output === 'string' ? output : JSON.stringify(output);
return raw.length > maxLen ? raw.slice(0, maxLen) + '…' : raw;
}

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatToolOutput() has the same failure mode as formatToolArgs: JSON.stringify(output) can return undefined (or throw) but the code unconditionally reads raw.length. Guard against undefined/non-serializable outputs (fallback to empty string / String(output)) to avoid crashing when part.output is missing or not JSON-serializable.

Copilot uses AI. Check for mistakes.
Comment on lines +281 to 292
const [selectedAgent, setSelectedAgent] = useState<string>(loadSelectedAgent);
const scrollRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(null);
const baseUrl = getApiBaseUrl();
const { agents, loading: agentsLoading } = useAgentList(baseUrl);

const initialMessages = useMemo(() => loadMessages() as UIMessage[], []);

const transport = useMemo(
() => new DefaultChatTransport({ api: `${baseUrl}/api/v1/ai/chat` }),
[baseUrl],
() => new DefaultChatTransport({ api: chatApiUrl(baseUrl, selectedAgent) }),
[baseUrl, selectedAgent],
);

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Persisted selectedAgent is used to build the transport URL immediately, but the value is never validated against the fetched agent list. If the server doesn’t expose agent routes (no metadata service) or the previously selected agent is inactive/removed, the panel will keep calling /api/v1/ai/agents/{agent}/chat and fail until the user manually switches back. Suggest: once agentsLoading finishes, if selectedAgent isn’t __general__ and isn’t in agents, automatically fall back to __general__ (and update localStorage).

Copilot uses AI. Check for mistakes.
@hotlong

hotlong commented Apr 2, 2026

Copy link
Copy Markdown
Collaborator

@copilot Fix all

…olOutput, validate agent selection, remove unenforced requiresConfirmation

Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/f75e2ec2-9b8a-4e71-916e-8193b834ad01

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>

Copilot AI commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Fix all

All 5 review issues fixed in bbdcad9:

  1. formatToolArgs() crash — guarded JSON.stringify() with try/catch and String(v) fallback for undefined/bigint/non-serializable values
  2. formatToolOutput() crash — same guard pattern for output rendering
  3. Agent validation fallback — added useEffect that auto-resets to General Chat when the persisted agent is not in the fetched list (e.g. removed/inactive)
  4. create_object requiresConfirmation — removed flag (server-side chatWithTools executes tools immediately without checking it)
  5. delete_field requiresConfirmation — same removal with documentation comment

All 146 service-ai tests + 21 studio tests pass.

Copilot AI changed the title feat: metadata_assistant agent, tool call display, agent selector, and confirmation UI feat: metadata_assistant agent, tool call display, and agent selector UI Apr 2, 2026
@hotlong
hotlong merged commit 32c86b8 into main Apr 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

完善 AI Agent 模式:支持通过对话创建和管理对象

4 participants