@ copilotkit/ 反应本地化
副驾驶Kit的无头回放原生装订. 提供轻量级提供商并再出口平台-不可知钩-没有DOM,CSS,或网络框架依赖性. 您用标准 React 本地组件构建聊天 UI .
安装
npm install @copilotkit/react-native "zod@^3.25"
每一个条目都需要 zod 3.25 或以后,同龄人依赖
@copilotkit/react-core。命令安装了 zod 3, 这是一种基因化的 UI
使用目录 child() 或者说 childList() 需要帮助.
圆柱体
React Industrial's JS runtime (Hermes) 缺少几个由CopilotKit所依赖的Web API. 导入多填充器 在任何其他代码之前 输入点 :
// index.js
import "@copilotkit/react-native/polyfills";
import { AppRegistry } from "react-native";
import App from "./App";
import { name as appName } from "./app.json";
AppRegistry.registerComponent(appName, () => App);
如果您已经填充了其中的一些 API, 您只能导入您需要的 :
import "@copilotkit/react-native/polyfills/streams";
import "@copilotkit/react-native/polyfills/encoding";
import "@copilotkit/react-native/polyfills/crypto";
import "@copilotkit/react-native/polyfills/dom";
import "@copilotkit/react-native/polyfills/location";
快开始
import {
CopilotKitProvider,
useAgent,
useCopilotKit,
} from "@copilotkit/react-native/headless";
export default function App() {
return (
<CopilotKitProvider runtimeUrl="https://your-server/api/copilotkit">
<ChatScreen />
</CopilotKitProvider>
);
}
再出口钩子从 @copilotkit/react-core数字 : useAgent, (中文(简体) ), useFrontendTool, (中文(简体) ), useComponent, (中文(简体) ), useHumanInTheLoop, (中文(简体) ), useInterrupt, (中文(简体) ), useSuggestions, (中文(简体) ), useConfigureSuggestions, (中文(简体) ), useAgentContext, (中文(简体) ), useThreads,以及 useCopilotKit。 。 。 .
基因UI(实验性)
@copilotkit/react-native/generative-ui 让代理制作您的目录
拥有本地组件 render_ui 工具。 安装 zod 3.25或以后:
包的每个条目都需要它 因为 @copilotkit/react-core 需求
作为同伴的依赖关系(@copilotkit/react-native 声明 zod 选项
端点 >=3.0.0 只因为缩小这个范围是一次突破
页:1 安装 zod 3 (npm install "zod@^3.25"用于目录
child() 或者说 childList():它们返回了zod 3个计划,这个zod 4个计划
无法维持。 运行时间默认不需要更改 : CopilotRuntime 添加
当应用程序发送至少包含一个组件的目录时的工具,除非
服务器集 generativeUI: false 或者限制它与
generativeUI: { agents: [...] }。此示例需要 home 代理在
运行时间: 一旦运行时间连接, useAgent 抛出没有它. /generative-ui 1.8.5.0或任何较早的版本
1.8.5.0之后释放的船舶.
import { useCallback, useEffect, useRef, useState } from "react";
import {
ActivityIndicator,
Pressable,
ScrollView,
Text,
View,
} from "react-native";
import { z } from "zod";
import {
CopilotKitCoreErrorCode,
CopilotKitCoreRuntimeConnectionStatus,
CopilotKitProvider,
useAgent,
useAgentContext,
useCopilotKit,
} from "@copilotkit/react-native/headless";
import {
GenerativeUIActivity,
GenerativeUIProvider,
createGenerativeUI,
isGenerativeUIActivity,
useGenerativeUIRuntime,
type GenerativeUIActivityMessage,
} from "@copilotkit/react-native/generative-ui";
const ui = createGenerativeUI({
id: "acme/bank@1",
components: {
BalanceCard: {
component: ({ account, amount }: { account: string; amount: number }) => (
<View>
<Text>{account}</Text>
<Text>{amount}</Text>
</View>
),
description:
"The balance of one account. Copy the values from the context.",
props: z.object({ account: z.string(), amount: z.number() }),
},
},
});
type CopilotKit = ReturnType<typeof useCopilotKit>["copilotkit"];
type Agent = ReturnType<typeof useAgent>["agent"];
/**
* The errors that end a run. A failed tool call is not one of them
* (TOOL_HANDLER_FAILED, TOOL_ARGUMENT_PARSE_FAILED): its error goes back to
* the model as the result of the call, and the run goes on.
*/
const RUN_FAILURES: ReadonlySet<CopilotKitCoreErrorCode> = new Set([
CopilotKitCoreErrorCode.AGENT_RUN_FAILED,
CopilotKitCoreErrorCode.AGENT_RUN_FAILED_EVENT,
CopilotKitCoreErrorCode.AGENT_RUN_ERROR_EVENT,
CopilotKitCoreErrorCode.AGENT_THREAD_LOCKED,
]);
// Date.now() alone repeats within a millisecond; with a counter, ids do not.
let sent = 0;
/**
* Sends one user message and runs the agent. Resolves with the run's error,
* or undefined; it never rejects. It counts every run failure of this agent
* while it waits, so start one run of an agent at a time.
*/
async function runTurn(copilotkit: CopilotKit, agent: Agent, content: string) {
const before = agent.messages.map((message) => message.id);
const { threadId } = agent;
let failure: Error | undefined;
const subscription = copilotkit.subscribe({
onError: ({ error, code, context }) => {
if (context.agentId === agent.agentId && RUN_FAILURES.has(code)) {
failure ??= error;
}
},
});
try {
sent += 1;
agent.addMessage({
id: `user-${Date.now()}-${sent}`,
role: "user",
content,
});
await copilotkit.runAgent({ agent });
} catch (error) {
failure ??= error instanceof Error ? error : new Error(String(error));
} finally {
subscription.unsubscribe();
}
try {
if (failure) takeBack(agent, before, threadId);
} catch (error) {
console.error("Could not take the failed turn back:", error);
}
return failure;
}
/**
* Removes the messages added since `before` (their ids), such as the user
* message and a partial reply. It keeps the surfaces: the provider keeps
* every surface that a run rendered, and the model still sees it, so the
* screen shows it too.
*/
function takeBack(
agent: Agent,
before: readonly string[],
threadId: string | undefined,
) {
// The agent moved to another thread: those messages are not this turn's.
if (agent.threadId !== threadId) return;
const kept = new Set(before);
agent.setMessages(
agent.messages.filter(
(message) => kept.has(message.id) || isGenerativeUIActivity(message),
),
);
}
/**
* An activity message that GenerativeUIActivity draws: its surface is aimed at
* the chat (the target it shows) and the agent has not deleted it.
*/
function isChatSurface(message: GenerativeUIActivityMessage) {
if (!isGenerativeUIActivity(message)) return false;
const content = message.content as {
target?: unknown;
operations?: unknown;
} | null;
const operations = Array.isArray(content?.operations)
? content.operations
: [];
return (
content?.target === "chat" &&
!operations.some(
(op) => typeof op === "object" && op !== null && "deleteSurface" in op,
)
);
}
/**
* The chat surfaces that this device built, each message id with its operations,
* to tell whether a run rendered or updated one here. The operations alone: each
* call of the agent also refills the list of rejected operations, so a call whose
* updates were all rejected changes the content but not the surface. A surface
* whose createSurface the app rejected (`failed`) does not count: the app checks
* the operations again, prop values included, and GenerativeUIActivity then shows
* only an error line.
*/
function chatSurfaces(
messages: readonly GenerativeUIActivityMessage[],
failed: (surfaceId: string) => boolean,
) {
const built = new Map<string | undefined, string>();
for (const message of messages.filter(isChatSurface)) {
const { surfaceId, operations } = message.content as {
surfaceId?: unknown;
operations?: unknown;
};
if (typeof surfaceId === "string" && failed(surfaceId)) continue;
built.set(message.id, JSON.stringify(operations));
}
return built;
}
// Your app's data. The agent sees only what you give it with useAgentContext.
const accounts = [
{ account: "Chequing", amount: 3482.1 },
{ account: "Savings", amount: 6150 },
];
function Home() {
const { copilotkit } = useCopilotKit();
// Until the runtime connects, `agent` is a stand-in (`isReady` is false). The
// registered agent replaces it, so a run started on the stand-in is lost.
// Once the runtime has connected, useAgent throws if it has no `home` agent.
const { agent, isReady } = useAgent({ agentId: "home" });
const runtime = useGenerativeUIRuntime();
// The thread it last checked: a ref, so StrictMode's second effect run sees it.
const checkedThread = useRef<string | undefined>(undefined);
const composing = useRef(false); // a ref, so a second tap is ignored at once
const [checked, setChecked] = useState(false);
const [busy, setBusy] = useState(false);
const [error, setError] = useState<string>();
useAgentContext({
description:
"The user's accounts. Show only these, with these exact balances.",
value: accounts,
});
const failed = useCallback(
(surfaceId: string) =>
!!runtime?.manager.getFailedSurface(agent.threadId, surfaceId),
[runtime, agent],
);
const compose = useCallback(async () => {
// A stand-in agent (not ready) would lose the run.
if (composing.current || !isReady) return;
composing.current = true;
setBusy(true);
setError(undefined);
try {
const before = agent.messages.map((message) => message.id);
const { threadId } = agent;
const shownBefore = chatSurfaces(agent.messages, failed);
const failure = await runTurn(copilotkit, agent, "Show my balances.");
const shownAfter = chatSurfaces(agent.messages, failed);
if (failure) {
// runTurn took the prompt back, so a retry sends it once.
setError(failure.message);
} else if (
![...shownAfter].some(
([id, content]) => shownBefore.get(id) !== content,
)
) {
// This run rendered or updated nothing here. Take its prompt back too.
takeBack(agent, before, threadId);
setError("The agent rendered nothing for this screen.");
}
} catch (reason) {
setError(reason instanceof Error ? reason.message : String(reason));
} finally {
composing.current = false;
setBusy(false);
}
}, [agent, copilotkit, failed, isReady]);
// The agent and its messages live in the core and outlive this screen. Run only
// when the registered agent has no surface here yet and is not running, so a
// screen that mounts again shows what it has instead of composing it again.
// Check again when the agent moves to another thread, once it is not running
// and no compose is under way (`busy` reruns this when one ends).
useEffect(() => {
if (
!isReady ||
busy ||
composing.current ||
agent.isRunning ||
checkedThread.current === agent.threadId
) {
return;
}
checkedThread.current = agent.threadId;
setChecked(true);
setError(undefined);
if (chatSurfaces(agent.messages, failed).size === 0) {
void compose();
}
}, [isReady, busy, agent, agent.threadId, agent.isRunning, compose, failed]);
const surfaces = agent.messages.filter(isChatSurface);
const built = chatSurfaces(agent.messages, failed);
const unreachable =
copilotkit.runtimeConnectionStatus ===
CopilotKitCoreRuntimeConnectionStatus.Error;
const waiting = !isReady || !checked || busy || agent.isRunning;
return (
<ScrollView contentContainerStyle={{ padding: 16 }}>
{surfaces.map((message) => (
<GenerativeUIActivity
key={message.id}
message={message}
agent={agent}
/>
))}
{unreachable ? (
<Text accessibilityRole="alert">
Can't reach the agent. Check the runtime URL.
</Text>
) : waiting ? (
<ActivityIndicator />
) : error || built.size === 0 ? (
<Pressable accessibilityRole="button" onPress={() => void compose()}>
<Text>
Couldn't load your balances{error ? ` (${error})` : ""}. Tap to try
again.
</Text>
</Pressable>
) : null}
</ScrollView>
);
}
export default function App() {
return (
<CopilotKitProvider runtimeUrl="https://your-server/api/copilotkit">
<GenerativeUIProvider ui={ui}>
<Home />
</GenerativeUIProvider>
</CopilotKitProvider>
);
}
GenerativeUIActivity 使一个活动消息的表面在您自己的
信件列表或屏幕上没有聊天。 就像网络聊天,它只显示
面向聊天的表面; 通过 target 以显示另一个目标。 它让
无所事事(并在发展中发出警告) GenerativeUIProvider下,a
没有提供者 ui,或当代理人没有 threadId。示例显示
一个旋转器,一个无法到达的跑步时间 和与原因的重试。 运行失败
迅速被带回,但表面 它使停留: 提供者和
模特儿留着 文档添加聊天列表 .
GenerativeUISurface 在应用程序中标出一个命名的地方; style 应用时
倒计时秀也。 只传送工具; 没有标准目录, 没有
theme 道具(a) theme 由 JavaScript 传递, 并随开发而放弃
(警告)和无 openUrl ( 它失败 ) “ 打开Url 无法在此访问
平台”和预建 CopilotChat 尚未绘制表面。 见
反应原生文件。 。 。 .
API 表面
根导入( E)@copilotkit/react-native这是v2 API.
和SDK的网络不同 /v2 子路径, 反应本地导出
直接取自包根的 v2 API.
根还再导出聊天组件和 useAttachments,哪个
进口 expo-document-picker 财务报告和审定财务报表 expo-file-system; Metro解决了它们
捆绑时间,所以从根部导入的应用程序必须同时安装两种.
@copilotkit/react-native/headless 将提供者和钩子导出
这就是上面的"快起步"使用的原因.
文档
完整的设置指令、使用示例和故障排除,见 反应原生文件。 。 。 .