ToolApprovalCard

Action

도구 승인·interrupt 요청에 연결하는 카드.

Usage

assistant-ui의 tool-call part 승인(approval) 또는 interrupt 요청을 카드로 연결할 때 사용한다.

import

import

import { ToolApprovalCard } from '@mildang/design-system/unofficial/Chat';

예제를 복사해 쓸 때 필요한 준비

@mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.

API Reference

ToolApprovalCard Props

Prop

Type

Default

addResult
필수

(result: unknown) => void

지정 안 함

args
필수

any

지정 안 함

argsText
필수

string

지정 안 함

respondToApproval
필수

(response: ToolApprovalResponse) => void

지정 안 함

resume
필수

(payload: unknown) => void

지정 안 함

status
필수

{ readonly type: "running"; } | { readonly type: "complete"; } | { readonly type: "incomplete"; readonly reason: "error" | "other" | "length" | "cancelled" | "content-filter"; readonly error?: unknown; } | { readonly type: "requires-action"; readonly reason: "interrupt" | "tool-calls"; } | { readonly type: "incomplete"; readonly reason: "tool-calls"; readonly error?: ReadonlyJSONValue; }

지정 안 함

toolCallId
필수

string

지정 안 함

toolName
필수

string

지정 안 함

type
필수

"tool-call"

지정 안 함

approval

{ readonly id: string; readonly approved?: boolean; readonly reason?: string; readonly isAutomatic?: boolean; readonly options?: readonly ToolApprovalOption[]; readonly optionId?: string; readonly resolution?: "cancelled" | "expired"; }

지정 안 함

artifact

unknown

지정 안 함

className

string

지정 안 함

description

ReactNode

지정 안 함

icon

ReactNode

지정 안 함

interrupt

{ type: "human"; payload: unknown; }

지정 안 함

isError

boolean

지정 안 함

mcp

ToolCallMessagePartMcpMetadata

지정 안 함

messages

readonly ThreadMessage[]

지정 안 함

modelContent

readonly ToolModelContentPart[]

지정 안 함

parentId

string

지정 안 함

providerMetadata

PartProviderMetadata

지정 안 함

result

unknown

지정 안 함

timing

ToolCallTiming

지정 안 함

title

ReactNode

지정 안 함

같은 패밀리

@mildang/design-system/unofficial/Chat 에서 같이 내보내는 컴포넌트다.

예제

request — 옵션 있음

approval.options가 있으면 옵션 그대로 버튼이 되고 allow 계열 첫 항목이 강조된다.

import { ToolApprovalCard } from '@mildang/design-system/unofficial/Chat';
import { ToolApprovalCardProps } from '@mildang/design-system/unofficial/Chat';
import { css } from '@mildang/styled-system/css';

const wrapper = css({ padding: '16', maxWidth: '400px' });

// 데모용 최소 part 필드만 지정하고 캐스팅한다(Tool 스토리와 동일 패턴).
const partProps = (o: Partial<ToolApprovalCardProps>): ToolApprovalCardProps =>
  ({
    type: 'tool-call',
    toolCallId: 'demo',
    toolName: 'run_command',
    args: { command: 'pnpm test --filter design-system' },
    argsText: 'pnpm test --filter design-system',
    status: { type: 'requires-action', reason: 'tool-calls' },
    addResult: () => {},
    resume: () => {},
    respondToApproval: () => {},
    ...o,
  }) as ToolApprovalCardProps;

const ApprovalCardRequestWithOptionsExample = () => (
    <div className={wrapper}>
      <ToolApprovalCard
        {...partProps({
          approval: {
            id: 'ap-1',
            options: [
              { id: 'o-reject', kind: 'reject-once' },
              { id: 'o-always', kind: 'allow-always' },
              { id: 'o-once', kind: 'allow-once' },
            ],
          },
        })}
      />
    </div>
  );

export default ApprovalCardRequestWithOptionsExample;

request — 허용/거부 기본

options가 없으면 허용/거부 기본 쌍으로 떨어지는 예시다.

import { ToolApprovalCard } from '@mildang/design-system/unofficial/Chat';
import { ToolApprovalCardProps } from '@mildang/design-system/unofficial/Chat';
import { css } from '@mildang/styled-system/css';

const wrapper = css({ padding: '16', maxWidth: '400px' });

// 데모용 최소 part 필드만 지정하고 캐스팅한다(Tool 스토리와 동일 패턴).
const partProps = (o: Partial<ToolApprovalCardProps>): ToolApprovalCardProps =>
  ({
    type: 'tool-call',
    toolCallId: 'demo',
    toolName: 'run_command',
    args: { command: 'pnpm test --filter design-system' },
    argsText: 'pnpm test --filter design-system',
    status: { type: 'requires-action', reason: 'tool-calls' },
    addResult: () => {},
    resume: () => {},
    respondToApproval: () => {},
    ...o,
  }) as ToolApprovalCardProps;

const ApprovalCardRequestAllowDenyExample = () => (
    <div className={wrapper}>
      <ToolApprovalCard {...partProps({ approval: { id: 'ap-2' } })} />
    </div>
  );

export default ApprovalCardRequestAllowDenyExample;

running

승인 직후 도구가 실행 중인 상태다. 버튼은 사라지고 스피너 라벨로 스왑된다.

