Fragments: ITCF.

This commit is contained in:
Enrico Ros
2024-10-16 19:13:24 -07:00
parent 57bb1edcfc
commit a7ab95e905
8 changed files with 48 additions and 40 deletions
@@ -1,7 +1,7 @@
import * as React from 'react';
import type { DMessageId } from '~/common/stores/chat/chat.message';
import { createTextContentFragment, DMessageFragment, DMessageFragmentId, isContentFragment, isTextPart } from '~/common/stores/chat/chat.fragments';
import { createTextContentFragment, DMessageFragment, DMessageFragmentId, isTextContentFragment } from '~/common/stores/chat/chat.fragments';
import { wrapWithMarkdownSyntax } from '~/modules/blocks/markdown/markdown.wrapper';
import { BUBBLE_MIN_TEXT_LENGTH } from './ChatMessage';
@@ -41,7 +41,7 @@ export function useSelHighlighterMemo(
// Create the highlighter function, if there's 1 and only 1 occurrence of the selection
const highlightFunction = fragments.reduce((acc: false /* not found */ | ((tool: HighlightTool) => void) | true /* more than one */, fragment) => {
if (!acc && isContentFragment(fragment) && isTextPart(fragment.part)) {
if (!acc && isTextContentFragment(fragment)) {
const fragmentText = fragment.part.text;
let index = fragmentText.indexOf(selText);
+3 -3
View File
@@ -4,7 +4,7 @@ import type { DConversationId } from '~/common/stores/chat/chat.conversation';
import type { DMessage } from '~/common/stores/chat/chat.message';
import { ConversationHandler } from '~/common/chat-overlay/ConversationHandler';
import { ConversationsManager } from '~/common/chat-overlay/ConversationsManager';
import { createTextContentFragment, isContentFragment, isTextPart } from '~/common/stores/chat/chat.fragments';
import { createTextContentFragment, isTextContentFragment } from '~/common/stores/chat/chat.fragments';
import { getConversationSystemPurposeId } from '~/common/stores/chat/store-chats';
import type { ChatExecuteMode } from '../execute-mode/execute-mode.types';
@@ -80,7 +80,7 @@ export async function _handleExecute(chatExecuteMode: ChatExecuteMode, conversat
case 'generate-image':
// verify we were called with a single DMessageTextContent
if (!isContentFragment(firstFragment) || !isTextPart(firstFragment.part))
if (!isTextContentFragment(firstFragment))
return false;
const imagePrompt = firstFragment.part.text;
cHandler.messageFragmentReplace(lastMessage.id, firstFragment.fId, createTextContentFragment(textToDrawCommand(imagePrompt)), true);
@@ -88,7 +88,7 @@ export async function _handleExecute(chatExecuteMode: ChatExecuteMode, conversat
case 'react-content':
// verify we were called with a single DMessageTextContent
if (!isContentFragment(firstFragment) || !isTextPart(firstFragment.part))
if (!isTextContentFragment(firstFragment))
return false;
const reactPrompt = firstFragment.part.text;
cHandler.messageFragmentReplace(lastMessage.id, firstFragment.fId, createTextContentFragment(textToDrawCommand(reactPrompt)), true);
@@ -1,7 +1,7 @@
import type { DLLMId } from '~/common/stores/llms/llms.types';
import type { DMessage, DMessageId } from '~/common/stores/chat/chat.message';
import { ConversationHandler } from '~/common/chat-overlay/ConversationHandler';
import { createTextContentFragment, DMessageFragment, isContentFragment, isTextPart } from '~/common/stores/chat/chat.fragments';
import { createTextContentFragment, DMessageFragment, isTextContentFragment } from '~/common/stores/chat/chat.fragments';
import { extractChatCommand, helpPrettyChatCommands } from '../commands/commands.registry';
import { runBrowseGetPageUpdatingState } from './browse-load';
@@ -15,7 +15,7 @@ export const RET_NO_CMD = 'no-cmd';
export async function _handleExecuteCommand(lastMessageId: DMessageId, lastMessageFirstFragment: DMessageFragment, lastMessage: Readonly<DMessage>, cHandler: ConversationHandler, chatLLMId: DLLMId) {
// commands must have a first Content DMessageTextPart
if (!isContentFragment(lastMessageFirstFragment) || !isTextPart(lastMessageFirstFragment.part))
if (!isTextContentFragment(lastMessageFirstFragment))
return RET_NO_CMD;
// check if we have a command
@@ -1,6 +1,6 @@
import { speakText } from '~/modules/elevenlabs/elevenlabs.client';
import { isContentFragment, isTextPart } from '~/common/stores/chat/chat.fragments';
import { isTextContentFragment } from '~/common/stores/chat/chat.fragments';
import type { AixChatGenerateContent_DMessage } from '~/modules/aix/client/aix.client';
@@ -20,7 +20,7 @@ export class PersonaChatMessageSpeak implements PersonaProcessorInterface {
if (this.autoSpeakType === 'off' || this.spokenLine) return;
// Require a Content.Text first fragment
if (!accumulatedMessage.fragments?.length || !isContentFragment(accumulatedMessage.fragments[0]) || !isTextPart(accumulatedMessage.fragments[0].part))
if (!accumulatedMessage.fragments?.length || !isTextContentFragment(accumulatedMessage.fragments[0]))
return;
const text = accumulatedMessage.fragments[0].part.text;
+7 -3
View File
@@ -176,6 +176,10 @@ export function isContentFragment(fragment: DMessageFragment) {
return fragment.ft === 'content';
}
export function isTextContentFragment(fragment: DMessageFragment): fragment is DMessageContentFragment & { part: DMessageTextPart } {
return fragment.ft === 'content' && fragment.part.pt === 'text';
}
export function isAttachmentFragment(fragment: DMessageFragment) {
return fragment.ft === 'attachment';
}
@@ -205,7 +209,7 @@ export function isErrorPart(part: DMessageContentFragment['part']) {
return part.pt === 'error';
}
export function isToolResponseFunctionCallPart(part: DMessageContentFragment['part']) {
export function isToolResponseFunctionCallPart(part: DMessageContentFragment['part']): part is DMessageToolResponsePart & { response: { type: 'function_call' } } {
return part.pt === 'tool_response' && part.response.type === 'function_call';
}
@@ -443,14 +447,14 @@ function _duplicate_DataReference(ref: DMessageDataRef): DMessageDataRef {
export function editTextPartsInline(fragments: DMessageFragment[], editText: (text: string, idx: number) => string): void {
fragments.forEach((fragment, idx) => {
if (isContentFragment(fragment) && isTextPart(fragment.part))
if (isTextContentFragment(fragment))
fragment.part.text = editText(fragment.part.text, idx);
});
}
export function prependTextPartsInline(fragments: DMessageFragment[], textPrefix: string): void {
for (const fragment of fragments) {
if (!isContentFragment(fragment) || !isTextPart(fragment.part))
if (!isTextContentFragment(fragment))
continue;
fragment.part.text = textPrefix + ' ' + fragment.part.text;
return;
+27 -24
View File
@@ -237,31 +237,34 @@ export function messageFragmentsReduceText(fragments: DMessageFragment[], fragme
return fragments
.map(fragment => {
if (isContentFragment(fragment)) {
switch (fragment.part.pt) {
case 'text':
return fragment.part.text;
case 'error':
return fragment.part.error;
case 'image_ref':
return '';
case 'tool_invocation':
case 'tool_response':
// Ignore tools for the text reduction
return '';
}
} else if (isAttachmentFragment(fragment)) {
switch (fragment.part.pt) {
case 'doc':
return fragment.part.data.text;
case 'image_ref':
return '';
}
} else if (isVoidFragment(fragment)) {
// all void fragments are ignored by definition when doing a text reduction
return '';
switch (true) {
case isContentFragment(fragment):
switch (fragment.part.pt) {
case 'text':
return fragment.part.text;
case 'error':
return fragment.part.error;
case 'image_ref':
return '';
case 'tool_invocation':
case 'tool_response':
// Ignore tools for the text reduction
return '';
}
break;
case isAttachmentFragment(fragment):
switch (fragment.part.pt) {
case 'doc':
return fragment.part.data.text;
case 'image_ref':
return '';
}
break;
case isVoidFragment(fragment):
// all void fragments are ignored by definition when doing a text reduction
return '';
}
console.warn(`DEV: messageFragmentsReduceText: unexpected '${fragment.ft}' fragment with '${(fragment as any)?.part?.pt}' part`);
console.warn(`[DEV] messageFragmentsReduceText: unexpected '${fragment.ft}' fragment with '${(fragment as any)?.part?.pt}' part`);
return '';
})
.filter(text => !!text)
+3 -2
View File
@@ -8,7 +8,7 @@ import type { DModelsService } from '~/common/stores/llms/modelsservice.types';
import { createDConversation, DConversation, type DConversationId } from './chat.conversation';
import { createDMessageTextContent, DMessage, MESSAGE_FLAG_NOTIFY_COMPLETE, messageSetUserFlag } from './chat.message';
import { createErrorContentFragment, isAttachmentFragment, isContentFragment, isContentOrAttachmentFragment, isDocPart, isPlaceholderPart, isTextPart, isVoidFragment } from './chat.fragments';
import { createErrorContentFragment, isAttachmentFragment, isContentFragment, isContentOrAttachmentFragment, isDocPart, isPlaceholderPart, isTextContentFragment, isTextPart, isVoidFragment } from './chat.fragments';
// configuration
@@ -63,7 +63,8 @@ export namespace V4ToHeadConverters {
// [Emergency] validate part types, can mess up in development
if (EMERGENCY_CLEANUP_PARTS) {
// If a text part has 'object' in place of 'string' for pText: remove the part altogether
if (isContentFragment(fragment) && isTextPart(fragment.part)) {
if (isTextContentFragment(fragment)) {
// noinspection SuspiciousTypeOfGuard
if (typeof fragment.part.text !== 'string') {
// Remove this fragment
m.fragments.splice(i, 1);
@@ -1,7 +1,7 @@
import { getImageAsset } from '~/modules/dblobs/dblobs.images';
import { DMessage, DMessageRole, DMetaReferenceItem, MESSAGE_FLAG_AIX_SKIP, MESSAGE_FLAG_VND_ANT_CACHE_AUTO, MESSAGE_FLAG_VND_ANT_CACHE_USER, messageHasUserFlag } from '~/common/stores/chat/chat.message';
import { DMessageFragment, DMessageImageRefPart, isContentFragment, isContentOrAttachmentFragment, isTextPart, isToolResponseFunctionCallPart } from '~/common/stores/chat/chat.fragments';
import { DMessageFragment, DMessageImageRefPart, isContentOrAttachmentFragment, isTextContentFragment, isToolResponseFunctionCallPart } from '~/common/stores/chat/chat.fragments';
import { Is } from '~/common/util/pwaUtils';
import { LLMImageResizeMode, resizeBase64ImageIfNeeded } from '~/common/util/imageUtils';
@@ -85,7 +85,7 @@ export async function aixCGR_FromDMessagesOrThrow(
};
}
for (const systemFragment of m.fragments) {
if (isContentFragment(systemFragment) && isTextPart(systemFragment.part)) {
if (isTextContentFragment(systemFragment)) {
acc.systemMessage.parts.push(systemFragment.part);
} else {
console.warn('aixCGR_FromDMessages: unexpected system fragment', systemFragment);