ctx_management: summarize on command button (#2479)
This commit is contained in:
@@ -5,7 +5,7 @@ use goose::config::permission::PermissionLevel;
|
|||||||
use goose::config::ExtensionEntry;
|
use goose::config::ExtensionEntry;
|
||||||
use goose::message::{
|
use goose::message::{
|
||||||
ContextLengthExceeded, FrontendToolRequest, Message, MessageContent, RedactedThinkingContent,
|
ContextLengthExceeded, FrontendToolRequest, Message, MessageContent, RedactedThinkingContent,
|
||||||
ThinkingContent, ToolConfirmationRequest, ToolRequest, ToolResponse,
|
SummarizationRequested, ThinkingContent, ToolConfirmationRequest, ToolRequest, ToolResponse,
|
||||||
};
|
};
|
||||||
use goose::permission::permission_confirmation::PrincipalType;
|
use goose::permission::permission_confirmation::PrincipalType;
|
||||||
use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata};
|
use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata};
|
||||||
@@ -70,6 +70,7 @@ use utoipa::OpenApi;
|
|||||||
FrontendToolRequest,
|
FrontendToolRequest,
|
||||||
ResourceContents,
|
ResourceContents,
|
||||||
ContextLengthExceeded,
|
ContextLengthExceeded,
|
||||||
|
SummarizationRequested,
|
||||||
Role,
|
Role,
|
||||||
ProviderMetadata,
|
ProviderMetadata,
|
||||||
ExtensionEntry,
|
ExtensionEntry,
|
||||||
|
|||||||
@@ -91,6 +91,11 @@ pub struct ContextLengthExceeded {
|
|||||||
pub msg: String,
|
pub msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||||
|
pub struct SummarizationRequested {
|
||||||
|
pub msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||||
/// Content passed inside a message, which can be both simple content and tool content
|
/// Content passed inside a message, which can be both simple content and tool content
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
@@ -104,6 +109,7 @@ pub enum MessageContent {
|
|||||||
Thinking(ThinkingContent),
|
Thinking(ThinkingContent),
|
||||||
RedactedThinking(RedactedThinkingContent),
|
RedactedThinking(RedactedThinkingContent),
|
||||||
ContextLengthExceeded(ContextLengthExceeded),
|
ContextLengthExceeded(ContextLengthExceeded),
|
||||||
|
SummarizationRequested(SummarizationRequested),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageContent {
|
impl MessageContent {
|
||||||
@@ -172,6 +178,19 @@ impl MessageContent {
|
|||||||
MessageContent::ContextLengthExceeded(ContextLengthExceeded { msg: msg.into() })
|
MessageContent::ContextLengthExceeded(ContextLengthExceeded { msg: msg.into() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn summarization_requested<S: Into<String>>(msg: S) -> Self {
|
||||||
|
MessageContent::SummarizationRequested(SummarizationRequested { msg: msg.into() })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this new method to check for summarization requested content
|
||||||
|
pub fn as_summarization_requested(&self) -> Option<&SummarizationRequested> {
|
||||||
|
if let MessageContent::SummarizationRequested(ref summarization_requested) = self {
|
||||||
|
Some(summarization_requested)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_tool_request(&self) -> Option<&ToolRequest> {
|
pub fn as_tool_request(&self) -> Option<&ToolRequest> {
|
||||||
if let MessageContent::ToolRequest(ref tool_request) = self {
|
if let MessageContent::ToolRequest(ref tool_request) = self {
|
||||||
Some(tool_request)
|
Some(tool_request)
|
||||||
@@ -451,6 +470,11 @@ impl Message {
|
|||||||
.iter()
|
.iter()
|
||||||
.all(|c| matches!(c, MessageContent::Text(_)))
|
.all(|c| matches!(c, MessageContent::Text(_)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add summarization requested to the message
|
||||||
|
pub fn with_summarization_requested<S: Into<String>>(self, msg: S) -> Self {
|
||||||
|
self.with_content(MessageContent::summarization_requested(msg))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
|
|||||||
MessageContent::ContextLengthExceeded(_) => {
|
MessageContent::ContextLengthExceeded(_) => {
|
||||||
// Skip
|
// Skip
|
||||||
}
|
}
|
||||||
|
MessageContent::SummarizationRequested(_) => {
|
||||||
|
// Skip
|
||||||
|
}
|
||||||
MessageContent::Thinking(thinking) => {
|
MessageContent::Thinking(thinking) => {
|
||||||
content.push(json!({
|
content.push(json!({
|
||||||
"type": "thinking",
|
"type": "thinking",
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ pub fn to_bedrock_message_content(content: &MessageContent) -> Result<bedrock::C
|
|||||||
MessageContent::ContextLengthExceeded(_) => {
|
MessageContent::ContextLengthExceeded(_) => {
|
||||||
bail!("ContextLengthExceeded should not get passed to the provider")
|
bail!("ContextLengthExceeded should not get passed to the provider")
|
||||||
}
|
}
|
||||||
|
MessageContent::SummarizationRequested(_) => {
|
||||||
|
bail!("SummarizationRequested should not get passed to the provider")
|
||||||
|
}
|
||||||
MessageContent::ToolRequest(tool_req) => {
|
MessageContent::ToolRequest(tool_req) => {
|
||||||
let tool_use_id = tool_req.id.to_string();
|
let tool_use_id = tool_req.id.to_string();
|
||||||
let tool_use = if let Ok(call) = tool_req.tool_call.as_ref() {
|
let tool_use = if let Ok(call) = tool_req.tool_call.as_ref() {
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<
|
|||||||
MessageContent::ContextLengthExceeded(_) => {
|
MessageContent::ContextLengthExceeded(_) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
MessageContent::SummarizationRequested(_) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
MessageContent::ToolResponse(response) => {
|
MessageContent::ToolResponse(response) => {
|
||||||
match &response.tool_result {
|
match &response.tool_result {
|
||||||
Ok(contents) => {
|
Ok(contents) => {
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<
|
|||||||
MessageContent::ContextLengthExceeded(_) => {
|
MessageContent::ContextLengthExceeded(_) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
MessageContent::SummarizationRequested(_) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
MessageContent::ToolRequest(request) => match &request.tool_call {
|
MessageContent::ToolRequest(request) => match &request.tool_call {
|
||||||
Ok(tool_call) => {
|
Ok(tool_call) => {
|
||||||
let sanitized_name = sanitize_function_name(&tool_call.name);
|
let sanitized_name = sanitize_function_name(&tool_call.name);
|
||||||
|
|||||||
@@ -1244,6 +1244,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/SummarizationRequested"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"summarizationRequested"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Content passed inside a message, which can be both simple content and tool content",
|
"description": "Content passed inside a message, which can be both simple content and tool content",
|
||||||
@@ -1571,6 +1592,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SummarizationRequested": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"msg"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"msg": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"TextContent": {
|
"TextContent": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
@@ -503,9 +503,6 @@ export default function App() {
|
|||||||
// If GOOSE_ALLOWLIST_WARNING is true, use warning-only mode (STRICT_ALLOWLIST=false)
|
// If GOOSE_ALLOWLIST_WARNING is true, use warning-only mode (STRICT_ALLOWLIST=false)
|
||||||
// If GOOSE_ALLOWLIST_WARNING is not set or false, use strict blocking mode (STRICT_ALLOWLIST=true)
|
// If GOOSE_ALLOWLIST_WARNING is not set or false, use strict blocking mode (STRICT_ALLOWLIST=true)
|
||||||
const STRICT_ALLOWLIST = config.GOOSE_ALLOWLIST_WARNING === true ? false : true;
|
const STRICT_ALLOWLIST = config.GOOSE_ALLOWLIST_WARNING === true ? false : true;
|
||||||
console.log(
|
|
||||||
`Extension security mode: ${STRICT_ALLOWLIST ? 'Strict' : 'Warning-only'} (GOOSE_ALLOWLIST_WARNING=${config.GOOSE_ALLOWLIST_WARNING})`
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('Setting up extension handler');
|
console.log('Setting up extension handler');
|
||||||
|
|||||||
@@ -196,6 +196,8 @@ export type MessageContent = (TextContent & {
|
|||||||
type: 'redactedThinking';
|
type: 'redactedThinking';
|
||||||
}) | (ContextLengthExceeded & {
|
}) | (ContextLengthExceeded & {
|
||||||
type: 'contextLengthExceeded';
|
type: 'contextLengthExceeded';
|
||||||
|
}) | (SummarizationRequested & {
|
||||||
|
type: 'summarizationRequested';
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -360,6 +362,10 @@ export type SessionMetadata = {
|
|||||||
working_dir: string;
|
working_dir: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SummarizationRequested = {
|
||||||
|
msg: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type TextContent = {
|
export type TextContent = {
|
||||||
annotations?: Annotations | null;
|
annotations?: Annotations | null;
|
||||||
text: string;
|
text: string;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Attach, Send } from './icons';
|
|||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import BottomMenu from './bottom_menu/BottomMenu';
|
import BottomMenu from './bottom_menu/BottomMenu';
|
||||||
import { LocalMessageStorage } from '../utils/localMessageStorage';
|
import { LocalMessageStorage } from '../utils/localMessageStorage';
|
||||||
|
import { Message } from '../types/message';
|
||||||
|
|
||||||
interface ChatInputProps {
|
interface ChatInputProps {
|
||||||
handleSubmit: (e: React.FormEvent) => void;
|
handleSubmit: (e: React.FormEvent) => void;
|
||||||
@@ -16,6 +17,9 @@ interface ChatInputProps {
|
|||||||
droppedFiles?: string[];
|
droppedFiles?: string[];
|
||||||
setView: (view: View) => void;
|
setView: (view: View) => void;
|
||||||
numTokens?: number;
|
numTokens?: number;
|
||||||
|
hasMessages?: boolean;
|
||||||
|
messages?: Message[];
|
||||||
|
setMessages: (messages: Message[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ChatInput({
|
export default function ChatInput({
|
||||||
@@ -27,6 +31,8 @@ export default function ChatInput({
|
|||||||
setView,
|
setView,
|
||||||
numTokens,
|
numTokens,
|
||||||
droppedFiles = [],
|
droppedFiles = [],
|
||||||
|
messages = [],
|
||||||
|
setMessages,
|
||||||
}: ChatInputProps) {
|
}: ChatInputProps) {
|
||||||
const [_value, setValue] = useState(initialValue);
|
const [_value, setValue] = useState(initialValue);
|
||||||
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
|
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
|
||||||
@@ -327,7 +333,13 @@ export default function ChatInput({
|
|||||||
<Attach />
|
<Attach />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<BottomMenu setView={setView} numTokens={numTokens} />
|
<BottomMenu
|
||||||
|
setView={setView}
|
||||||
|
numTokens={numTokens}
|
||||||
|
messages={messages}
|
||||||
|
isLoading={isLoading}
|
||||||
|
setMessages={setMessages}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import { Recipe } from '../recipe';
|
|||||||
import {
|
import {
|
||||||
ChatContextManagerProvider,
|
ChatContextManagerProvider,
|
||||||
useChatContextManager,
|
useChatContextManager,
|
||||||
} from './context_management/ContextManager';
|
} from './context_management/ChatContextManager';
|
||||||
import { ContextLengthExceededHandler } from './context_management/ContextLengthExceededHandler';
|
import { ContextHandler } from './context_management/ContextHandler';
|
||||||
import { LocalMessageStorage } from '../utils/localMessageStorage';
|
import { LocalMessageStorage } from '../utils/localMessageStorage';
|
||||||
import {
|
import {
|
||||||
Message,
|
Message,
|
||||||
@@ -105,7 +105,8 @@ function ChatContent({
|
|||||||
resetMessagesWithSummary,
|
resetMessagesWithSummary,
|
||||||
closeSummaryModal,
|
closeSummaryModal,
|
||||||
updateSummary,
|
updateSummary,
|
||||||
hasContextLengthExceededContent,
|
hasContextHandlerContent,
|
||||||
|
getContextHandlerType,
|
||||||
} = useChatContextManager();
|
} = useChatContextManager();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -521,16 +522,29 @@ function ChatContent({
|
|||||||
data-testid="message-container"
|
data-testid="message-container"
|
||||||
>
|
>
|
||||||
{isUserMessage(message) ? (
|
{isUserMessage(message) ? (
|
||||||
<UserMessage message={message} />
|
|
||||||
) : (
|
|
||||||
<>
|
<>
|
||||||
{/* Only render GooseMessage if it's not a CLE message */}
|
{hasContextHandlerContent(message) ? (
|
||||||
{hasContextLengthExceededContent(message) ? (
|
<ContextHandler
|
||||||
<ContextLengthExceededHandler
|
|
||||||
messages={messages}
|
messages={messages}
|
||||||
messageId={message.id ?? message.created.toString()}
|
messageId={message.id ?? message.created.toString()}
|
||||||
chatId={chat.id}
|
chatId={chat.id}
|
||||||
workingDir={window.appConfig.get('GOOSE_WORKING_DIR') as string}
|
workingDir={window.appConfig.get('GOOSE_WORKING_DIR') as string}
|
||||||
|
contextType={getContextHandlerType(message)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<UserMessage message={message} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{/* Only render GooseMessage if it's not a message invoking some context management */}
|
||||||
|
{hasContextHandlerContent(message) ? (
|
||||||
|
<ContextHandler
|
||||||
|
messages={messages}
|
||||||
|
messageId={message.id ?? message.created.toString()}
|
||||||
|
chatId={chat.id}
|
||||||
|
workingDir={window.appConfig.get('GOOSE_WORKING_DIR') as string}
|
||||||
|
contextType={getContextHandlerType(message)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<GooseMessage
|
<GooseMessage
|
||||||
@@ -587,6 +601,8 @@ function ChatContent({
|
|||||||
hasMessages={hasMessages}
|
hasMessages={hasMessages}
|
||||||
numTokens={sessionTokenCount}
|
numTokens={sessionTokenCount}
|
||||||
droppedFiles={droppedFiles}
|
droppedFiles={droppedFiles}
|
||||||
|
messages={messages}
|
||||||
|
setMessages={setMessages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import { BottomMenuModeSelection } from './BottomMenuModeSelection';
|
|||||||
import ModelsBottomBar from '../settings_v2/models/bottom_bar/ModelsBottomBar';
|
import ModelsBottomBar from '../settings_v2/models/bottom_bar/ModelsBottomBar';
|
||||||
import { useConfig } from '../ConfigContext';
|
import { useConfig } from '../ConfigContext';
|
||||||
import { getCurrentModelAndProvider } from '../settings_v2/models';
|
import { getCurrentModelAndProvider } from '../settings_v2/models';
|
||||||
|
import { Message } from '../../types/message';
|
||||||
|
import { ManualSummarizeButton } from '../context_management/ManualSummaryButton';
|
||||||
|
|
||||||
const TOKEN_LIMIT_DEFAULT = 128000; // fallback for custom models that the backend doesn't know about
|
const TOKEN_LIMIT_DEFAULT = 128000; // fallback for custom models that the backend doesn't know about
|
||||||
const TOKEN_WARNING_THRESHOLD = 0.8; // warning shows at 80% of the token limit
|
const TOKEN_WARNING_THRESHOLD = 0.8; // warning shows at 80% of the token limit
|
||||||
@@ -20,9 +22,15 @@ const TOOLS_MAX_SUGGESTED = 60; // max number of tools before we show a warning
|
|||||||
export default function BottomMenu({
|
export default function BottomMenu({
|
||||||
setView,
|
setView,
|
||||||
numTokens = 0,
|
numTokens = 0,
|
||||||
|
messages = [],
|
||||||
|
isLoading = false,
|
||||||
|
setMessages,
|
||||||
}: {
|
}: {
|
||||||
setView: (view: View, viewOptions?: ViewOptions) => void;
|
setView: (view: View, viewOptions?: ViewOptions) => void;
|
||||||
numTokens?: number;
|
numTokens?: number;
|
||||||
|
messages?: Message[];
|
||||||
|
isLoading?: boolean;
|
||||||
|
setMessages: (messages: Message[]) => void;
|
||||||
}) {
|
}) {
|
||||||
const [isModelMenuOpen, setIsModelMenuOpen] = useState(false);
|
const [isModelMenuOpen, setIsModelMenuOpen] = useState(false);
|
||||||
const { currentModel } = useModel();
|
const { currentModel } = useModel();
|
||||||
@@ -267,6 +275,18 @@ export default function BottomMenu({
|
|||||||
|
|
||||||
{/* Goose Mode Selector Dropdown */}
|
{/* Goose Mode Selector Dropdown */}
|
||||||
<BottomMenuModeSelection setView={setView} />
|
<BottomMenuModeSelection setView={setView} />
|
||||||
|
|
||||||
|
{/* Summarize Context Button - ADD THIS */}
|
||||||
|
{messages.length > 0 && (
|
||||||
|
<>
|
||||||
|
<div className="w-[1px] h-4 bg-borderSubtle mx-2" />
|
||||||
|
<ManualSummarizeButton
|
||||||
|
messages={messages}
|
||||||
|
isLoading={isLoading}
|
||||||
|
setMessages={setMessages}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+52
-35
@@ -1,6 +1,10 @@
|
|||||||
import React, { createContext, useContext, useState } from 'react';
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
import { Message } from '../../types/message';
|
import { Message } from '../../types/message';
|
||||||
import { manageContextFromBackend, convertApiMessageToFrontendMessage } from './index';
|
import {
|
||||||
|
manageContextFromBackend,
|
||||||
|
convertApiMessageToFrontendMessage,
|
||||||
|
createSummarizationRequestMessage,
|
||||||
|
} from './index';
|
||||||
|
|
||||||
// Define the context management interface
|
// Define the context management interface
|
||||||
interface ChatContextManagerState {
|
interface ChatContextManagerState {
|
||||||
@@ -9,10 +13,10 @@ interface ChatContextManagerState {
|
|||||||
isSummaryModalOpen: boolean;
|
isSummaryModalOpen: boolean;
|
||||||
isLoadingSummary: boolean;
|
isLoadingSummary: boolean;
|
||||||
errorLoadingSummary: boolean;
|
errorLoadingSummary: boolean;
|
||||||
|
preparingManualSummary: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatContextManagerActions {
|
interface ChatContextManagerActions {
|
||||||
fetchSummary: (messages: Message[]) => Promise<void>;
|
|
||||||
updateSummary: (newSummaryContent: string) => void;
|
updateSummary: (newSummaryContent: string) => void;
|
||||||
resetMessagesWithSummary: (
|
resetMessagesWithSummary: (
|
||||||
messages: Message[],
|
messages: Message[],
|
||||||
@@ -23,12 +27,15 @@ interface ChatContextManagerActions {
|
|||||||
) => void;
|
) => void;
|
||||||
openSummaryModal: () => void;
|
openSummaryModal: () => void;
|
||||||
closeSummaryModal: () => void;
|
closeSummaryModal: () => void;
|
||||||
|
hasContextHandlerContent: (message: Message) => boolean;
|
||||||
hasContextLengthExceededContent: (message: Message) => boolean;
|
hasContextLengthExceededContent: (message: Message) => boolean;
|
||||||
handleContextLengthExceeded: (
|
hasSummarizationRequestedContent: (message: Message) => boolean;
|
||||||
|
getContextHandlerType: (message: Message) => 'contextLengthExceeded' | 'summarizationRequested';
|
||||||
|
handleContextLengthExceeded: (messages: Message[]) => Promise<void>;
|
||||||
|
handleManualSummarization: (
|
||||||
messages: Message[],
|
messages: Message[],
|
||||||
chatId: string,
|
setMessages: (messages: Message[]) => void
|
||||||
workingDir: string
|
) => void;
|
||||||
) => Promise<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the context
|
// Create the context
|
||||||
@@ -45,10 +52,12 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
const [isSummaryModalOpen, setIsSummaryModalOpen] = useState<boolean>(false);
|
const [isSummaryModalOpen, setIsSummaryModalOpen] = useState<boolean>(false);
|
||||||
const [isLoadingSummary, setIsLoadingSummary] = useState<boolean>(false);
|
const [isLoadingSummary, setIsLoadingSummary] = useState<boolean>(false);
|
||||||
const [errorLoadingSummary, setErrorLoadingSummary] = useState<boolean>(false);
|
const [errorLoadingSummary, setErrorLoadingSummary] = useState<boolean>(false);
|
||||||
|
const [preparingManualSummary, setPreparingManualSummary] = useState<boolean>(false);
|
||||||
|
|
||||||
const handleContextLengthExceeded = async (messages: Message[]): Promise<void> => {
|
const handleContextLengthExceeded = async (messages: Message[]): Promise<void> => {
|
||||||
setIsLoadingSummary(true);
|
setIsLoadingSummary(true);
|
||||||
setErrorLoadingSummary(false);
|
setErrorLoadingSummary(false);
|
||||||
|
setPreparingManualSummary(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2. Now get the summary from the backend
|
// 2. Now get the summary from the backend
|
||||||
@@ -75,38 +84,25 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
console.error('Error handling context length exceeded:', err);
|
console.error('Error handling context length exceeded:', err);
|
||||||
setErrorLoadingSummary(true);
|
setErrorLoadingSummary(true);
|
||||||
setIsLoadingSummary(false);
|
setIsLoadingSummary(false);
|
||||||
|
} finally {
|
||||||
|
setPreparingManualSummary(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchSummary = async (messages: Message[]) => {
|
const handleManualSummarization = (
|
||||||
setIsLoadingSummary(true);
|
messages: Message[],
|
||||||
setErrorLoadingSummary(false);
|
setMessages: (messages: Message[]) => void
|
||||||
|
): void => {
|
||||||
|
// add some messages to the message thread
|
||||||
|
// these messages will be filtered out in chat view
|
||||||
|
// but they will also be what allows us to render some text in the chatview itself, similar to CLE events
|
||||||
|
const summarizationRequest = createSummarizationRequestMessage(
|
||||||
|
messages,
|
||||||
|
'Summarize the session and begin a new one'
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
// add the message to the message thread
|
||||||
const response = await manageContextFromBackend({
|
setMessages([...messages, summarizationRequest]);
|
||||||
messages: messages,
|
|
||||||
manageAction: 'summarize',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Convert API messages to frontend messages
|
|
||||||
const convertedMessages = response.messages.map(
|
|
||||||
(apiMessage) => convertApiMessageToFrontendMessage(apiMessage, false, true) // do not show to user but send to llm
|
|
||||||
);
|
|
||||||
|
|
||||||
// Extract the summary text from the first message
|
|
||||||
const summaryMessage = convertedMessages[0].content[0];
|
|
||||||
if (summaryMessage.type === 'text') {
|
|
||||||
const summary = summaryMessage.text;
|
|
||||||
setSummaryContent(summary);
|
|
||||||
setSummarizedThread(convertedMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoadingSummary(false);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error fetching summary:', err);
|
|
||||||
setErrorLoadingSummary(true);
|
|
||||||
setIsLoadingSummary(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSummary = (newSummaryContent: string) => {
|
const updateSummary = (newSummaryContent: string) => {
|
||||||
@@ -212,10 +208,27 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
setSummaryContent('');
|
setSummaryContent('');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasContextHandlerContent = (message: Message): boolean => {
|
||||||
|
return hasContextLengthExceededContent(message) || hasSummarizationRequestedContent(message);
|
||||||
|
};
|
||||||
|
|
||||||
const hasContextLengthExceededContent = (message: Message): boolean => {
|
const hasContextLengthExceededContent = (message: Message): boolean => {
|
||||||
return message.content.some((content) => content.type === 'contextLengthExceeded');
|
return message.content.some((content) => content.type === 'contextLengthExceeded');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasSummarizationRequestedContent = (message: Message): boolean => {
|
||||||
|
return message.content.some((content) => content.type === 'summarizationRequested');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getContextHandlerType = (
|
||||||
|
message: Message
|
||||||
|
): 'contextLengthExceeded' | 'summarizationRequested' => {
|
||||||
|
if (hasContextLengthExceededContent(message)) {
|
||||||
|
return 'contextLengthExceeded';
|
||||||
|
}
|
||||||
|
return 'summarizationRequested';
|
||||||
|
};
|
||||||
|
|
||||||
const openSummaryModal = () => {
|
const openSummaryModal = () => {
|
||||||
setIsSummaryModalOpen(true);
|
setIsSummaryModalOpen(true);
|
||||||
};
|
};
|
||||||
@@ -231,15 +244,19 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
isSummaryModalOpen,
|
isSummaryModalOpen,
|
||||||
isLoadingSummary,
|
isLoadingSummary,
|
||||||
errorLoadingSummary,
|
errorLoadingSummary,
|
||||||
|
preparingManualSummary,
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
fetchSummary,
|
|
||||||
updateSummary,
|
updateSummary,
|
||||||
resetMessagesWithSummary,
|
resetMessagesWithSummary,
|
||||||
openSummaryModal,
|
openSummaryModal,
|
||||||
closeSummaryModal,
|
closeSummaryModal,
|
||||||
|
hasContextHandlerContent,
|
||||||
hasContextLengthExceededContent,
|
hasContextLengthExceededContent,
|
||||||
|
hasSummarizationRequestedContent,
|
||||||
|
getContextHandlerType,
|
||||||
handleContextLengthExceeded,
|
handleContextLengthExceeded,
|
||||||
|
handleManualSummarization,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
+66
-21
@@ -1,19 +1,21 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { Message } from '../../types/message';
|
import { Message } from '../../types/message';
|
||||||
import { useChatContextManager } from './ContextManager';
|
import { useChatContextManager } from './ChatContextManager';
|
||||||
|
|
||||||
interface ContextLengthExceededHandlerProps {
|
interface ContextHandlerProps {
|
||||||
messages: Message[];
|
messages: Message[];
|
||||||
messageId: string;
|
messageId: string;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
workingDir: string;
|
workingDir: string;
|
||||||
|
contextType: 'contextLengthExceeded' | 'summarizationRequested';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ContextLengthExceededHandler: React.FC<ContextLengthExceededHandlerProps> = ({
|
export const ContextHandler: React.FC<ContextHandlerProps> = ({
|
||||||
messages,
|
messages,
|
||||||
messageId,
|
messageId,
|
||||||
chatId,
|
chatId,
|
||||||
workingDir,
|
workingDir,
|
||||||
|
contextType,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
summaryContent,
|
summaryContent,
|
||||||
@@ -22,10 +24,11 @@ export const ContextLengthExceededHandler: React.FC<ContextLengthExceededHandler
|
|||||||
openSummaryModal,
|
openSummaryModal,
|
||||||
handleContextLengthExceeded,
|
handleContextLengthExceeded,
|
||||||
} = useChatContextManager();
|
} = useChatContextManager();
|
||||||
|
|
||||||
const [hasFetchStarted, setHasFetchStarted] = useState(false);
|
const [hasFetchStarted, setHasFetchStarted] = useState(false);
|
||||||
const [retryCount, setRetryCount] = useState(0);
|
const [retryCount, setRetryCount] = useState(0);
|
||||||
|
|
||||||
|
const isContextLengthExceeded = contextType === 'contextLengthExceeded';
|
||||||
|
|
||||||
// Find the relevant message to check if it's the latest
|
// Find the relevant message to check if it's the latest
|
||||||
const isCurrentMessageLatest =
|
const isCurrentMessageLatest =
|
||||||
messageId === messages[messages.length - 1]?.id ||
|
messageId === messages[messages.length - 1]?.id ||
|
||||||
@@ -43,7 +46,7 @@ export const ContextLengthExceededHandler: React.FC<ContextLengthExceededHandler
|
|||||||
fetchStartedRef.current = true;
|
fetchStartedRef.current = true;
|
||||||
|
|
||||||
// Call the async function without awaiting it in useEffect
|
// Call the async function without awaiting it in useEffect
|
||||||
handleContextLengthExceeded(messages, chatId, workingDir).catch((err) => {
|
handleContextLengthExceeded(messages).catch((err) => {
|
||||||
console.error('Error handling context length exceeded:', err);
|
console.error('Error handling context length exceeded:', err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -109,8 +112,16 @@ export const ContextLengthExceededHandler: React.FC<ContextLengthExceededHandler
|
|||||||
|
|
||||||
const renderFailedState = () => (
|
const renderFailedState = () => (
|
||||||
<>
|
<>
|
||||||
<span className="text-xs text-gray-400">{`Your conversation has exceeded the model's context capacity`}</span>
|
<span className="text-xs text-gray-400">
|
||||||
<span className="text-xs text-gray-400">{`This conversation has too much information to continue. Extension data often takes up significant space.`}</span>
|
{isContextLengthExceeded
|
||||||
|
? `Your conversation has exceeded the model's context capacity`
|
||||||
|
: `Summarization requested`}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-gray-400">
|
||||||
|
{isContextLengthExceeded
|
||||||
|
? `This conversation has too much information to continue. Extension data often takes up significant space.`
|
||||||
|
: `Summarization failed. Continue chatting or start a new session.`}
|
||||||
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={openNewSession}
|
onClick={openNewSession}
|
||||||
className="text-xs text-textStandard hover:text-textSubtle transition-colors mt-1 flex items-center"
|
className="text-xs text-textStandard hover:text-textSubtle transition-colors mt-1 flex items-center"
|
||||||
@@ -122,7 +133,11 @@ export const ContextLengthExceededHandler: React.FC<ContextLengthExceededHandler
|
|||||||
|
|
||||||
const renderRetryState = () => (
|
const renderRetryState = () => (
|
||||||
<>
|
<>
|
||||||
<span className="text-xs text-gray-400">{`Your conversation has exceeded the model's context capacity`}</span>
|
<span className="text-xs text-gray-400">
|
||||||
|
{isContextLengthExceeded
|
||||||
|
? `Your conversation has exceeded the model's context capacity`
|
||||||
|
: `Summarization requested`}
|
||||||
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={handleRetry}
|
onClick={handleRetry}
|
||||||
className="text-xs text-textStandard hover:text-textSubtle transition-colors mt-1 flex items-center"
|
className="text-xs text-textStandard hover:text-textSubtle transition-colors mt-1 flex items-center"
|
||||||
@@ -134,27 +149,57 @@ export const ContextLengthExceededHandler: React.FC<ContextLengthExceededHandler
|
|||||||
|
|
||||||
const renderSuccessState = () => (
|
const renderSuccessState = () => (
|
||||||
<>
|
<>
|
||||||
<span className="text-xs text-gray-400">{`Your conversation has exceeded the model's context capacity and a summary was prepared.`}</span>
|
<span className="text-xs text-gray-400">
|
||||||
<span className="text-xs text-gray-400">{`Messages above this line remain viewable but specific details are not included in active context.`}</span>
|
{isContextLengthExceeded
|
||||||
<button
|
? `Your conversation has exceeded the model's context capacity and a summary was prepared.`
|
||||||
onClick={openSummaryModal}
|
: `A summary of your conversation was prepared as requested.`}
|
||||||
className="text-xs text-textStandard hover:text-textSubtle transition-colors mt-1 flex items-center"
|
</span>
|
||||||
>
|
<span className="text-xs text-gray-400">
|
||||||
View or edit summary (you may continue your conversation based on the summary)
|
{isContextLengthExceeded
|
||||||
</button>
|
? `Messages above this line remain viewable but specific details are not included in active context.`
|
||||||
|
: `This summary includes key points from your conversation.`}
|
||||||
|
</span>
|
||||||
|
{shouldAllowSummaryInteraction && (
|
||||||
|
<button
|
||||||
|
onClick={openSummaryModal}
|
||||||
|
className="text-xs text-textStandard hover:text-textSubtle transition-colors mt-1 flex items-center"
|
||||||
|
>
|
||||||
|
View or edit summary{' '}
|
||||||
|
{isContextLengthExceeded
|
||||||
|
? '(you may continue your conversation based on the summary)'
|
||||||
|
: ''}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Render persistent summarized notification when we shouldn't show interaction options
|
||||||
|
const renderPersistentMarker = () => (
|
||||||
|
<span className="text-xs text-gray-400">
|
||||||
|
Session summarized — messages above this line are not included in the conversation
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
const renderContentState = () => {
|
const renderContentState = () => {
|
||||||
if (!shouldAllowSummaryInteraction) {
|
// If this is not the latest context event message but we have a valid summary,
|
||||||
return null;
|
// show the persistent marker
|
||||||
|
if (!shouldAllowSummaryInteraction && summaryContent) {
|
||||||
|
return renderPersistentMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorLoadingSummary) {
|
// For the latest message with the context event
|
||||||
return retryCount >= 2 ? renderFailedState() : renderRetryState();
|
if (shouldAllowSummaryInteraction) {
|
||||||
|
if (errorLoadingSummary) {
|
||||||
|
return retryCount >= 2 ? renderFailedState() : renderRetryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (summaryContent) {
|
||||||
|
return renderSuccessState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderSuccessState();
|
// Fallback to showing at least the persistent marker
|
||||||
|
return renderPersistentMarker();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { ScrollText } from 'lucide-react';
|
||||||
|
import Modal from '../Modal';
|
||||||
|
import { Button } from '../ui/button';
|
||||||
|
import { useChatContextManager } from './ChatContextManager';
|
||||||
|
import { Message } from '../../types/message';
|
||||||
|
|
||||||
|
interface ManualSummarizeButtonProps {
|
||||||
|
messages: Message[];
|
||||||
|
isLoading?: boolean; // need this prop to know if Goose is responding
|
||||||
|
setMessages: (messages: Message[]) => void; // context management is triggered via special message content types
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ManualSummarizeButton: React.FC<ManualSummarizeButtonProps> = ({
|
||||||
|
messages,
|
||||||
|
isLoading = false,
|
||||||
|
setMessages,
|
||||||
|
}) => {
|
||||||
|
const { handleManualSummarization, isLoadingSummary } = useChatContextManager();
|
||||||
|
|
||||||
|
const [isConfirmationOpen, setIsConfirmationOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
setIsConfirmationOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSummarize = async () => {
|
||||||
|
setIsConfirmationOpen(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
handleManualSummarization(messages, setMessages);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in handleSummarize:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Footer content for the confirmation modal
|
||||||
|
const footerContent = (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
onClick={handleSummarize}
|
||||||
|
className="w-full h-[60px] rounded-none border-b border-borderSubtle bg-transparent hover:bg-bgSubtle text-textProminent font-medium text-large"
|
||||||
|
>
|
||||||
|
Summarize
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => setIsConfirmationOpen(false)}
|
||||||
|
variant="ghost"
|
||||||
|
className="w-full h-[60px] rounded-none hover:bg-bgSubtle text-textSubtle hover:text-textStandard text-large font-regular"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="relative flex items-center">
|
||||||
|
<button
|
||||||
|
className={`flex items-center justify-center text-textSubtle hover:text-textStandard h-6 [&_svg]:size-4 ${
|
||||||
|
isLoadingSummary || isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
||||||
|
}`}
|
||||||
|
onClick={handleClick}
|
||||||
|
disabled={isLoadingSummary || isLoading}
|
||||||
|
title="Summarize conversation context"
|
||||||
|
>
|
||||||
|
<ScrollText size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Confirmation Modal */}
|
||||||
|
{isConfirmationOpen && (
|
||||||
|
<Modal footer={footerContent} onClose={() => setIsConfirmationOpen(false)}>
|
||||||
|
<div className="flex flex-col mb-6">
|
||||||
|
<div>
|
||||||
|
<ScrollText className="text-iconStandard" size={24} />
|
||||||
|
</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<h2 className="text-2xl font-regular text-textStandard">Summarize Conversation</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<p className="text-textStandard mb-4">
|
||||||
|
This will summarize your conversation history to save context space.
|
||||||
|
</p>
|
||||||
|
<p className="text-textStandard">
|
||||||
|
Previous messages will remain visible but only the summary will be included in the
|
||||||
|
active context for Goose. This is useful for long conversations that are approaching
|
||||||
|
the context limit.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
MessageContent as FrontendMessageContent,
|
MessageContent as FrontendMessageContent,
|
||||||
ToolCallResult,
|
ToolCallResult,
|
||||||
ToolCall,
|
ToolCall,
|
||||||
|
Role,
|
||||||
} from '../../types/message';
|
} from '../../types/message';
|
||||||
import {
|
import {
|
||||||
ContextManageRequest,
|
ContextManageRequest,
|
||||||
@@ -115,9 +116,40 @@ function mapApiContentToFrontendMessageContent(
|
|||||||
type: 'contextLengthExceeded',
|
type: 'contextLengthExceeded',
|
||||||
msg: apiContent.msg,
|
msg: apiContent.msg,
|
||||||
};
|
};
|
||||||
|
} else if (apiContent.type === 'summarizationRequested') {
|
||||||
|
return {
|
||||||
|
type: 'summarizationRequested',
|
||||||
|
msg: apiContent.msg,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// For types that exist in API but not in frontend, either skip or convert
|
// For types that exist in API but not in frontend, either skip or convert
|
||||||
console.warn(`Skipping unsupported content type: ${apiContent.type}`);
|
console.warn(`Skipping unsupported content type: ${apiContent.type}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createSummarizationRequestMessage(
|
||||||
|
messages: FrontendMessage[],
|
||||||
|
requestMessage: string
|
||||||
|
): FrontendMessage {
|
||||||
|
// Get the last message
|
||||||
|
const lastMessage = messages[messages.length - 1];
|
||||||
|
|
||||||
|
// Determine the next role (opposite of the last message)
|
||||||
|
const nextRole: Role = lastMessage.role === 'user' ? 'assistant' : 'user';
|
||||||
|
|
||||||
|
// Create the new message with SummarizationRequestedContent
|
||||||
|
return {
|
||||||
|
id: generateId(),
|
||||||
|
role: nextRole,
|
||||||
|
created: Math.floor(Date.now() / 1000),
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'summarizationRequested',
|
||||||
|
msg: requestMessage,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sendToLLM: false,
|
||||||
|
display: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ export function useMessageStream({
|
|||||||
...extraMetadataRef.current.headers,
|
...extraMetadataRef.current.headers,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
messages: requestMessages,
|
messages: filteredMessages,
|
||||||
...extraMetadataRef.current.body,
|
...extraMetadataRef.current.body,
|
||||||
}),
|
}),
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
@@ -439,7 +439,6 @@ export function useMessageStream({
|
|||||||
event?.preventDefault?.();
|
event?.preventDefault?.();
|
||||||
if (!input.trim()) return;
|
if (!input.trim()) return;
|
||||||
|
|
||||||
console.log('handleSubmit called with input:', input);
|
|
||||||
await append(input);
|
await append(input);
|
||||||
setInput('');
|
setInput('');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -78,21 +78,27 @@ export interface ContextLengthExceededContent {
|
|||||||
msg: string;
|
msg: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SummarizationRequestedContent {
|
||||||
|
type: 'summarizationRequested';
|
||||||
|
msg: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageContent =
|
export type MessageContent =
|
||||||
| TextContent
|
| TextContent
|
||||||
| ImageContent
|
| ImageContent
|
||||||
| ToolRequestMessageContent
|
| ToolRequestMessageContent
|
||||||
| ToolResponseMessageContent
|
| ToolResponseMessageContent
|
||||||
| ToolConfirmationRequestMessageContent
|
| ToolConfirmationRequestMessageContent
|
||||||
| ContextLengthExceededContent;
|
| ContextLengthExceededContent
|
||||||
|
| SummarizationRequestedContent;
|
||||||
|
|
||||||
export interface Message {
|
export interface Message {
|
||||||
id?: string;
|
id?: string;
|
||||||
role: Role;
|
role: Role;
|
||||||
created: number;
|
created: number;
|
||||||
content: MessageContent[];
|
content: MessageContent[];
|
||||||
display: boolean;
|
display?: boolean;
|
||||||
sendToLLM: boolean;
|
sendToLLM?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions to create messages
|
// Helper functions to create messages
|
||||||
|
|||||||
Reference in New Issue
Block a user