fix: unify chat process and reply presentation
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const id = z.string().min(1).max(128);
|
||||
const revision = z.number().int().nonnegative();
|
||||
const option = z.object({ id, label: z.string().max(200) }).strict();
|
||||
export const interactionAnswerSchema = z.object({ question_id: id, selected_option_ids: z.array(id).max(12), custom_text: z.string().max(2000) }).strict();
|
||||
export const interactionRequestSchema = z.object({
|
||||
version: z.literal("agent-interaction-request@1"), interaction_id: id, message_id: id, turn_id: id, call_id: id,
|
||||
questions: z.array(z.object({ question_id: id, title: z.string().max(200), body: z.string().max(2000), options: z.array(option).max(12), mode: z.enum(["single", "multiple"]), allow_custom: z.boolean(), required: z.boolean() }).strict()).min(1).max(3),
|
||||
status: z.enum(["pending", "answered", "skipped", "cancelled", "invalidated"]), revision, draft_revision: revision,
|
||||
draft: z.array(interactionAnswerSchema).max(3), answers: z.array(interactionAnswerSchema.extend({ selected_options: z.array(option) }).strict()).optional(),
|
||||
created_at: z.string(), resolved_at: z.string().optional(),
|
||||
delivery: z.object({ native_message_id: id, revision, status: z.enum(["queued", "dispatching", "delivered", "blocked", "cancelled"]), error_code: z.string().optional(), response_id: z.string().optional() }).strict().optional(),
|
||||
}).strict();
|
||||
export type InteractionRequest = z.infer<typeof interactionRequestSchema>;
|
||||
export const canReplaceInteractionRequest = (next: InteractionRequest, current: InteractionRequest) =>
|
||||
(next.delivery?.revision ?? -1) >= (current.delivery?.revision ?? -1)
|
||||
&& (next.revision > current.revision || (next.revision === current.revision && next.draft_revision >= current.draft_revision));
|
||||
export function readInteractionReply(data: unknown, messageId: string): InteractionRequest | undefined {
|
||||
const parsed = interactionRequestSchema.safeParse(data);
|
||||
return parsed.success && ["answered", "skipped"].includes(parsed.data.status)
|
||||
&& parsed.data.delivery?.native_message_id === messageId ? parsed.data : undefined;
|
||||
}
|
||||
export type InteractionAnswer = z.infer<typeof interactionAnswerSchema>;
|
||||
export type InteractionWrite = { version: "agent-interaction-request@1"; request_id: string; expected_revision: number; answers?: InteractionAnswer[] };
|
||||
export type InteractionDraftWrite = { version: "agent-interaction-request@1"; request_id: string; expected_draft_revision: number; answers: InteractionAnswer[] };
|
||||
export class InteractionRequestApiError extends Error {
|
||||
constructor(message: string, readonly status: number, readonly current?: InteractionRequest) { super(message); }
|
||||
}
|
||||
export function createInteractionRequestApi(request: (path: string, init?: RequestInit) => Promise<Response>) {
|
||||
const path = (sessionId: string, interactionId?: string) => `/session/${encodeURIComponent(sessionId)}/interactions${interactionId ? `/${encodeURIComponent(interactionId)}` : ""}`;
|
||||
const read = async (response: Response) => {
|
||||
const value: unknown = await response.json().catch(() => {
|
||||
throw new InteractionRequestApiError("提问状态暂不可用", response.status);
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = z.object({ detail: z.string().optional(), current: z.unknown().optional() }).passthrough().safeParse(value);
|
||||
const current = interactionRequestSchema.safeParse(error.success ? error.data.current : null);
|
||||
throw new InteractionRequestApiError(error.success ? error.data.detail ?? "提问状态暂不可用" : "提问状态暂不可用", response.status, current.success ? current.data : undefined);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
return {
|
||||
async listInteractionRequests(sessionId: string) { return z.object({ items: z.array(interactionRequestSchema), next_cursor: z.string().optional() }).strict().parse(await read(await request(path(sessionId), { cache: "no-store" }))); },
|
||||
async getInteractionRequest(sessionId: string, interactionId: string) { return interactionRequestSchema.parse(await read(await request(path(sessionId, interactionId), { cache: "no-store" }))); },
|
||||
async writeInteractionRequest(sessionId: string, interactionId: string, action: "draft" | "reply" | "skip" | "cancel" | "resume", body: InteractionWrite | InteractionDraftWrite) {
|
||||
return interactionRequestSchema.parse(await read(await request(`${path(sessionId, interactionId)}/${action}`, { method: action === "draft" ? "PUT" : "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) })));
|
||||
},
|
||||
};
|
||||
}
|
||||
export type InteractionRequestApi = ReturnType<typeof createInteractionRequestApi>;
|
||||
Reference in New Issue
Block a user