Z ZPI 文档 v0.3.8
介绍页

源码

packages/agent-sdk/src/messages.ts

typescript129 行查看原文
import { createHash, randomUUID } from "node:crypto";
import {
  type AssistantMessage,
  contentText,
  isJsonObject,
  type Message,
  type Usage as NativeUsage,
} from "@zpi-dev/ai";
import type { SessionEntry } from "@zpi-dev/coding-agent";
import type { SDKAssistantMessage, SDKUserMessage, Usage } from "./types.ts";

export const emptyUsage = (): Usage => ({
  input_tokens: 0,
  output_tokens: 0,
  cache_creation_input_tokens: 0,
  cache_read_input_tokens: 0,
  server_tool_use: { web_search_requests: 0, web_fetch_requests: 0 },
});
export function usage(value: NativeUsage): Usage {
  return {
    ...emptyUsage(),
    input_tokens: value.input,
    output_tokens: value.output,
    cache_creation_input_tokens: value.cacheWrite,
    cache_read_input_tokens: value.cacheRead,
  };
}
export function stableUuid(id: string): string {
  const h = createHash("sha256").update(id).digest("hex");
  return `${h.slice(0, 8)}-${h.slice(8, 12)}-5${h.slice(13, 16)}-8${h.slice(17, 20)}-${h.slice(20, 32)}`;
}
export function messageText(message: Message): string {
  return contentText(message.content);
}
export function assistantMessage(
  message: AssistantMessage,
  sessionId: string,
  uuid: string = randomUUID(),
  parent: string | null = null,
): SDKAssistantMessage {
  return {
    type: "assistant",
    uuid,
    session_id: sessionId,
    parent_tool_use_id: parent,
    ...(message.stopReason === "error" ? { error: "unknown" as const } : {}),
    ...(message.stopReason === "aborted" ? { aborted: true as const } : {}),
    message: {
      id: `msg_${uuid}`,
      type: "message",
      role: "assistant",
      model: message.model,
      content: message.content.map((part) =>
        part.type === "text"
          ? { type: "text", text: part.text }
          : part.type === "thinking"
            ? { type: "thinking", thinking: part.thinking, signature: part.thinkingSignature ?? "" }
            : {
                type: "tool_use",
                id: part.id,
                name: part.name,
                input: part.arguments as Record<string, unknown>,
              },
      ),
      stop_reason:
        message.stopReason === "toolUse"
          ? "tool_use"
          : message.stopReason === "length"
            ? "max_tokens"
            : ["aborted", "error"].includes(message.stopReason)
              ? null
              : "end_turn",
      stop_sequence: null,
      usage: usage(message.usage),
    },
  };
}
export function userMessage(
  message: Extract<Message, { role: "user" | "toolResult" }>,
  sessionId: string,
  uuid: string,
  parent: string | null = null,
): SDKUserMessage {
  const content =
    typeof message.content === "string"
      ? message.content
      : message.content.map((part) =>
          part.type === "text"
            ? { type: "text" as const, text: part.text }
            : {
                type: "image" as const,
                source: { type: "base64" as const, media_type: part.mimeType, data: part.data },
              },
        );
  return {
    type: "user",
    uuid,
    session_id: sessionId,
    parent_tool_use_id: parent,
    message: {
      role: "user",
      content:
        message.role === "toolResult"
          ? [{ type: "tool_result", tool_use_id: message.toolCallId, content, is_error: message.isError }]
          : content,
    },
  };
}
export function messageUuids(entries: readonly SessionEntry[]): Map<string, string> {
  const ids = new Map<string, string>();
  let user: { uuid: string; text: string } | undefined;
  for (const entry of entries) {
    if (
      entry.type === "custom" &&
      entry.customType === "sdk.user" &&
      isJsonObject(entry.data) &&
      typeof entry.data.uuid === "string" &&
      typeof entry.data.text === "string"
    )
      user = { uuid: entry.data.uuid, text: entry.data.text };
    if (entry.type === "message") {
      const isUser = entry.message.role === "user" && user && messageText(entry.message) === user.text;
      ids.set(entry.id, isUser && user ? user.uuid : stableUuid(entry.id));
      if (isUser) user = undefined;
    }
  }
  return ids;
}