import { ToolApprovalCard } from '@mildang/design-system/unofficial/Chat';
import { ToolApprovalCardProps } from '@mildang/design-system/unofficial/Chat';
import { css } from '@mildang/styled-system/css';

const wrapper = css({ padding: '16', maxWidth: '400px' });

// 데모용 최소 part 필드만 지정하고 캐스팅한다(Tool 스토리와 동일 패턴).
const partProps = (o: Partial<ToolApprovalCardProps>): ToolApprovalCardProps =>
  ({
    type: 'tool-call',
    toolCallId: 'demo',
    toolName: 'run_command',
    args: { command: 'pnpm test --filter design-system' },
    argsText: 'pnpm test --filter design-system',
    status: { type: 'requires-action', reason: 'tool-calls' },
    addResult: () => {},
    resume: () => {},
    respondToApproval: () => {},
    ...o,
  }) as ToolApprovalCardProps;

const ApprovalCardRunningExample = () => (
    <div className={wrapper}>
      <ToolApprovalCard
        {...partProps({
          approval: { id: 'ap-3', approved: true },
          status: { type: 'running' },
        })}
      />
    </div>
  );

export default ApprovalCardRunningExample;

approved

승인 후 실행까지 끝난 상태다.

import { ToolApprovalCard } from '@mildang/design-system/unofficial/Chat';
import { ToolApprovalCardProps } from '@mildang/design-system/unofficial/Chat';
import { css } from '@mildang/styled-system/css';

const wrapper = css({ padding: '16', maxWidth: '400px' });

// 데모용 최소 part 필드만 지정하고 캐스팅한다(Tool 스토리와 동일 패턴).
const partProps = (o: Partial<ToolApprovalCardProps>): ToolApprovalCardProps =>
  ({
    type: 'tool-call',
    toolCallId: 'demo',
    toolName: 'run_command',
    args: { command: 'pnpm test --filter design-system' },
    argsText: 'pnpm test --filter design-system',
    status: { type: 'requires-action', reason: 'tool-calls' },
    addResult: () => {},
    resume: () => {},
    respondToApproval: () => {},
    ...o,
  }) as ToolApprovalCardProps;

const ApprovalCardApprovedExample = () => (
    <div className={wrapper}>
      <ToolApprovalCard
        {...partProps({
          approval: { id: 'ap-4', approved: true, optionId: 'o-once' },
          status: { type: 'complete' },
          result: { exitCode: 0 },
        })}
      />
    </div>
  );

export default ApprovalCardApprovedExample;

denied

거부 상태다. 결과 라벨만 남고 버튼은 비활성 상태로 스왑된다.

import { ToolApprovalCard } from '@mildang/design-system/unofficial/Chat';
import { ToolApprovalCardProps } from '@mildang/design-system/unofficial/Chat';
import { css } from '@mildang/styled-system/css';

const wrapper = css({ padding: '16', maxWidth: '400px' });

// 데모용 최소 part 필드만 지정하고 캐스팅한다(Tool 스토리와 동일 패턴).
const partProps = (o: Partial<ToolApprovalCardProps>): ToolApprovalCardProps =>
  ({
    type: 'tool-call',
    toolCallId: 'demo',
    toolName: 'run_command',
    args: { command: 'pnpm test --filter design-system' },
    argsText: 'pnpm test --filter design-system',
    status: { type: 'requires-action', reason: 'tool-calls' },
    addResult: () => {},
    resume: () => {},
    respondToApproval: () => {},
    ...o,
  }) as ToolApprovalCardProps;

const ApprovalCardDeniedExample = () => (
    <div className={wrapper}>
      <ToolApprovalCard
        {...partProps({
          approval: { id: 'ap-5', approved: false },
          status: { type: 'complete' },
        })}
      />
    </div>
  );

export default ApprovalCardDeniedExample;

interrupt

LangGraph 계열 interrupt. payload를 보여주고 확인 시 resume(payload)를 호출한다.

import { ToolApprovalCard } from '@mildang/design-system/unofficial/Chat';
import { ToolApprovalCardProps } from '@mildang/design-system/unofficial/Chat';
import { css } from '@mildang/styled-system/css';

const wrapper = css({ padding: '16', maxWidth: '400px' });

// 데모용 최소 part 필드만 지정하고 캐스팅한다(Tool 스토리와 동일 패턴).
const partProps = (o: Partial<ToolApprovalCardProps>): ToolApprovalCardProps =>
  ({
    type: 'tool-call',
    toolCallId: 'demo',
    toolName: 'run_command',
    args: { command: 'pnpm test --filter design-system' },
    argsText: 'pnpm test --filter design-system',
    status: { type: 'requires-action', reason: 'tool-calls' },
    addResult: () => {},
    resume: () => {},
    respondToApproval: () => {},
    ...o,
  }) as ToolApprovalCardProps;

const ApprovalCardInterruptExample = () => (
    <div className={wrapper}>
      <ToolApprovalCard
        {...partProps({
          toolName: 'ask_teacher',
          interrupt: { type: 'human', payload: { question: '이 학생을 보충반으로 옮길까요?' } },
          status: { type: 'requires-action', reason: 'interrupt' },
        })}
      />
    </div>
  );

export default ApprovalCardInterruptExample;