release: prepare 0629001 portal updates
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
export const CHAT_IMAGE_UPLOAD_MAX_INPUT_BYTES = 8 * 1024 * 1024;
|
||||
export const CHAT_IMAGE_UPLOAD_MAX_INPUT_BYTES = 4 * 1024 * 1024;
|
||||
export const CHAT_IMAGE_UPLOAD_MAX_OUTPUT_BYTES = 1.5 * 1024 * 1024;
|
||||
export const MINDSPACE_IMAGE_UPLOAD_MAX_BYTES = 4 * 1024 * 1024;
|
||||
export const CHAT_IMAGE_UPLOAD_MAX_COUNT = 10;
|
||||
export const CHAT_IMAGE_UPLOAD_MAX_TOTAL_BYTES = 20 * 1024 * 1024;
|
||||
export const CHAT_IMAGE_MAX_SIDE = 1920;
|
||||
const MAX_PIXELS = 2_500_000;
|
||||
const ACCEPTED_IMAGE_MIME = new Set(['image/jpeg', 'image/png', 'image/webp']);
|
||||
|
||||
+22
-11
@@ -3,6 +3,9 @@ import { mergeMessageContent } from '../../message-stream.mjs';
|
||||
import { stripUserAddressPrefix } from './userAddress';
|
||||
|
||||
const IMAGE_URL_LINE_RE = /^\[图片\d+]: (.+)$/;
|
||||
const IMAGE_URL_LINES_RE = /\n*\[图片\d+]: [^\n]+/g;
|
||||
const USER_IDENTITY_BLOCK_RE = /^\[用户身份\][\s\S]*?(?:\n{2,}|$)/;
|
||||
const TKMIND_VISION_NOTE_RE = /\n*【TKMind 图片分析结果[\s\S]*$/;
|
||||
|
||||
function normalizeImageUrl(url: string): string {
|
||||
const value = String(url ?? '').trim();
|
||||
@@ -32,6 +35,14 @@ function formatImageUrlsForAgentText(urls: string[]): string {
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function stripImageUrlLines(text: string): string {
|
||||
return String(text ?? '')
|
||||
.replace(USER_IDENTITY_BLOCK_RE, '')
|
||||
.replace(TKMIND_VISION_NOTE_RE, '')
|
||||
.replace(IMAGE_URL_LINES_RE, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function parseImageUrlsFromText(text: string): string[] {
|
||||
const urls: string[] = [];
|
||||
for (const line of text.split('\n')) {
|
||||
@@ -71,12 +82,12 @@ export function normalizeUserMessageForApi(message: Message): Message {
|
||||
const imageUrls = getImageUrls(message);
|
||||
const displayText =
|
||||
message.metadata.displayText ??
|
||||
message.content
|
||||
.filter((item): item is Extract<MessageContent, { type: 'text' }> => item.type === 'text')
|
||||
.map((item) => item.text)
|
||||
.join('\n')
|
||||
.replace(/\n*\[图片\d+]: [^\n]+/g, '')
|
||||
.trim();
|
||||
stripImageUrlLines(
|
||||
message.content
|
||||
.filter((item): item is Extract<MessageContent, { type: 'text' }> => item.type === 'text')
|
||||
.map((item) => item.text)
|
||||
.join('\n'),
|
||||
);
|
||||
|
||||
const textOnly = message.content
|
||||
.filter((item): item is Extract<MessageContent, { type: 'text' }> => item.type === 'text')
|
||||
@@ -89,7 +100,7 @@ export function normalizeUserMessageForApi(message: Message): Message {
|
||||
}
|
||||
|
||||
const imageText = formatImageUrlsForAgentText(imageUrls);
|
||||
const agentText = [textOnly.replace(/\n*\[图片\d+]: [^\n]+/g, '').trim(), imageText]
|
||||
const agentText = [stripImageUrlLines(textOnly), imageText]
|
||||
.filter(Boolean)
|
||||
.join('\n\n');
|
||||
|
||||
@@ -108,7 +119,7 @@ export function buildUserMessage(
|
||||
text: string,
|
||||
options?: { agentText?: string; displayText?: string; imageUrls?: string[]; previewImageUrls?: string[] },
|
||||
): Message {
|
||||
const displayText = options?.displayText ?? text;
|
||||
const displayText = stripImageUrlLines(options?.displayText ?? text);
|
||||
const imageUrls = (options?.imageUrls ?? []).filter((value) => typeof value === 'string' && value.trim());
|
||||
const previewImageUrls = (options?.previewImageUrls ?? []).filter(
|
||||
(value) => typeof value === 'string' && value.trim(),
|
||||
@@ -160,12 +171,12 @@ export function mergeConversationSnapshot(current: Message[], incoming: Message[
|
||||
|
||||
export function getDisplayText(message: Message): string {
|
||||
if ('displayText' in message.metadata) {
|
||||
return message.metadata.displayText ?? '';
|
||||
return stripImageUrlLines(message.metadata.displayText ?? '');
|
||||
}
|
||||
const systemText = getSystemNotificationText(message);
|
||||
if (systemText) return systemText;
|
||||
const visible = getVisibleText(message);
|
||||
return message.role === 'user' ? stripUserAddressPrefix(visible) : visible;
|
||||
return message.role === 'user' ? stripImageUrlLines(stripUserAddressPrefix(visible)) : visible;
|
||||
}
|
||||
|
||||
export function pushMessage(messages: Message[], incoming: Message): Message[] {
|
||||
@@ -187,7 +198,7 @@ export function getVisibleText(message: Message): string {
|
||||
.filter((c): c is Extract<MessageContent, { type: 'text' }> => c.type === 'text')
|
||||
.map((c) => c.text)
|
||||
.join('');
|
||||
return text.replace(/\n*\[图片\d+]: [^\n]+/g, '').trim();
|
||||
return stripImageUrlLines(text);
|
||||
}
|
||||
|
||||
export function getImageUrls(message: Message): string[] {
|
||||
|
||||
+44
-17
@@ -1,5 +1,4 @@
|
||||
import { appConfig } from '../config';
|
||||
import type { Session } from '../types';
|
||||
import type { Session, SessionSummary } from '../types';
|
||||
|
||||
export const DEFAULT_CHAT_TITLE = 'New Chat';
|
||||
|
||||
@@ -12,28 +11,42 @@ function isDefaultSessionName(name: string): boolean {
|
||||
return !trimmed || trimmed === DEFAULT_CHAT_TITLE || trimmed === '新对话' || isGeneratedSessionName(trimmed);
|
||||
}
|
||||
|
||||
export function shouldShowNewChatTitle(session: Session): boolean {
|
||||
export function shouldShowNewChatTitle(session: SessionSummary): boolean {
|
||||
if (session.recipe) return false;
|
||||
return !session.user_set_name && session.message_count === 0 && isDefaultSessionName(session.name);
|
||||
}
|
||||
|
||||
export function sortAndTrim(sessions: Session[]): Session[] {
|
||||
export function sortAndTrim<T extends SessionSummary>(sessions: T[]): T[] {
|
||||
return [...sessions]
|
||||
.sort((a, b) => {
|
||||
const aTime = new Date(a.updated_at ?? a.created_at ?? 0).getTime();
|
||||
const bTime = new Date(b.updated_at ?? b.created_at ?? 0).getTime();
|
||||
return bTime - aTime;
|
||||
})
|
||||
.slice(0, appConfig.sessionMaxCount);
|
||||
});
|
||||
}
|
||||
|
||||
export function hasMeaningfulSessionTitle(session: Session): boolean {
|
||||
export function hasMeaningfulSessionTitle(session: SessionSummary): boolean {
|
||||
if (session.user_set_name) return Boolean(session.name.trim());
|
||||
if (session.recipe?.title?.trim()) return true;
|
||||
return !isDefaultSessionName(session.name);
|
||||
}
|
||||
|
||||
export function mergeSessionRecord(existing: Session | undefined, incoming: Session): Session {
|
||||
export function toSessionSummary(session: Session | SessionSummary): SessionSummary {
|
||||
return {
|
||||
id: session.id,
|
||||
name: session.name,
|
||||
message_count: session.message_count,
|
||||
created_at: session.created_at,
|
||||
updated_at: session.updated_at,
|
||||
user_set_name: session.user_set_name,
|
||||
recipe: session.recipe ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeSessionRecord(
|
||||
existing: SessionSummary | undefined,
|
||||
incoming: SessionSummary,
|
||||
): SessionSummary {
|
||||
if (!existing) return incoming;
|
||||
|
||||
const keepExistingTitle =
|
||||
@@ -50,19 +63,33 @@ export function mergeSessionRecord(existing: Session | undefined, incoming: Sess
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeSessionLists(prev: Session[], incoming: Session[]): Session[] {
|
||||
export function mergeSessionLists(prev: SessionSummary[], incoming: SessionSummary[]): SessionSummary[] {
|
||||
const previousById = new Map(prev.map((item) => [item.id, item]));
|
||||
return sortAndTrim(incoming.map((item) => mergeSessionRecord(previousById.get(item.id), item)));
|
||||
}
|
||||
|
||||
export function prependUnique(prev: Session[], session: Session): Session[] {
|
||||
export function appendSessionLists(prev: SessionSummary[], incoming: SessionSummary[]): SessionSummary[] {
|
||||
const previousById = new Map(prev.map((item) => [item.id, item]));
|
||||
const mergedIncoming = incoming.map((item) => mergeSessionRecord(previousById.get(item.id), item));
|
||||
const incomingIds = new Set(mergedIncoming.map((item) => item.id));
|
||||
return sortAndTrim([
|
||||
...prev.filter((item) => !incomingIds.has(item.id)),
|
||||
...mergedIncoming,
|
||||
]);
|
||||
}
|
||||
|
||||
export function prependUnique(prev: SessionSummary[], session: SessionSummary): SessionSummary[] {
|
||||
return sortAndTrim([
|
||||
mergeSessionRecord(prev.find((s) => s.id === session.id), session),
|
||||
...prev.filter((s) => s.id !== session.id),
|
||||
]);
|
||||
}
|
||||
|
||||
export function touchSession(sessions: Session[], sessionId: string, messageDelta = 0): Session[] {
|
||||
export function touchSession(
|
||||
sessions: SessionSummary[],
|
||||
sessionId: string,
|
||||
messageDelta = 0,
|
||||
): SessionSummary[] {
|
||||
const now = new Date().toISOString();
|
||||
return sortAndTrim(
|
||||
sessions.map((s) =>
|
||||
@@ -77,7 +104,7 @@ export function touchSession(sessions: Session[], sessionId: string, messageDelt
|
||||
);
|
||||
}
|
||||
|
||||
export function getSessionDisplayName(session: Session): string {
|
||||
export function getSessionDisplayName(session: SessionSummary): string {
|
||||
if (session.user_set_name) return session.name;
|
||||
if (session.recipe?.title) return session.recipe.title;
|
||||
if (shouldShowNewChatTitle(session)) return DEFAULT_CHAT_TITLE;
|
||||
@@ -92,7 +119,7 @@ function sameLocalDay(a: Date, b: Date): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export function getSessionDateKey(session: Session): string {
|
||||
export function getSessionDateKey(session: SessionSummary): string {
|
||||
const iso = session.updated_at ?? session.created_at;
|
||||
if (!iso) return '';
|
||||
const date = new Date(iso);
|
||||
@@ -100,7 +127,7 @@ export function getSessionDateKey(session: Session): string {
|
||||
return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
|
||||
}
|
||||
|
||||
export function formatSessionDateLabel(session: Session): string {
|
||||
export function formatSessionDateLabel(session: SessionSummary): string {
|
||||
const iso = session.updated_at ?? session.created_at;
|
||||
if (!iso) return '';
|
||||
const date = new Date(iso);
|
||||
@@ -123,10 +150,10 @@ export function formatSessionDateLabel(session: Session): string {
|
||||
export type SessionDateGroup = {
|
||||
dateKey: string;
|
||||
label: string;
|
||||
sessions: Session[];
|
||||
sessions: SessionSummary[];
|
||||
};
|
||||
|
||||
export function groupSessionsByDate(sessions: Session[]): SessionDateGroup[] {
|
||||
export function groupSessionsByDate(sessions: SessionSummary[]): SessionDateGroup[] {
|
||||
const groups: SessionDateGroup[] = [];
|
||||
for (const session of sessions) {
|
||||
const dateKey = getSessionDateKey(session);
|
||||
@@ -143,7 +170,7 @@ export function groupSessionsByDate(sessions: Session[]): SessionDateGroup[] {
|
||||
|
||||
const DEFAULT_SESSION_NAMES = new Set([DEFAULT_CHAT_TITLE, '新对话']);
|
||||
|
||||
export function getSessionListLabel(session: Session): string {
|
||||
export function getSessionListLabel(session: SessionSummary): string {
|
||||
const displayName = getSessionDisplayName(session).trim();
|
||||
if (displayName && !DEFAULT_SESSION_NAMES.has(displayName) && !isGeneratedSessionName(displayName)) {
|
||||
return displayName;
|
||||
|
||||
Reference in New Issue
Block a user