@cogitator-ai/ 下一个
Next.js App路由器集成 Cogitator AI 运行时间. 提供流式聊天处理器,一个分批代理处理器,以及能说出以Vercel AI SDK事件流为模式的SSE协议的回放钩.
安装
pnpm add @cogitator-ai/next @cogitator-ai/core
与Next.js 14,15和16以及React 18和19合作.
快速启动
1. 创建API 路由
// app/api/chat/route.ts
import { Cogitator, Agent, tool } from '@cogitator-ai/core';
import { createChatHandler } from '@cogitator-ai/next';
import { z } from 'zod';
const cogitator = new Cogitator({
llm: {
defaultModel: 'openai/gpt-6-luna',
providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } },
},
});
const agent = new Agent({
name: 'assistant',
model: 'openai/gpt-6-luna',
instructions: 'You are a helpful assistant.',
tools: [
tool({
name: 'get_weather',
description: 'Get weather for a location',
parameters: z.object({ location: z.string() }),
execute: async ({ location }) => `Weather in ${location}: 72°F, sunny`,
}),
],
});
export const POST = createChatHandler(cogitator, agent);
2. 客户部分的使用
'use client';
import { useCogitatorChat } from '@cogitator-ai/next/client';
export function Chat() {
const { messages, input, setInput, send, isLoading } = useCogitatorChat({
api: '/api/chat',
});
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form
onSubmit={(e) => {
e.preventDefault();
send();
}}
>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type a message..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
}
服务器处理器
createChatHandler
创建流式聊天处理器。 处理者:
- 运行代理服务器, 启用符号流并转发
text-*, (中文(简体) ),tool-*财务报告和审定财务报表finish发生时的事件 - 使用该 上次用户消息 作为运行输入,对话历史由
threadId(配置)memory编辑Cogitator(见A/CN.9/WG.III/WP.III/WP.1号文件)。 没有一条,处理器打开一条新线条,并在其中命名start事件,以及finish事件重复,并且useCogitatorChat自动通过 - 通行证申请
metadata运行为context键acceptContext允许: 运行发送context作为所提供应用程序的数据,提交该模型,因此,默认情况下,请求使用metadata拒绝使用400 - 写入
: keep-alive注释每sseHeartbeatMs(默认5 s,0关闭( 关闭它 ) , 而运行是静默的, 因此代理或平台不会关闭在慢工具上等待的流 - 当客户端断开时中止运行( r)
req.signal),除非resumableStreams打开( 见 可恢复流(中文(简体) ) - 仅读取 JSON 天体(
415页:1text/plain和表单,浏览器在没有飞行前的情况下发送到源头),验证正体(JSON对象,messages数组, 非空白threadId对象metadata页:1multitaskStrategy联 合 国enqueue, (中文(简体) ),reject, (中文(简体) ),interrupt或者说rollback),将其限制在1 MB(另作413个)并返回400当没有用户消息时
import { createChatHandler } from '@cogitator-ai/next';
export const POST = createChatHandler(cogitator, agent, {
// Custom input parsing
parseInput: async (req) => {
const body = await req.json();
return {
messages: body.messages,
threadId: body.threadId,
metadata: body.metadata,
};
},
// Pre-processing hook — the returned object is merged into the run options
// (e.g. userId, threadId, timeout). Streaming callbacks and the abort
// signal are always managed by the handler. Throw an error with a `status`
// property to reject the request with that status (default 401).
beforeRun: async (req, input) => {
console.log('Starting chat with', input.messages.length, 'messages');
return { userId: 'user-123' };
},
// Post-processing hook — runs before the `finish` event; if it throws,
// the client receives an `error` event instead of `finish`
afterRun: async (result) => {
console.log('Chat completed:', result.output);
},
});
可恢复流
与 resumableStreams运行会超过一个中断的连接 客户端会从它停止的地方取出它的流. useCogitatorChat 客户端本身是否:
// app/api/chat/route.ts
const handler = createChatHandler(cogitator, agent, {
beforeRun: async (req) => ({ userId: await userIdFrom(req) }),
resumableStreams: { owner: (req) => (req ? userIdFrom(req) : undefined) },
});
export const POST = handler;
export const DELETE = handler;
// app/api/chat/resume/route.ts
const resume = createResumeHandler(cogitator, agent, {
stream: true,
beforeRun: async (req) => ({ userId: await userIdFrom(req) }),
resumableStreams: { owner: (req) => (req ? userIdFrom(req) : undefined) },
});
export const POST = resume;
export const DELETE = resume;
- 每个事件都带有SSE
id, (中文(简体) ),<streamId>:<seq>,而一个断开的客户端不再停止运行(该客户端signalbeforeRun返回仍然如此. - 同样的要求再次与
Last-Event-ID: <id>重新连接而不是启动运行: 答案重放 id 之后的事件, 然后跟踪运行到finish或者说error。 。 。 .?lastEventId=<id>也起作用了,但它把ID放入了URL和日志中,因此更喜欢标题. DELETE同一路线?streamId=<streamId>取消运行( e)204:其溪流以STREAM_CANCELLED。 。 。 .useCogitatorChat送来stop(), (中文(简体) ),reload()和每一次其他的中断, 所以一个停止的答案不会去 花象征性的.- 重新连接和取消读取无正文和运行
beforeRun也没有deps,它的副作用会重复。 溪流注定owner(req): 只有拥有同一所有者的请求才能在authorizeThread(req, threadId)在溪流的线上(403 THREAD_FORBIDDEN) (中文(简体) ). 与beforeRun, (中文(简体) ),owner需要处理器投出。 无beforeRun每个打电话的人都是匿名的 任何人,未知或过期的溪流得到404 STREAM_NOT_FOUND,一个错误的id400 INVALID_INPUT。 。 。 . - 不再保存的事件后重新连接( 多于
maxEvents从此以后error事件为STREAM_RESUME_GAP无人跟踪detachedTimeoutMs(5分钟)停止,每跑一次until中止, 这样流会以STREAM_ABORTED时的新流maxStreams溪流都会被拒绝503 STREAM_LIMIT_REACHED。 。 。 . true将事件保存在处理器的记忆中:每条溪流有1000个事件,一个完成的溪流5分钟,1000个溪流({ maxEvents, retentionMs, maxStreams, detachedTimeoutMs }”)。 在没有服务器的平台或若干实例中,重新连接或取消可能到达未启动运行的实例:通过 astore共享(StreamReplayStore从@cogitator-ai/server-shared,由 Redis 支持或与 pub/sub 类似,并运行在某个函数可能超过其请求的地方.
createAgentHandler
为长跑任务创建分批(非流)处理器. 默认解析器是每个 Cogitator 适配器股份的验证器: 一个非空格字符串 input,可选对象 context 仅使用密钥 acceptContext 允许( 无默认的, 因为模型读取 context 作为所提供的应用程序的数据),可选的非空白 threadId 和可选 multitaskStrategy (单位:千美元)400 { error: { message, code: 'INVALID_INPUT' } } 否则, 415 对于一个不是JSON的身体。 A 类 parseInput 由你自己决定 如果客户端断开, 运行将被中止 .
import { createAgentHandler } from '@cogitator-ai/next';
export const POST = createAgentHandler(cogitator, researchAgent, {
parseInput: async (req) => {
const body = await req.json();
return {
input: body.query,
context: body.context,
threadId: body.threadId,
};
},
});
反应格式 :
{
"output": "Research results...",
"threadId": "thread-abc",
"usage": {
"inputTokens": 150,
"outputTokens": 500,
"totalTokens": 650
},
"toolCalls": [{ "id": "call_1", "name": "search", "arguments": { "q": "AI" } }],
"status": "completed",
"traceId": "trace-xyz"
}
答案是每个 Cogitator 适配器返回的相同对象(toAgentRunResponse() 联 合 国 @cogitator-ai/server-shared: reasoning, (中文(简体) ), structured, (中文(简体) ), structuredError, (中文(简体) ), truncated, (中文(简体) ), blocked 财务报告和审定财务报表 iterationLimitReached 应用时出现。 它从不携带系统即时,历史或追踪跨度,其属性具有原始工具参数和错误: traceId 将答案链接到您的痕迹,以及 afterRun 接收整个 RunResult运行等待 核准数 答题 "status": "paused" 联合国 pendingApprovals运行失败 CogitatorError 答题 { "error": { "message": message, "code": code } } 带有该错误状态(例如 429 LLM_RATE_LIMITED)和a 数据 Retry-After 当供应商说什么时候再试时, 标题 。 服务器上记录了任何其他错误并回答为 500 { "error": { "message": "Internal server error", "code": "INTERNAL_ERROR" } },因此其文本(连接字符串,文件路径)从未到达客户端. 每个Cogitator适配器都会在这个信封中回答错误. 当 afterRun 掷出.
多个用户
当几个人共享服务器时,返回呼叫者的 userId 从 beforeRun达到 cogitator.run,使每个 threadId 给创建它运行的用户:
export const POST = createChatHandler(cogitator, agent, {
beforeRun: async (req) => {
const user = await getSession(req);
if (!user) throw Object.assign(new Error('Unauthorized'), { status: 401 });
return { userId: user.id };
},
});
A 类 threadId 属于另一个用户的,在使用该模型之前被拒绝: createAgentHandler 答题 403 与 code: "THREAD_ACCESS_DENIED",以及 createChatHandler 以 error 带有密码的事件。 没有 userId,呼叫者只能使用没有主人的线程.
每个处理器还采用服务器合同中其他 Cogitator 适配器共享的选项:
export const POST = createChatHandler(cogitator, agent, {
beforeRun: async (req) => ({ userId: (await getSession(req))?.id }),
anonymous: 'deny',
authorizeThread: (req, threadId) => threadId.startsWith(`${tenantOf(req)}:`),
deps: (req) => ({ db: databaseFor(tenantOf(req)) }),
});
anonymous: 'deny'答题401 UNAUTHORIZED何时beforeRun返回无userId(默认)'allow'让这些来电者进来.authorizeThread(req, threadId)在运行开始前询问关于线程的要求名称,以及false答题403 THREAD_FORBIDDEN。 。 。 .deps(req)给运行deps(单位:千美元)context.deps永远不要从身体读取。 A 类deps那个beforeRun返回战胜它.
核准
一个工具与 requiresApproval 在执行前暂停运行。 运行时间将运行的检查点保存在服务器上, 每个线程; 客户端只看到等待决定的内容 :
createAgentHandler答案为"status": "paused"财务报告和审定财务报表pendingApprovals(单位:千美元)toolCallId, (中文(简体) ),toolName, (中文(简体) ),arguments, (中文(简体) ),description, (中文(简体) ),sideEffects) (中文(简体) ).createChatHandler发送{"type":"approval-required","threadId":"…","approvals":[…]}刚刚finish。 。 。 .
createResumeHandler 继续运行。 这需要 { threadId, decisions?, defaultDecision? },在决定是 { approved: true } 或者说 { approved: false, reason? } 由工具调用 id. 授权的电话运行,拒绝的电话以理由回答模型,而电话未决定的电话再次暂停运行. 把电话拿回来 userId 从 beforeRun 至于其他处理器:只有运行属于的用户可以恢复运行.
// app/api/chat/resume/route.ts
import { createResumeHandler } from '@cogitator-ai/next';
export const POST = createResumeHandler(cogitator, agent, {
stream: true,
beforeRun: async (req) => ({ userId: (await getSession(req)).id }),
});
默认它回答像 createAgentHandler; 与 stream: true 它流出 其余的跑象 createChatHandler,这是什么 useCogitatorChat 想要 想要吗? 另一个用户运行的答案 403 THREAD_ACCESS_DENIED,线程没有暂停运行 409 RUN_NOT_PAUSED (单位:千美元) error 流经时的事件).
在客户身上, useCogitatorChat({ api, resumeApi }) 曝光情况 pendingApprovals 继续运行到一个新的助理消息:
'use client';
import { useCogitatorChat } from '@cogitator-ai/next/client';
export function Chat() {
const { messages, pendingApprovals, approve, deny, isLoading } = useCogitatorChat({
api: '/api/chat',
resumeApi: '/api/chat/resume',
});
return (
<div>
{messages.map((m) => (
<p key={m.id}>{m.content}</p>
))}
{pendingApprovals.length > 0 && (
<div>
{pendingApprovals.map((call) => (
<p key={call.toolCallId}>
{call.toolName}: {JSON.stringify(call.arguments)}
</p>
))}
<button disabled={isLoading} onClick={() => approve()}>
Approve
</button>
<button disabled={isLoading} onClick={() => deny('Not approved')}>
Deny
</button>
</div>
)}
</div>
);
}
每一次通话决定,使用一次 resume({ decisions: { [toolCallId]: { approved: true } }, defaultDecision: { approved: false } })。 。 。 .
approve() 财务报告和审定财务报表 deny(reason?) 回答每个待发的电话, approveAlways() 批准他们,并节省每个人的 suggestedRule (见 权限) (中文(简体) ). 一旦恢复程序被接受,当服务器再也没有任何暂停时,批准就明确了(409)),或者当用户发送了新消息,从而拒绝了服务器上的等待呼叫. 一个JSON resumeApi 也起作用:其答复作为一个助理信息附上. useCogitatorAgent({ api, resumeApi }) 曝光情况 pendingApprovals 最后结果和 resume(decisions)。 。 。 .
工具也可以向用户提问 context.interrupt(),它暂停运行的方式相同. createAgentHandler 然后用 interrupts (单位:千美元)id, (中文(简体) ), kind, (中文(简体) ), reason, (中文(简体) ), message, (中文(简体) ), responseSchema, (中文(简体) ), expiresAt, (中文(简体) ), toolCallId, (中文(简体) ), toolName),以及 createChatHandler 发送 {"type":"interrupt-required","threadId":"…","interrupts":[…]} 改为 approval-required。 。 。 . createResumeHandler 以中断 id 的方式获取答案 responses两个钩子都把问题暴露为 pendingInterrupts 并用 resume({ responses: { [id]: answer } })。 。 。 . useCogitatorChat 还有电话 onInterruptRequired。一个与其 responseSchema 拒绝使用 400 INTERRUPT_ANSWER_INVALID后发送 expiresAt 与 410 INTERRUPT_EXPIRED (流线) createResumeHandler 两者都作为 error 与此相关的事件 code) (中文(简体) ).
命名线索的运行也可能命名为 multitaskStrategy 当线程在这个 Cogitator 中已经运行时 : enqueue 等待运行, reject 拒绝新运行 THREAD_BUSY, (中文(简体) ), interrupt 停止运行( 它失败) RUN_INTERRUPTED和 rollback 停止它和从它开始的树枝. createAgentHandler 从身体读出来 multitaskStrategy 并用 409。 。 。 . createChatHandler 读它,但它的溪已经回答 200 发送 start 当跑步遇到另一跑道时,它会把它们当作一条 error 与此相关的事件 code。没有一个 科吉塔克 multitaskStrategy 适用.
useCogitatorChat({ multitaskStrategy }) 发送它与每一个消息。 没有一个人, send 中止先流的请求, 一如既往。 与 interrupt 或者说 rollback 它让服务器停止运行 旧的答案会一直停留到它得到的 钩子会忽略它发出的其他信息,包括 RUN_INTERRUPTED 错误, 因为关闭连接本身会在服务器看到新信件之前中止运行 rollback 就会失去分出点 旧请求一旦完成就会中止,或者当新请求失败时立即中止(a) 403 例如,它从来就没有隐蔽过。 与 enqueue 或者说 reject 它让请求独处,让服务器决定:运行后一个被排列的答案流,一个被拒绝的消息最后会进入 error 财务报告和审定财务报表 onError 而运行中的答案继续. isLoading 留下来 true 直到每个请求都得到满足 stop 全部停止,包括交给服务器的.
在新对话中, 服务器选择线索并将其命名于 start根据战略发出的信息,在它提出请求之前,它已经指定了它的线程等待 start,因此信件共享一线程而不是各启动一线程(一个结束而不命名一线程的请求让下一条消息启动自己的一线程). 批准和问题属于暂停的请求:当后一消息运行在线程上时,服务器放弃了暂停运行,所以 pendingApprovals 财务报告和审定财务报表 pendingInterrupts 清除信件完成后, 或者显示自己的暂停 。 一个被拒绝的消息从未运行过,所以在它停留之前暂停.
createAGUIHandler
代理给 AG-UI 1.0 客户端,如 CopilotKit 或 a @ag-ui/client HttpAgent: 它需要一个 RunAgentInput 和流 AG-UI 事件。 AG-UI线程是跑的内存线程,最后一条消息(用户)是新转弯, resume 条目立即回答暂停运行的每次中断, state 是运行状态和 context 简介。 子代理流作为 AG-UI 子代理, 数据部分作为活动, 状态变化为 STATE_DELTA,然后暂停运行结束 RUN_FINISHED 和结果 interrupt。 。 。 .
// app/api/agui/route.ts
import { createAGUIHandler } from '@cogitator-ai/next';
export const POST = createAGUIHandler(cogitator, agent, {
beforeRun: async (req) => ({ userId: await userIdFrom(req) }),
});
触发和收件箱(实验)
createTriggerHandler 财务报告和审定财务报表 createInboxHandler 服务于 API 触发器的路径,webhooks 及其运行时触发器管理器结果的收件箱. 自己开始经理.
// lib/cogitator.ts
export const cog = new Cogitator({ experimental_triggers: { agents, routines } });
await cog.experimental_triggers?.start();
export const inbox = createInboxHandler(cog.experimental_triggers!, {
auth: async (req) => ({ userId: await userIdOf(req) }),
});
// app/api/triggers/[id]/[route]/route.ts
export const POST = createTriggerHandler(cog.experimental_triggers!);
// app/api/inbox/route.ts
export const { GET } = inbox;
// app/api/inbox/[id]/route.ts
export const { PATCH } = inbox;
| 路线 | 认证 | 体质 |
|---|---|---|
POST /api/triggers/:id/fire | 扳机本身的无记名符号 | { "text": "..." }原始读取 |
POST /api/triggers/:id/webhook | 发送者的签名(HMAC、GitHub、Mailgun、Postmark、SendGrid、Slack) | 任何内容类型, 读取原始 |
GET /api/inbox | auth 财务报告和审定财务报表 anonymous | ?status=unread&kind=failure&routineId=...&limit=20&before=<ms> |
PATCH /api/inbox/:id | auth 财务报告和审定财务报表 anonymous | { "status": "read" | "unread" | "archived" } |
触发处理器本身不认证任何内容:一个触发器按来时的准确顺序检查指使或签名,读取任何内容类型(JSON,表单,多部分)的原始内容可达10个MiB并从未解析或再串行. 一个更大的身体答案 413 PAYLOAD_TOO_LARGE,则a route 其他 fire 财务报告和审定财务报表 webhook 404它以状态、JSON身体和触发器管理器头来回答, { error: { code, message } } 在拒绝。 收件箱的处理者从 auth (单位:千美元){ userId }, (中文(简体) ), undefined 给匿名电话的 一个没有用户代号的呼叫者,这是每个没有用户代号的呼叫者 auth获取 401 UNAUTHORIZED 除非 anonymous: 'allow' 已经设定了,然后他们就看到没有一件的物品。 呼叫者只看到并更改其项目 userId,另一个用户的项目答案 404 NOT_FOUND。 。 。 . params 可以是承诺(后15后)或物体本身.
客户钩
useCogitatorChat
全能聊天钩 有流支持.
const {
// State
messages, // ChatMessage[]
input, // string
isLoading, // boolean
error, // Error | null
threadId, // string | undefined
// Actions
setInput, // (value: string) => void
send, // (input?: string, metadata?: Record<string, unknown>) => Promise<void>
stop, // () => void
reload, // () => Promise<void>
setThreadId, // (id: string) => void
// Message management
appendMessage, // (message: ChatMessage) => void
clearMessages, // () => void
setMessages, // (messages: ChatMessage[]) => void
// Approvals (see "Approvals")
pendingApprovals, // PendingApproval[]
pendingInterrupts, // PendingInterrupt[], the questions the run's tools asked
resume, // (decisions: ResumeDecisions) => Promise<void>, answers go in `responses`
approve, // () => Promise<void>
deny, // (reason?: string) => Promise<void>
} = useCogitatorChat({
api: '/api/chat',
threadId: 'optional-thread-id',
initialMessages: [],
headers: { 'X-Custom-Header': 'value' },
// Callbacks
onError: (error) => console.error(error),
onFinish: (message) => console.log('Done:', message),
onToolCall: (toolCall) => console.log('Tool called:', toolCall.name),
onToolResult: (result) => console.log('Tool result:', result.result, result.presentation),
onToolCallDropped: (toolCallId) => console.log('Never ran:', toolCallId), // removed from the message
onReasoning: (delta) => console.log('Thinking:', delta),
onApprovalRequired: (approvals) => console.log('Waiting for approval:', approvals),
onInterruptRequired: (interrupts) => console.log('Waiting for answers:', interrupts),
// Endpoint of a createResumeHandler, for resume/approve/deny
resumeApi: '/api/chat/resume',
// Sent with every message, for a thread that already has a run going on
multitaskStrategy: 'enqueue',
// Retry configuration
retry: {
maxRetries: 3,
delay: 1000,
backoff: 'exponential', // 1s, 2s, 4s
},
// Resume a stream that drops mid-run (default, false turns it off)
reconnect: { attempts: 3, delayMs: 1000 },
});
重新连接
对付一个处理者 resumableStreams,它的事件携带的ID, 钩保留了它得到的最后一次事件的ID。 当流落到跑道中间时(网络失败,或者身体在运行前结束) finish 或者说 error),它再次以 Last-Event-ID 和助理消息继续, 没有丢失或重复的事件。 等着 delayMs 在第一次重接之前, 和两倍的时间之前, 之后放弃 attempts 在失败或未带来事件的一行中重新连接, 并有最后一个错误( 连续结束的流) finish 放弃吧 The stream ended before the run finished从来没有一个解答像它做了. STREAM_NOT_FOUND (服务器不再保留流)和 STREAM_RESUME_GAP (它在 id 之后丢失了事件) 以该错误结束请求: 呼叫 setMessages 与线程的线程 从自己的线程赶上. stop(), (中文(简体) ), reload() 和中断当前信件的新信件也取消其在服务器上的运行(DELETE 与 ?streamId=) (中文(简体) ). 无相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相.
随元数据发送
// Basic send
await send('Hello!');
// Send with metadata (passed to the run as `context` on the server,
// for the keys the handler accepts: createChatHandler(cogitator, agent, { acceptContext: ['priority'] }))
await send('Analyze this', {
priority: 'high',
});
// Send using input state
setInput('My message');
await send();
信件管理
// Add a system message
appendMessage({
id: crypto.randomUUID(),
role: 'system',
content: 'Context updated.',
});
// Clear conversation
clearMessages();
// Replace all messages
setMessages([{ id: '1', role: 'user', content: 'New conversation' }]);
useCogitatorAgent
非分流批量请求的钩(研究、分析等).
const {
run, // (input: AgentInput) => Promise<void>
result, // AgentResponse | null
reasoning, // string | undefined — result?.reasoning
pendingApprovals, // PendingApproval[] — result?.pendingApprovals
resume, // (decisions: ResumeDecisions) => Promise<void>
isLoading, // boolean
error, // Error | null
reset, // () => void
} = useCogitatorAgent({
api: '/api/research',
resumeApi: '/api/research/resume',
headers: { Authorization: 'Bearer token' },
onError: (error) => console.error(error),
onSuccess: (result) => console.log('Done:', result.output),
retry: {
maxRetries: 2,
delay: 2000,
backoff: 'linear',
},
});
// Execute
await run({
input: 'Research AI trends in 2025',
context: { focus: 'enterprise' },
threadId: 'research-session-1',
});
// Access result
console.log(result?.output);
console.log(result?.toolCalls);
分流协议
聊天处理器流 Server-Sent Events. 事件名称沿用了Vercel AI SDK事件流风格,但有效载荷是Cogitator的自有tool-call-*, (中文(简体) ), message 错误, usage编号 :threadId ),因此使用 useCogitatorChat (或你自己的解析器)在客户端而不是AI SDK's useChat数字 :
data: {"type":"start","messageId":"msg-1","threadId":"thread-abc"}
data: {"type":"text-start","id":"text-1"}
data: {"type":"text-delta","id":"text-1","delta":"Hello"}
data: {"type":"text-delta","id":"text-1","delta":" world"}
data: {"type":"text-end","id":"text-1"}
data: {"type":"tool-call-start","id":"tool-1","toolName":"get_weather"}
data: {"type":"tool-call-delta","id":"tool-1","argsTextDelta":"{\"location\":\"NYC\"}"}
data: {"type":"tool-call-end","id":"tool-1"}
data: {"type":"tool-result","id":"tr-1","toolCallId":"tool-1","result":"72°F"}
data: {"type":"finish","messageId":"msg-1","usage":{...},"threadId":"thread-abc","status":"completed"}
data: [DONE]
一个特工与 reasoning: { summary: true } 并流出其推理摘要 reasoning-start, (中文(简体) ), reasoning-delta 财务报告和审定财务报表 reasoning-end 事件。 文本或推理块以其第一个三角洲打开,在其它类型的块、工具呼叫或 finish,所以块永远不会重叠. useCogitatorChat 将三角洲收集到 message.reasoning (电话) onReasoning )),和"每张相" createAgentHandler 返回摘要为 reasoning 旁边 usage.reasoningTokens, (中文(简体) ), usage.cachedInputTokens 财务报告和审定财务报表 usage.cacheWriteTokens 当供应商报告时。 这个 finish 事件 createChatHandler 携带同样的 usage,这些计数包括.
暂停的跑步 核准数 发送 {"type":"approval-required","threadId":"…","approvals":[…]} 打开块关闭后及关闭前 finish,并有一个暂停提问 它的工具被问到 {"type":"interrupt-required","threadId":"…","interrupts":[…]} 换个位置.
一个工具的数据部件( A)context.data()以下列方式抵达 {"type":"data","id":"…","name":"…","data":…}数字 : useCogitatorChat 保存每个标识中的最新值 message.data 并报告每个人 onData,瞬间"transient":true只有在这里。 跑步的状态是 state-snapshot,则 state-delta 贾森 补丁,钩子把它放进去 state (电话) onStateChangeA级 reset-step 事件命名了文字和推理部分以及工具的调用,这些调用是从另一个模型上开始的一步,而钩子将它们取出消息.
如果运行失败, 打开的文本或推理块将被关闭 {"type":"error","message":"...","code":"..."} 发送事件代替 finishA级 CogitatorError 保存其信件和代码; 任何其它错误都会被记录在服务器上并作为 "message":"Internal server error","code":"INTERNAL_ERROR"。 。 。 .
协议是每个适配器 说话,从 @cogitator-ai/server-shared。服务器侧构件导出为自定义处理器 :
import { StreamWriter, encodeSSE, generateId } from '@cogitator-ai/next';
import type { StreamEvent, Usage } from '@cogitator-ai/next';
类型
interface ChatMessage {
id: string;
role: 'user' | 'assistant' | 'system';
content: string;
reasoning?: string; // reasoning summary of an assistant message
toolCalls?: ToolCall[];
metadata?: Record<string, unknown>;
createdAt?: Date;
}
interface AgentInput {
input: string;
context?: Record<string, unknown>;
threadId?: string;
}
interface AgentResponse {
output: string;
threadId: string;
usage: {
inputTokens: number;
outputTokens: number;
totalTokens: number;
reasoningTokens?: number;
cachedInputTokens?: number;
cacheWriteTokens?: number;
};
toolCalls: { id: string; name: string; arguments: Record<string, unknown> }[];
reasoning?: string;
status: 'completed' | 'paused';
pendingApprovals?: PendingApproval[];
structured?: unknown;
structuredError?: string;
truncated?: true;
blocked?: 'content_filter' | 'refusal';
iterationLimitReached?: true;
traceId: string;
}
interface PendingApproval {
toolCallId: string;
toolName: string;
arguments: Record<string, unknown>;
description: string;
sideEffects?: string[];
}
interface ResumeDecisions {
decisions?: Record<string, { approved: true } | { approved: false; reason?: string }>;
defaultDecision?: { approved: true } | { approved: false; reason?: string };
}
interface RetryConfig {
maxRetries?: number; // default: 0
delay?: number; // default: 1000ms
backoff?: 'linear' | 'exponential';
}
处理错误
两个钩子都提供出错状态和回调:
const { error, isLoading } = useCogitatorChat({
api: '/api/chat',
onError: (err) => {
toast.error(err.message);
},
});
if (error) {
return <div>Error: {error.message}</div>;
}
HTTP 失败被抛出为 HttpError (从 @cogitator-ai/next/client带有 status 属性和信件 Request failed: 400 - No user message provided。流层 error 事件调用 onError 与活动 message 跳过 onFinish。 。 。 .
在服务器上, a beforeRun 在客户端出错时丢出错误 status (单位:千美元)400 改为 499以这一地位、其信息以及 code (默认) UNAUTHORIZED (单位:千美元) 401页:1 CogitatorError 答案作为失败的运行。 任何其它错误都会被记录和回答 401 Unauthorized 所以一个下行的会话店 永远不显示地址 A 类 parseInput 丢出答案 400 INVALID_INPUT。运行失败遵循以上规则:a CogitatorError 保存其信息并 code任何其它东西 Internal server error 与 code: "INTERNAL_ERROR"。 。 。 .
启用重试后, 即时错误( 网络故障, 408/429/ 502/ 503/ 504) 将自动重试; 后退等待被取消 stop()数字 :
useCogitatorChat({
api: '/api/chat',
retry: {
maxRetries: 3,
delay: 1000,
backoff: 'exponential',
},
});
取消
停止与 stop() 函数。 部分助理信件保存在 messages,当连接关闭时,服务器中止代理运行。 通话 send() 当一个响应在流 中断它的方式相同。 当组件卸载时中止待决请求; useCogitatorAgent 中止上一个 run() 当一个新的开始和开始 reset()。 。 。 .
const { send, stop, isLoading } = useCogitatorChat({ api: '/api/chat' });
// Cancel current request
if (isLoading) {
stop();
}
文档
全向导 : cogitator.app/docs/整合/nextjs
许可证
麻省理工学院