mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
Fragments: ITCF.
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user