UNPKG

@copilotkit/react-core

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

123 lines (110 loc) 3.91 kB
import { ActionRenderProps, ActionRenderPropsWait, FrontendAction } from "../types"; import { CopilotKitError, CopilotKitErrorCode, MappedParameterTypes, Parameter, getZodParameters, parseJson, } from "@copilotkit/shared"; import { useHumanInTheLoop as useHumanInTheLoopVNext } from "@copilotkitnext/react"; import { ToolCallStatus } from "@copilotkitnext/core"; import React, { ComponentType, FunctionComponent, useEffect, useRef } from "react"; type HumanInTheLoopOptions = Parameters<typeof useHumanInTheLoopVNext>[0]; type HumanInTheLoopRender = HumanInTheLoopOptions["render"]; type HumanInTheLoopRenderArgs = HumanInTheLoopRender extends (props: infer P) => any ? P : never; export type UseHumanInTheLoopArgs<T extends Parameter[] | [] = []> = { available?: "disabled" | "enabled"; render: FrontendAction<T>["renderAndWaitForResponse"]; followUp?: FrontendAction<T>["followUp"]; } & Pick<FrontendAction<T>, "name" | "description" | "parameters">; type HitlRendererArgs = | { name: string; description: string; args: Partial<Record<string, unknown>>; status: ToolCallStatus.InProgress; result: undefined; respond: undefined; } | { name: string; description: string; args: Record<string, unknown>; status: ToolCallStatus.Executing; result: undefined; respond: (result: unknown) => Promise<void>; } | { name: string; description: string; args: Record<string, unknown>; status: ToolCallStatus.Complete; result: string; respond: undefined; }; type HitlRenderer = FunctionComponent<HitlRendererArgs>; export function useHumanInTheLoop<const T extends Parameter[] | [] = []>( tool: UseHumanInTheLoopArgs<T>, dependencies?: any[], ) { const { render, ...toolRest } = tool; const { name, description, parameters, followUp } = toolRest; const zodParameters = getZodParameters(parameters); const renderRef = useRef<HitlRenderer | null>(null); useEffect(() => { renderRef.current = (args: HitlRendererArgs): React.ReactElement | null => { if (typeof render === "string") { return React.createElement(React.Fragment, null, render); } if (!render) { return null; } const renderProps: ActionRenderPropsWait<T> = (() => { const mappedArgs = args.args as unknown as MappedParameterTypes<T>; switch (args.status) { case ToolCallStatus.InProgress: return { args: mappedArgs, respond: args.respond, status: args.status, handler: undefined, }; case ToolCallStatus.Executing: return { args: mappedArgs, respond: args.respond, status: args.status, handler: () => {}, }; case ToolCallStatus.Complete: return { args: mappedArgs, respond: args.respond, status: args.status, result: args.result ? parseJson(args.result, args.result) : args.result, handler: undefined, }; default: throw new CopilotKitError({ code: CopilotKitErrorCode.UNKNOWN, message: `Invalid tool call status: ${(args as unknown as { status: string }).status}`, }); } })(); const rendered = render(renderProps); if (typeof rendered === "string") { return React.createElement(React.Fragment, null, rendered); } return rendered ?? null; }; }, [render, ...(dependencies ?? [])]); useHumanInTheLoopVNext({ name, description, followUp, parameters: zodParameters, render: ((args: HumanInTheLoopRenderArgs) => renderRef.current?.(args as HitlRendererArgs) ?? null) as HumanInTheLoopOptions["render"], }); }