343 lines
9.0 KiB
JavaScript
343 lines
9.0 KiB
JavaScript
import crypto from 'node:crypto';
|
|
import path from 'node:path';
|
|
import { registerChatDocxArtifactForConversation } from './mindspace-chat-docx-package.mjs';
|
|
import { registerPublicHtmlArtifactsForConversationPackage } from './mindspace-conversation-package-public-html.mjs';
|
|
import { resolveMindSpaceUserPublishDir } from './mindspace-runtime-config.mjs';
|
|
|
|
function normalizeBuffer(value) {
|
|
if (Buffer.isBuffer(value)) return value;
|
|
if (typeof value === 'string') {
|
|
return Buffer.from(value, 'utf8');
|
|
}
|
|
if (ArrayBuffer.isView(value)) {
|
|
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function generatedArtifactMetadata(
|
|
relativePath,
|
|
) {
|
|
const extension = path.posix
|
|
.extname(relativePath)
|
|
.toLowerCase();
|
|
const byExtension = {
|
|
'.csv': [
|
|
'generated_file',
|
|
'text/csv',
|
|
],
|
|
'.doc': [
|
|
'docx',
|
|
'application/msword',
|
|
],
|
|
'.docx': [
|
|
'docx',
|
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
],
|
|
'.gif': ['generated_image', 'image/gif'],
|
|
'.jpeg': [
|
|
'generated_image',
|
|
'image/jpeg',
|
|
],
|
|
'.jpg': [
|
|
'generated_image',
|
|
'image/jpeg',
|
|
],
|
|
'.json': [
|
|
'generated_file',
|
|
'application/json',
|
|
],
|
|
'.md': [
|
|
'generated_file',
|
|
'text/markdown',
|
|
],
|
|
'.pdf': ['pdf', 'application/pdf'],
|
|
'.png': [
|
|
'generated_image',
|
|
'image/png',
|
|
],
|
|
'.svg': [
|
|
'generated_image',
|
|
'image/svg+xml',
|
|
],
|
|
'.txt': [
|
|
'generated_file',
|
|
'text/plain',
|
|
],
|
|
'.webp': [
|
|
'generated_image',
|
|
'image/webp',
|
|
],
|
|
'.xlsx': [
|
|
'generated_file',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
],
|
|
};
|
|
const [artifactKind, mimeType] =
|
|
byExtension[extension] ?? [
|
|
'generated_file',
|
|
'application/octet-stream',
|
|
];
|
|
return { artifactKind, mimeType };
|
|
}
|
|
|
|
export function createMindSpaceConversationArtifactService({
|
|
conversationPackageRegistry,
|
|
h5Root,
|
|
env = process.env,
|
|
resolveMindSpaceUserPublishDirFn =
|
|
resolveMindSpaceUserPublishDir,
|
|
registerPublicHtmlArtifactsFn =
|
|
registerPublicHtmlArtifactsForConversationPackage,
|
|
registerChatDocxArtifactFn =
|
|
registerChatDocxArtifactForConversation,
|
|
} = {}) {
|
|
if (!conversationPackageRegistry) {
|
|
throw new Error(
|
|
'createMindSpaceConversationArtifactService requires a conversation package registry',
|
|
);
|
|
}
|
|
if (!h5Root) {
|
|
throw new Error(
|
|
'createMindSpaceConversationArtifactService requires h5Root',
|
|
);
|
|
}
|
|
|
|
return {
|
|
async registerPublicHtmlArtifacts({
|
|
userId,
|
|
sessionId,
|
|
title = null,
|
|
relativePaths = [],
|
|
artifactRefs = [],
|
|
} = {}) {
|
|
const normalizedUserId = String(userId ?? '').trim();
|
|
if (
|
|
!normalizedUserId ||
|
|
!String(sessionId ?? '').trim()
|
|
) {
|
|
return [];
|
|
}
|
|
const user = { id: normalizedUserId };
|
|
const publishDir = resolveMindSpaceUserPublishDirFn(
|
|
h5Root,
|
|
user,
|
|
);
|
|
return registerPublicHtmlArtifactsFn({
|
|
conversationPackageRegistry,
|
|
h5Root,
|
|
env,
|
|
user,
|
|
publishDir,
|
|
sessionId,
|
|
title,
|
|
relativePaths,
|
|
artifactRefs,
|
|
});
|
|
},
|
|
|
|
async registerChatDocxArtifact({
|
|
userId,
|
|
sessionId,
|
|
messageId,
|
|
selectedLinkIndex = 0,
|
|
title = null,
|
|
filename,
|
|
body,
|
|
} = {}) {
|
|
const buffer = normalizeBuffer(body);
|
|
if (
|
|
!String(userId ?? '').trim() ||
|
|
!String(sessionId ?? '').trim() ||
|
|
!String(messageId ?? '').trim() ||
|
|
!String(filename ?? '').trim() ||
|
|
!buffer
|
|
) {
|
|
return null;
|
|
}
|
|
return registerChatDocxArtifactFn({
|
|
registry: conversationPackageRegistry,
|
|
user: { id: String(userId).trim() },
|
|
bundle: {
|
|
source: {
|
|
session: {
|
|
name: String(title ?? '').trim() || null,
|
|
},
|
|
},
|
|
},
|
|
requestBody: {
|
|
session_id: String(sessionId).trim(),
|
|
message_id: String(messageId).trim(),
|
|
selected_link_index: selectedLinkIndex,
|
|
},
|
|
filename: String(filename).trim(),
|
|
buffer,
|
|
});
|
|
},
|
|
|
|
async registerPublishedLongImageArtifact({
|
|
ownerId,
|
|
sessionId,
|
|
messageId = null,
|
|
publicationId,
|
|
pageId = null,
|
|
title = null,
|
|
filename = null,
|
|
body,
|
|
canonicalUrl = null,
|
|
} = {}) {
|
|
const normalizedOwnerId = String(ownerId ?? '').trim();
|
|
const normalizedSessionId = String(sessionId ?? '').trim();
|
|
const normalizedPublicationId =
|
|
String(publicationId ?? '').trim();
|
|
const buffer = normalizeBuffer(body);
|
|
if (
|
|
!normalizedOwnerId ||
|
|
!normalizedSessionId ||
|
|
!normalizedPublicationId ||
|
|
!buffer
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
const hash = crypto
|
|
.createHash('sha256')
|
|
.update(
|
|
`${normalizedOwnerId}:${normalizedSessionId}:${normalizedPublicationId}:long-image`,
|
|
)
|
|
.digest('hex')
|
|
.slice(0, 16);
|
|
const artifactId = `ca_long_image_${hash}`;
|
|
const displayName =
|
|
String(filename ?? '').trim() ||
|
|
`${normalizedPublicationId}.long.png`;
|
|
const relativePath =
|
|
`artifacts/${artifactId}/${path.posix.basename(displayName)}`;
|
|
const now = Date.now();
|
|
const { packageRecord, writeResult } =
|
|
await conversationPackageRegistry.putObjectForSession({
|
|
userId: normalizedOwnerId,
|
|
sessionId: normalizedSessionId,
|
|
title: String(title ?? '').trim() || null,
|
|
relativePath,
|
|
body: buffer,
|
|
});
|
|
const artifact =
|
|
await conversationPackageRegistry.recordArtifact({
|
|
id: artifactId,
|
|
packageId: packageRecord.id,
|
|
artifactKind: 'long_image',
|
|
role: 'assistant',
|
|
pageId: String(pageId ?? '').trim() || null,
|
|
publicationId: normalizedPublicationId,
|
|
messageId: String(messageId ?? '').trim() || null,
|
|
displayName: path.posix.basename(displayName),
|
|
mimeType: 'image/png',
|
|
sizeBytes: buffer.length,
|
|
storageKey: writeResult.key,
|
|
canonicalUrl:
|
|
String(canonicalUrl ?? '').trim() || null,
|
|
sortOrder: now,
|
|
now,
|
|
});
|
|
await conversationPackageRegistry.writeManifestForSession({
|
|
userId: normalizedOwnerId,
|
|
sessionId: normalizedSessionId,
|
|
});
|
|
return artifact;
|
|
},
|
|
|
|
async registerWorkspaceFileArtifact({
|
|
userId,
|
|
sessionId,
|
|
messageId = null,
|
|
title = null,
|
|
relativePath,
|
|
body,
|
|
} = {}) {
|
|
const normalizedUserId = String(
|
|
userId ?? '',
|
|
).trim();
|
|
const normalizedSessionId = String(
|
|
sessionId ?? '',
|
|
).trim();
|
|
const normalizedRelativePath = String(
|
|
relativePath ?? '',
|
|
)
|
|
.trim()
|
|
.replace(/\\/g, '/');
|
|
const buffer = normalizeBuffer(body);
|
|
if (
|
|
!normalizedUserId ||
|
|
!normalizedSessionId ||
|
|
!normalizedRelativePath ||
|
|
!buffer
|
|
) {
|
|
return null;
|
|
}
|
|
const hash = crypto
|
|
.createHash('sha256')
|
|
.update(
|
|
`${normalizedUserId}:${normalizedSessionId}:${normalizedRelativePath}`,
|
|
)
|
|
.digest('hex')
|
|
.slice(0, 20);
|
|
const artifactId =
|
|
`ca_workspace_write_${hash}`;
|
|
const displayName =
|
|
path.posix.basename(
|
|
normalizedRelativePath,
|
|
);
|
|
const packageRelativePath =
|
|
`artifacts/${artifactId}/${displayName}`;
|
|
const metadata =
|
|
generatedArtifactMetadata(
|
|
normalizedRelativePath,
|
|
);
|
|
const now = Date.now();
|
|
const { packageRecord, writeResult } =
|
|
await conversationPackageRegistry
|
|
.putObjectForSession({
|
|
userId: normalizedUserId,
|
|
sessionId: normalizedSessionId,
|
|
title:
|
|
String(title ?? '').trim() ||
|
|
null,
|
|
relativePath:
|
|
packageRelativePath,
|
|
body: buffer,
|
|
});
|
|
const artifact =
|
|
await conversationPackageRegistry
|
|
.recordArtifact({
|
|
id: artifactId,
|
|
packageId: packageRecord.id,
|
|
artifactKind:
|
|
metadata.artifactKind,
|
|
role: 'assistant',
|
|
messageId:
|
|
String(messageId ?? '').trim() ||
|
|
null,
|
|
displayName,
|
|
mimeType: metadata.mimeType,
|
|
sizeBytes: buffer.length,
|
|
storageKey: writeResult.key,
|
|
canonicalUrl: null,
|
|
sortOrder: now,
|
|
now,
|
|
});
|
|
await conversationPackageRegistry
|
|
.writeManifestForSession({
|
|
userId: normalizedUserId,
|
|
sessionId: normalizedSessionId,
|
|
});
|
|
return artifact;
|
|
},
|
|
};
|
|
}
|
|
|
|
export const mindSpaceConversationArtifactServiceInternals = {
|
|
normalizeBuffer,
|
|
generatedArtifactMetadata,
|
|
};
|