Files
Genarrative/packages/shared/src/contracts/editorAgent.ts
T
k88936 66a8b7f597
Project CI / Repository checks (push) Successful in 53s
Project CI / Frontend tests (push) Successful in 2m34s
Project CI / Native shell tests (push) Successful in 2m42s
Project CI / Backend tests (push) Successful in 3m28s
画布agent 完善上下文 (#101)
* 补全完整的工具参数
* 调整提示词应对一些bad case
* 把attachment的名称/描述纳入上下文

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/101
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-07-28 15:44:05 +08:00

218 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 画布Agent对话契约:会话元数据存 SpacetimeDB,消息正文整体存 OSSeditor-agent/{conversationId}.json)。
export const EDITOR_AGENT_MAX_ATTACHMENTS = 9;
export const EDITOR_AGENT_ATTACHMENT_LABEL_MAX_CODE_POINTS = 24;
export const EDITOR_AGENT_ERROR_MESSAGE_PREFIX = 'ERROR ';
export type EditorAgentMessageRole = 'user' | 'assistant' | 'system';
export type EditorAgentToolCallStatus =
'not_completed' | 'completed' | 'failed' | 'cancelled';
export type EditorAgentAttachmentSource = 'canvas_resource' | 'library_asset';
function isUnsafeEditorAgentAttachmentLabelCharacter(character: string) {
const codePoint = character.codePointAt(0) ?? 0;
const isControlCharacter =
codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);
const isAsciiPunctuation =
(codePoint >= 0x21 && codePoint <= 0x2f) ||
(codePoint >= 0x3a && codePoint <= 0x40) ||
(codePoint >= 0x5b && codePoint <= 0x60) ||
(codePoint >= 0x7b && codePoint <= 0x7e);
const isUnsafeAsciiPunctuation =
isAsciiPunctuation &&
character !== '-' &&
character !== '_' &&
character !== '.';
return isControlCharacter || isUnsafeAsciiPunctuation;
}
function normalizeEditorAgentAttachmentLabel(
label: string | null | undefined,
fallback: string,
) {
const normalizeCandidate = (candidate: string | null | undefined) => {
const normalized: string[] = [];
let codePoints = 0;
let pendingSpace = false;
for (const character of candidate?.trim() ?? '') {
if (isUnsafeEditorAgentAttachmentLabelCharacter(character)) {
continue;
}
if (/\s/u.test(character)) {
pendingSpace = normalized.length > 0;
continue;
}
if (
pendingSpace &&
codePoints + 1 < EDITOR_AGENT_ATTACHMENT_LABEL_MAX_CODE_POINTS
) {
normalized.push(' ');
codePoints += 1;
}
pendingSpace = false;
if (codePoints >= EDITOR_AGENT_ATTACHMENT_LABEL_MAX_CODE_POINTS) {
break;
}
normalized.push(character);
codePoints += 1;
}
return normalized.join('').trim();
};
return normalizeCandidate(label) || normalizeCandidate(fallback) || '图片';
}
export interface EditorAgentAttachmentRef {
source: EditorAgentAttachmentSource;
referenceId: string;
objectKey?: string | null;
imageSrc: string;
thumbnailSrc?: string | null;
label?: string | null;
width?: number | null;
height?: number | null;
}
export function createEditorAgentAttachmentRef(
input: EditorAgentAttachmentRef,
fallbackLabel: string = input.referenceId,
) {
return {
...input,
label: normalizeEditorAgentAttachmentLabel(input.label, fallbackLabel),
};
}
export interface EditorAgentGeneratedImage {
resourceId?: string | null;
objectKey?: string | null;
assetObjectId?: string | null;
imageSrc: string;
thumbnailSrc?: string | null;
width?: number | null;
height?: number | null;
}
export interface EditorAgentGeneratedVideo {
resourceId?: string | null;
objectKey?: string | null;
assetObjectId?: string | null;
videoSrc: string;
thumbnailSrc?: string | null;
width?: number | null;
height?: number | null;
}
export interface EditorAgentGeneratedAudio {
resourceId?: string | null;
objectKey?: string | null;
assetObjectId?: string | null;
audioSrc: string;
}
export interface EditorAgentToolCallStringArg {
name: string;
label: string;
value: string;
}
export interface EditorAgentToolCallImageRef {
imageId: string;
imageSrc: string;
objectKey?: string | null;
thumbnailSrc?: string | null;
label?: string | null;
width?: number | null;
height?: number | null;
}
export interface EditorAgentToolCallImageArg {
name: string;
label: string;
refs: EditorAgentToolCallImageRef[];
}
export interface EditorAgentToolCallDisplayExtras {
priceMudPoints: number;
}
export interface EditorAgentToolCallDisplayArgs {
stringArgs: EditorAgentToolCallStringArg[];
imageArgs: EditorAgentToolCallImageArg[];
extras: EditorAgentToolCallDisplayExtras;
}
export interface EditorAgentToolCall {
toolName: string;
status: EditorAgentToolCallStatus;
args: unknown;
displayArgs: EditorAgentToolCallDisplayArgs;
externalJobId?: string | null;
images: EditorAgentGeneratedImage[];
// Older persisted conversation documents do not contain these media fields.
videos?: EditorAgentGeneratedVideo[];
audios?: EditorAgentGeneratedAudio[];
error?: string | null;
}
export interface EditorAgentMessage {
// Frontend must not use this to organize messages.
// It is an opaque backend locator for pending tool-call operations.
id: number;
// Present only on user messages created from a client send request.
clientMessageId?: string | null;
role: EditorAgentMessageRole;
text: string;
attachments: EditorAgentAttachmentRef[];
toolCall: EditorAgentToolCall | null;
createdAt: string;
}
export interface EditorAgentConversationSummary {
conversationId: string;
projectId: string;
title: string;
updatedAt: string;
}
export interface EditorAgentConversationDetail {
conversationId: string;
projectId: string;
title: string;
createdAt: string;
updatedAt: string;
messages: EditorAgentMessage[];
}
export interface EditorAgentConversationMessagesDocument {
version: number;
conversationId: string;
messages: EditorAgentMessage[];
}
export interface CreateEditorAgentConversationRequest {
title?: string | null;
}
export interface EditorAgentConversationListResponse {
conversations: EditorAgentConversationSummary[];
}
export interface EditorAgentConversationResponse {
conversation: EditorAgentConversationDetail;
}
export interface EditorAgentMessageRequest {
clientMessageId: string;
text: string;
attachments?: EditorAgentAttachmentRef[];
}
export interface EditorAgentMessageResponse {
conversation: EditorAgentConversationSummary;
deltaMessages: EditorAgentMessage[];
errorMessage: string | null;
}