mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-19 07:04:21 +01:00
context_management: handle summarization in UI (#2377)
This commit is contained in:
@@ -3,8 +3,16 @@ use goose::agents::extension::ToolInfo;
|
|||||||
use goose::agents::ExtensionConfig;
|
use goose::agents::ExtensionConfig;
|
||||||
use goose::config::permission::PermissionLevel;
|
use goose::config::permission::PermissionLevel;
|
||||||
use goose::config::ExtensionEntry;
|
use goose::config::ExtensionEntry;
|
||||||
|
use goose::message::{
|
||||||
|
ContextLengthExceeded, FrontendToolRequest, Message, MessageContent, RedactedThinkingContent,
|
||||||
|
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};
|
||||||
|
use mcp_core::content::{Annotations, Content, EmbeddedResource, ImageContent, TextContent};
|
||||||
|
use mcp_core::handler::ToolResultSchema;
|
||||||
|
use mcp_core::resource::ResourceContents;
|
||||||
|
use mcp_core::role::Role;
|
||||||
use mcp_core::tool::{Tool, ToolAnnotations};
|
use mcp_core::tool::{Tool, ToolAnnotations};
|
||||||
use utoipa::OpenApi;
|
use utoipa::OpenApi;
|
||||||
|
|
||||||
@@ -25,6 +33,7 @@ use utoipa::OpenApi;
|
|||||||
super::routes::config_management::upsert_permissions,
|
super::routes::config_management::upsert_permissions,
|
||||||
super::routes::agent::get_tools,
|
super::routes::agent::get_tools,
|
||||||
super::routes::reply::confirm_permission,
|
super::routes::reply::confirm_permission,
|
||||||
|
super::routes::context::manage_context, // Added this path
|
||||||
),
|
),
|
||||||
components(schemas(
|
components(schemas(
|
||||||
super::routes::config_management::UpsertConfigQuery,
|
super::routes::config_management::UpsertConfigQuery,
|
||||||
@@ -37,6 +46,25 @@ use utoipa::OpenApi;
|
|||||||
super::routes::config_management::ToolPermission,
|
super::routes::config_management::ToolPermission,
|
||||||
super::routes::config_management::UpsertPermissionsQuery,
|
super::routes::config_management::UpsertPermissionsQuery,
|
||||||
super::routes::reply::PermissionConfirmationRequest,
|
super::routes::reply::PermissionConfirmationRequest,
|
||||||
|
super::routes::context::ContextManageRequest,
|
||||||
|
super::routes::context::ContextManageResponse,
|
||||||
|
Message,
|
||||||
|
MessageContent,
|
||||||
|
Content,
|
||||||
|
EmbeddedResource,
|
||||||
|
ImageContent,
|
||||||
|
Annotations,
|
||||||
|
TextContent,
|
||||||
|
ToolResponse,
|
||||||
|
ToolRequest,
|
||||||
|
ToolResultSchema,
|
||||||
|
ToolConfirmationRequest,
|
||||||
|
ThinkingContent,
|
||||||
|
RedactedThinkingContent,
|
||||||
|
FrontendToolRequest,
|
||||||
|
ResourceContents,
|
||||||
|
ContextLengthExceeded,
|
||||||
|
Role,
|
||||||
ProviderMetadata,
|
ProviderMetadata,
|
||||||
ExtensionEntry,
|
ExtensionEntry,
|
||||||
ExtensionConfig,
|
ExtensionConfig,
|
||||||
|
|||||||
@@ -9,23 +9,43 @@ use axum::{
|
|||||||
use goose::message::Message;
|
use goose::message::Message;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
// Direct message serialization for context mgmt request
|
/// Request payload for context management operations
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, ToSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ContextManageRequest {
|
pub struct ContextManageRequest {
|
||||||
messages: Vec<Message>,
|
/// Collection of messages to be managed
|
||||||
manage_action: String,
|
pub messages: Vec<Message>,
|
||||||
|
/// Operation to perform: "truncation" or "summarize"
|
||||||
|
pub manage_action: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direct message serialization for context mgmt request
|
/// Response from context management operations
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, ToSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ContextManageResponse {
|
pub struct ContextManageResponse {
|
||||||
messages: Vec<Message>,
|
/// Processed messages after the operation
|
||||||
token_counts: Vec<usize>,
|
pub messages: Vec<Message>,
|
||||||
|
/// Token counts for each processed message
|
||||||
|
pub token_counts: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/context/manage",
|
||||||
|
request_body = ContextManageRequest,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Context managed successfully", body = ContextManageResponse),
|
||||||
|
(status = 401, description = "Unauthorized - Invalid or missing API key"),
|
||||||
|
(status = 412, description = "Precondition failed - Agent not available"),
|
||||||
|
(status = 500, description = "Internal server error")
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("api_key" = [])
|
||||||
|
),
|
||||||
|
tag = "Context Management"
|
||||||
|
)]
|
||||||
async fn manage_context(
|
async fn manage_context(
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
@@ -40,7 +60,8 @@ async fn manage_context(
|
|||||||
|
|
||||||
let mut processed_messages: Vec<Message> = vec![];
|
let mut processed_messages: Vec<Message> = vec![];
|
||||||
let mut token_counts: Vec<usize> = vec![];
|
let mut token_counts: Vec<usize> = vec![];
|
||||||
if request.manage_action == "trunction" {
|
|
||||||
|
if request.manage_action == "truncation" {
|
||||||
(processed_messages, token_counts) = agent
|
(processed_messages, token_counts) = agent
|
||||||
.truncate_context(&request.messages)
|
.truncate_context(&request.messages)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -16,14 +16,17 @@ use mcp_core::role::Role;
|
|||||||
use mcp_core::tool::ToolCall;
|
use mcp_core::tool::ToolCall;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
mod tool_result_serde;
|
mod tool_result_serde;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[derive(ToSchema)]
|
||||||
pub struct ToolRequest {
|
pub struct ToolRequest {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
#[serde(with = "tool_result_serde")]
|
#[serde(with = "tool_result_serde")]
|
||||||
|
#[schema(value_type = Object)]
|
||||||
pub tool_call: ToolResult<ToolCall>,
|
pub tool_call: ToolResult<ToolCall>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,14 +48,17 @@ impl ToolRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[derive(ToSchema)]
|
||||||
pub struct ToolResponse {
|
pub struct ToolResponse {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
#[serde(with = "tool_result_serde")]
|
#[serde(with = "tool_result_serde")]
|
||||||
|
#[schema(value_type = Object)]
|
||||||
pub tool_result: ToolResult<Vec<Content>>,
|
pub tool_result: ToolResult<Vec<Content>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[derive(ToSchema)]
|
||||||
pub struct ToolConfirmationRequest {
|
pub struct ToolConfirmationRequest {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub tool_name: String,
|
pub tool_name: String,
|
||||||
@@ -60,31 +66,33 @@ pub struct ToolConfirmationRequest {
|
|||||||
pub prompt: Option<String>,
|
pub prompt: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct ThinkingContent {
|
pub struct ThinkingContent {
|
||||||
pub thinking: String,
|
pub thinking: String,
|
||||||
pub signature: String,
|
pub signature: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct RedactedThinkingContent {
|
pub struct RedactedThinkingContent {
|
||||||
pub data: String,
|
pub data: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[derive(ToSchema)]
|
||||||
pub struct FrontendToolRequest {
|
pub struct FrontendToolRequest {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
#[serde(with = "tool_result_serde")]
|
#[serde(with = "tool_result_serde")]
|
||||||
|
#[schema(value_type = Object)]
|
||||||
pub tool_call: ToolResult<ToolCall>,
|
pub tool_call: ToolResult<ToolCall>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct ContextLengthExceeded {
|
pub struct ContextLengthExceeded {
|
||||||
pub msg: String,
|
pub msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[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")]
|
||||||
pub enum MessageContent {
|
pub enum MessageContent {
|
||||||
@@ -273,7 +281,7 @@ impl From<PromptMessage> for Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(ToSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
/// A message to or from an LLM
|
/// A message to or from an LLM
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ use super::role::Role;
|
|||||||
use crate::resource::ResourceContents;
|
use crate::resource::ResourceContents;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(ToSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Annotations {
|
pub struct Annotations {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -14,6 +15,8 @@ pub struct Annotations {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub priority: Option<f32>,
|
pub priority: Option<f32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[schema(value_type = String, format = "date-time", example = "2023-01-01T00:00:00Z")]
|
||||||
|
// for openapi
|
||||||
pub timestamp: Option<DateTime<Utc>>,
|
pub timestamp: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +36,7 @@ impl Annotations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(ToSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct TextContent {
|
pub struct TextContent {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
@@ -41,7 +44,7 @@ pub struct TextContent {
|
|||||||
pub annotations: Option<Annotations>,
|
pub annotations: Option<Annotations>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(ToSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ImageContent {
|
pub struct ImageContent {
|
||||||
pub data: String,
|
pub data: String,
|
||||||
@@ -50,7 +53,7 @@ pub struct ImageContent {
|
|||||||
pub annotations: Option<Annotations>,
|
pub annotations: Option<Annotations>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(ToSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct EmbeddedResource {
|
pub struct EmbeddedResource {
|
||||||
pub resource: ResourceContents,
|
pub resource: ResourceContents,
|
||||||
@@ -67,7 +70,7 @@ impl EmbeddedResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(ToSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
pub enum Content {
|
pub enum Content {
|
||||||
Text(TextContent),
|
Text(TextContent),
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
#[allow(unused_imports)] // this is used in schema below
|
||||||
|
use serde_json::json;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Error, Debug, Clone, Deserialize, Serialize, PartialEq)]
|
#[derive(Error, Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||||
@@ -19,6 +22,18 @@ pub enum ToolError {
|
|||||||
|
|
||||||
pub type ToolResult<T> = std::result::Result<T, ToolError>;
|
pub type ToolResult<T> = std::result::Result<T, ToolError>;
|
||||||
|
|
||||||
|
// Define schema manually without generics issues
|
||||||
|
#[derive(ToSchema)]
|
||||||
|
#[schema(example = json!({"success": true, "data": {}}))]
|
||||||
|
pub struct ToolResultSchema {
|
||||||
|
#[schema(example = "Operation completed successfully")]
|
||||||
|
pub message: Option<String>,
|
||||||
|
#[schema(example = true)]
|
||||||
|
pub success: bool,
|
||||||
|
#[schema(value_type = Object)]
|
||||||
|
pub data: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ResourceError {
|
pub enum ResourceError {
|
||||||
#[error("Execution failed: {0}")]
|
#[error("Execution failed: {0}")]
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
use crate::content::Annotations;
|
||||||
/// Resources that servers provide to clients
|
/// Resources that servers provide to clients
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use utoipa::ToSchema;
|
||||||
use crate::content::Annotations;
|
|
||||||
|
|
||||||
const EPSILON: f32 = 1e-6; // Tolerance for floating point comparison
|
const EPSILON: f32 = 1e-6; // Tolerance for floating point comparison
|
||||||
|
|
||||||
@@ -28,6 +28,7 @@ pub struct Resource {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
|
#[derive(ToSchema)]
|
||||||
pub enum ResourceContents {
|
pub enum ResourceContents {
|
||||||
TextResourceContents {
|
TextResourceContents {
|
||||||
uri: String,
|
uri: String,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/// Roles to describe the origin/ownership of content
|
/// Roles to describe the origin/ownership of content
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Role {
|
pub enum Role {
|
||||||
User,
|
User,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"license": {
|
"license": {
|
||||||
"name": "Apache-2.0"
|
"name": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"version": "1.0.20"
|
"version": "1.0.21"
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"/agent/tools": {
|
"/agent/tools": {
|
||||||
@@ -408,10 +408,76 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/context/manage": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Context Management"
|
||||||
|
],
|
||||||
|
"operationId": "manage_context",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ContextManageRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Context managed successfully",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ContextManageResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized - Invalid or missing API key"
|
||||||
|
},
|
||||||
|
"412": {
|
||||||
|
"description": "Precondition failed - Agent not available"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal server error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"schemas": {
|
"schemas": {
|
||||||
|
"Annotations": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"audience": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Role"
|
||||||
|
},
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "float",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"example": "2023-01-01T00:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ConfigKey": {
|
"ConfigKey": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@@ -462,6 +528,152 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Content": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/TextContent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"text"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ImageContent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/EmbeddedResource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"resource"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"discriminator": {
|
||||||
|
"propertyName": "type"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ContextLengthExceeded": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"msg"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"msg": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ContextManageRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Request payload for context management operations",
|
||||||
|
"required": [
|
||||||
|
"messages",
|
||||||
|
"manageAction"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"manageAction": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Operation to perform: \"truncation\" or \"summarize\""
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Message"
|
||||||
|
},
|
||||||
|
"description": "Collection of messages to be managed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ContextManageResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Response from context management operations",
|
||||||
|
"required": [
|
||||||
|
"messages",
|
||||||
|
"tokenCounts"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"messages": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Message"
|
||||||
|
},
|
||||||
|
"description": "Processed messages after the operation"
|
||||||
|
},
|
||||||
|
"tokenCounts": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"description": "Token counts for each processed message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"EmbeddedResource": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"resource"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"annotations": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Annotations"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"$ref": "#/components/schemas/ResourceContents"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Envs": {
|
"Envs": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
@@ -704,6 +916,265 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"FrontendToolRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"toolCall"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"toolCall": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ImageContent": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"data",
|
||||||
|
"mimeType"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"annotations": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Annotations"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mimeType": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Message": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A message to or from an LLM",
|
||||||
|
"required": [
|
||||||
|
"role",
|
||||||
|
"created",
|
||||||
|
"content"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/MessageContent"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"$ref": "#/components/schemas/Role"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MessageContent": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/TextContent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"text"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ImageContent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolRequest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"toolRequest"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"toolResponse"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolConfirmationRequest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"toolConfirmationRequest"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/FrontendToolRequest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"frontendToolRequest"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ThinkingContent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"thinking"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/RedactedThinkingContent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"redactedThinking"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ContextLengthExceeded"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"contextLengthExceeded"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Content passed inside a message, which can be both simple content and tool content",
|
||||||
|
"discriminator": {
|
||||||
|
"propertyName": "type"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ModelInfo": {
|
"ModelInfo": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Information about a model's capabilities",
|
"description": "Information about a model's capabilities",
|
||||||
@@ -841,6 +1312,100 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"RedactedThinkingContent": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"data"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ResourceContents": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"uri",
|
||||||
|
"text"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"mime_type": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"uri",
|
||||||
|
"blob"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"blob": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mime_type": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Role": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"user",
|
||||||
|
"assistant"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TextContent": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"text"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"annotations": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Annotations"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ThinkingContent": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"thinking",
|
||||||
|
"signature"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"signature": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"thinking": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Tool": {
|
"Tool": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "A tool that can be used by a model.",
|
"description": "A tool that can be used by a model.",
|
||||||
@@ -898,6 +1463,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ToolConfirmationRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"toolName",
|
||||||
|
"arguments"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"arguments": {},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"prompt": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"toolName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ToolInfo": {
|
"ToolInfo": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Information about the tool used for building prompts",
|
"description": "Information about the tool used for building prompts",
|
||||||
@@ -945,6 +1531,61 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ToolRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"toolCall"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"toolCall": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ToolResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"toolResult"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"toolResult": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ToolResultSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"success",
|
||||||
|
"data"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Operation completed successfully",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"type": "boolean",
|
||||||
|
"example": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"example": {
|
||||||
|
"data": {},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"UpsertConfigQuery": {
|
"UpsertConfigQuery": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
|
import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
|
||||||
import type { GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ConfirmPermissionData } from './types.gen';
|
import type { GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse } from './types.gen';
|
||||||
import { client as _heyApiClient } from './client.gen';
|
import { client as _heyApiClient } from './client.gen';
|
||||||
|
|
||||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
|
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
|
||||||
@@ -132,3 +132,14 @@ export const confirmPermission = <ThrowOnError extends boolean = false>(options:
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const manageContext = <ThrowOnError extends boolean = false>(options: Options<ManageContextData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<ManageContextResponse, unknown, ThrowOnError>({
|
||||||
|
url: '/context/manage',
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...options?.headers
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
export type Annotations = {
|
||||||
|
audience?: Array<Role> | null;
|
||||||
|
priority?: number | null;
|
||||||
|
timestamp?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ConfigKey = {
|
export type ConfigKey = {
|
||||||
default?: string | null;
|
default?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -16,6 +22,51 @@ export type ConfigResponse = {
|
|||||||
config: {};
|
config: {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Content = (TextContent & {
|
||||||
|
type: 'text';
|
||||||
|
}) | (ImageContent & {
|
||||||
|
type: 'image';
|
||||||
|
}) | (EmbeddedResource & {
|
||||||
|
type: 'resource';
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ContextLengthExceeded = {
|
||||||
|
msg: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request payload for context management operations
|
||||||
|
*/
|
||||||
|
export type ContextManageRequest = {
|
||||||
|
/**
|
||||||
|
* Operation to perform: "truncation" or "summarize"
|
||||||
|
*/
|
||||||
|
manageAction: string;
|
||||||
|
/**
|
||||||
|
* Collection of messages to be managed
|
||||||
|
*/
|
||||||
|
messages: Array<Message>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response from context management operations
|
||||||
|
*/
|
||||||
|
export type ContextManageResponse = {
|
||||||
|
/**
|
||||||
|
* Processed messages after the operation
|
||||||
|
*/
|
||||||
|
messages: Array<Message>;
|
||||||
|
/**
|
||||||
|
* Token counts for each processed message
|
||||||
|
*/
|
||||||
|
tokenCounts: Array<number>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EmbeddedResource = {
|
||||||
|
annotations?: Annotations | null;
|
||||||
|
resource: ResourceContents;
|
||||||
|
};
|
||||||
|
|
||||||
export type Envs = {
|
export type Envs = {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
};
|
};
|
||||||
@@ -102,6 +153,51 @@ export type ExtensionResponse = {
|
|||||||
extensions: Array<ExtensionEntry>;
|
extensions: Array<ExtensionEntry>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FrontendToolRequest = {
|
||||||
|
id: string;
|
||||||
|
toolCall: {
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ImageContent = {
|
||||||
|
annotations?: Annotations | null;
|
||||||
|
data: string;
|
||||||
|
mimeType: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A message to or from an LLM
|
||||||
|
*/
|
||||||
|
export type Message = {
|
||||||
|
content: Array<MessageContent>;
|
||||||
|
created: number;
|
||||||
|
role: Role;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content passed inside a message, which can be both simple content and tool content
|
||||||
|
*/
|
||||||
|
export type MessageContent = (TextContent & {
|
||||||
|
type: 'text';
|
||||||
|
}) | (ImageContent & {
|
||||||
|
type: 'image';
|
||||||
|
}) | (ToolRequest & {
|
||||||
|
type: 'toolRequest';
|
||||||
|
}) | (ToolResponse & {
|
||||||
|
type: 'toolResponse';
|
||||||
|
}) | (ToolConfirmationRequest & {
|
||||||
|
type: 'toolConfirmationRequest';
|
||||||
|
}) | (FrontendToolRequest & {
|
||||||
|
type: 'frontendToolRequest';
|
||||||
|
}) | (ThinkingContent & {
|
||||||
|
type: 'thinking';
|
||||||
|
}) | (RedactedThinkingContent & {
|
||||||
|
type: 'redactedThinking';
|
||||||
|
}) | (ContextLengthExceeded & {
|
||||||
|
type: 'contextLengthExceeded';
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about a model's capabilities
|
* Information about a model's capabilities
|
||||||
*/
|
*/
|
||||||
@@ -180,6 +276,32 @@ export type ProvidersResponse = {
|
|||||||
providers: Array<ProviderDetails>;
|
providers: Array<ProviderDetails>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type RedactedThinkingContent = {
|
||||||
|
data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ResourceContents = {
|
||||||
|
mime_type?: string | null;
|
||||||
|
text: string;
|
||||||
|
uri: string;
|
||||||
|
} | {
|
||||||
|
blob: string;
|
||||||
|
mime_type?: string | null;
|
||||||
|
uri: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Role = 'user' | 'assistant';
|
||||||
|
|
||||||
|
export type TextContent = {
|
||||||
|
annotations?: Annotations | null;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ThinkingContent = {
|
||||||
|
signature: string;
|
||||||
|
thinking: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tool that can be used by a model.
|
* A tool that can be used by a model.
|
||||||
*/
|
*/
|
||||||
@@ -249,6 +371,13 @@ export type ToolAnnotations = {
|
|||||||
title?: string | null;
|
title?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ToolConfirmationRequest = {
|
||||||
|
arguments: unknown;
|
||||||
|
id: string;
|
||||||
|
prompt?: string | null;
|
||||||
|
toolName: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the tool used for building prompts
|
* Information about the tool used for building prompts
|
||||||
*/
|
*/
|
||||||
@@ -267,6 +396,28 @@ export type ToolPermission = {
|
|||||||
tool_name: string;
|
tool_name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ToolRequest = {
|
||||||
|
id: string;
|
||||||
|
toolCall: {
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ToolResponse = {
|
||||||
|
id: string;
|
||||||
|
toolResult: {
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ToolResultSchema = {
|
||||||
|
data: {
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
message?: string | null;
|
||||||
|
success: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type UpsertConfigQuery = {
|
export type UpsertConfigQuery = {
|
||||||
is_secret: boolean;
|
is_secret: boolean;
|
||||||
key: string;
|
key: string;
|
||||||
@@ -593,6 +744,37 @@ export type ConfirmPermissionResponses = {
|
|||||||
200: unknown;
|
200: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ManageContextData = {
|
||||||
|
body: ContextManageRequest;
|
||||||
|
path?: never;
|
||||||
|
query?: never;
|
||||||
|
url: '/context/manage';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ManageContextErrors = {
|
||||||
|
/**
|
||||||
|
* Unauthorized - Invalid or missing API key
|
||||||
|
*/
|
||||||
|
401: unknown;
|
||||||
|
/**
|
||||||
|
* Precondition failed - Agent not available
|
||||||
|
*/
|
||||||
|
412: unknown;
|
||||||
|
/**
|
||||||
|
* Internal server error
|
||||||
|
*/
|
||||||
|
500: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ManageContextResponses = {
|
||||||
|
/**
|
||||||
|
* Context managed successfully
|
||||||
|
*/
|
||||||
|
200: ContextManageResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ManageContextResponse = ManageContextResponses[keyof ManageContextResponses];
|
||||||
|
|
||||||
export type ClientOptions = {
|
export type ClientOptions = {
|
||||||
baseUrl: `${string}://${string}` | (string & {});
|
baseUrl: `${string}://${string}` | (string & {});
|
||||||
};
|
};
|
||||||
@@ -16,11 +16,12 @@ import { createRecipe } from '../recipe';
|
|||||||
import { AgentHeader } from './AgentHeader';
|
import { AgentHeader } from './AgentHeader';
|
||||||
import LayingEggLoader from './LayingEggLoader';
|
import LayingEggLoader from './LayingEggLoader';
|
||||||
import { fetchSessionDetails } from '../sessions';
|
import { fetchSessionDetails } from '../sessions';
|
||||||
// import { configureRecipeExtensions } from '../utils/recipeExtensions';
|
|
||||||
import 'react-toastify/dist/ReactToastify.css';
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
import { useMessageStream } from '../hooks/useMessageStream';
|
import { useMessageStream } from '../hooks/useMessageStream';
|
||||||
import { SessionSummaryModal } from './context_management/SessionSummaryModal';
|
import { SessionSummaryModal } from './context_management/SessionSummaryModal';
|
||||||
import { Recipe } from '../recipe';
|
import { Recipe } from '../recipe';
|
||||||
|
import { ContextManagerProvider, useChatContextManager } from './context_management/ContextManager';
|
||||||
|
import { ContextLengthExceededHandler } from './context_management/ContextLengthExceededHandler';
|
||||||
import {
|
import {
|
||||||
Message,
|
Message,
|
||||||
createUserMessage,
|
createUserMessage,
|
||||||
@@ -30,7 +31,6 @@ import {
|
|||||||
ToolResponseMessageContent,
|
ToolResponseMessageContent,
|
||||||
ToolConfirmationRequestMessageContent,
|
ToolConfirmationRequestMessageContent,
|
||||||
} from '../types/message';
|
} from '../types/message';
|
||||||
import { manageContext } from './context_management';
|
|
||||||
|
|
||||||
export interface ChatType {
|
export interface ChatType {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -63,6 +63,30 @@ export default function ChatView({
|
|||||||
setView: (view: View, viewOptions?: ViewOptions) => void;
|
setView: (view: View, viewOptions?: ViewOptions) => void;
|
||||||
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
|
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContextManagerProvider>
|
||||||
|
<ChatContent
|
||||||
|
chat={chat}
|
||||||
|
setChat={setChat}
|
||||||
|
setView={setView}
|
||||||
|
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
|
||||||
|
/>
|
||||||
|
</ContextManagerProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChatContent({
|
||||||
|
chat,
|
||||||
|
setChat,
|
||||||
|
setView,
|
||||||
|
setIsGoosehintsModalOpen,
|
||||||
|
}: {
|
||||||
|
chat: ChatType;
|
||||||
|
setChat: (chat: ChatType) => void;
|
||||||
|
setView: (view: View, viewOptions?: ViewOptions) => void;
|
||||||
|
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
|
||||||
|
}) {
|
||||||
// Disabled askAi calls to save costs
|
// Disabled askAi calls to save costs
|
||||||
// const [messageMetadata, setMessageMetadata] = useState<Record<string, string[]>>({});
|
// const [messageMetadata, setMessageMetadata] = useState<Record<string, string[]>>({});
|
||||||
const [hasMessages, setHasMessages] = useState(false);
|
const [hasMessages, setHasMessages] = useState(false);
|
||||||
@@ -72,15 +96,24 @@ export default function ChatView({
|
|||||||
const [sessionTokenCount, setSessionTokenCount] = useState<number>(0);
|
const [sessionTokenCount, setSessionTokenCount] = useState<number>(0);
|
||||||
const scrollRef = useRef<ScrollAreaHandle>(null);
|
const scrollRef = useRef<ScrollAreaHandle>(null);
|
||||||
|
|
||||||
const [isSummaryModalOpen, setIsSummaryModalOpen] = useState(false);
|
const {
|
||||||
const [summaryContent, setSummaryContent] = useState('');
|
summaryContent,
|
||||||
const [summarizedThread, setSummarizedThread] = useState<Message[]>([]);
|
summarizedThread,
|
||||||
|
isSummaryModalOpen,
|
||||||
|
resetMessagesWithSummary,
|
||||||
|
closeSummaryModal,
|
||||||
|
updateSummary,
|
||||||
|
hasContextLengthExceededContent,
|
||||||
|
} = useChatContextManager();
|
||||||
|
|
||||||
// Add this function to handle opening the summary modal with content
|
useEffect(() => {
|
||||||
const handleViewSummary = (summary: string) => {
|
// Log all messages when the component first mounts
|
||||||
setSummaryContent(summary);
|
console.log('Initial messages when resuming session:', chat.messages);
|
||||||
setIsSummaryModalOpen(true);
|
window.electron.logInfo(
|
||||||
};
|
'Initial messages when resuming session: ' + JSON.stringify(chat.messages, null, 2)
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []); // Empty dependency array means this runs once on mount;
|
||||||
|
|
||||||
// Get recipeConfig directly from appConfig
|
// Get recipeConfig directly from appConfig
|
||||||
const recipeConfig = window.appConfig.get('recipeConfig') as Recipe | null;
|
const recipeConfig = window.appConfig.get('recipeConfig') as Recipe | null;
|
||||||
@@ -103,6 +136,12 @@ export default function ChatView({
|
|||||||
onFinish: async (_message, _reason) => {
|
onFinish: async (_message, _reason) => {
|
||||||
window.electron.stopPowerSaveBlocker();
|
window.electron.stopPowerSaveBlocker();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (scrollRef.current?.scrollToBottom) {
|
||||||
|
scrollRef.current.scrollToBottom();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
|
||||||
// Disabled askAi calls to save costs
|
// Disabled askAi calls to save costs
|
||||||
// const messageText = getTextContent(message);
|
// const messageText = getTextContent(message);
|
||||||
// const fetchResponses = await askAi(messageText);
|
// const fetchResponses = await askAi(messageText);
|
||||||
@@ -118,11 +157,6 @@ export default function ChatView({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onToolCall: (toolCall: string) => {
|
|
||||||
// Handle tool calls if needed
|
|
||||||
console.log('Tool call received:', toolCall);
|
|
||||||
// Implement tool call handling logic here
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for make-agent-from-chat event
|
// Listen for make-agent-from-chat event
|
||||||
@@ -201,11 +235,27 @@ export default function ChatView({
|
|||||||
window.electron.startPowerSaveBlocker();
|
window.electron.startPowerSaveBlocker();
|
||||||
const customEvent = e as CustomEvent;
|
const customEvent = e as CustomEvent;
|
||||||
const content = customEvent.detail?.value || '';
|
const content = customEvent.detail?.value || '';
|
||||||
|
|
||||||
if (content.trim()) {
|
if (content.trim()) {
|
||||||
setLastInteractionTime(Date.now());
|
setLastInteractionTime(Date.now());
|
||||||
append(createUserMessage(content));
|
|
||||||
if (scrollRef.current?.scrollToBottom) {
|
if (process.env.ALPHA && summarizedThread.length > 0) {
|
||||||
scrollRef.current.scrollToBottom();
|
// First reset the messages with the summary
|
||||||
|
resetMessagesWithSummary(messages, setMessages);
|
||||||
|
|
||||||
|
// Then append the new user message
|
||||||
|
setTimeout(() => {
|
||||||
|
append(createUserMessage(content));
|
||||||
|
if (scrollRef.current?.scrollToBottom) {
|
||||||
|
scrollRef.current.scrollToBottom();
|
||||||
|
}
|
||||||
|
}, 150); // Small delay to ensure state updates properly
|
||||||
|
} else {
|
||||||
|
// Normal flow - just append the message
|
||||||
|
append(createUserMessage(content));
|
||||||
|
if (scrollRef.current?.scrollToBottom) {
|
||||||
|
scrollRef.current.scrollToBottom();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -300,55 +350,12 @@ export default function ChatView({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add this function to ChatView.tsx to detect if a message contains ContextLengthExceededContent
|
|
||||||
const hasContextLengthExceededContent = (message: Message): boolean => {
|
|
||||||
return message.content.some((content) => content.type === 'contextLengthExceeded');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleContextLengthExceeded = async () => {
|
|
||||||
// If we already have a summary, use that
|
|
||||||
if (summaryContent) {
|
|
||||||
return summaryContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, generate a summary
|
|
||||||
const response = await manageContext({ messages: messages, manageAction: 'summarize' });
|
|
||||||
setSummarizedThread(response.messages);
|
|
||||||
return response.messages[0].text;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SummarizedNotification = ({
|
|
||||||
onViewSummary,
|
|
||||||
}: {
|
|
||||||
onViewSummary: (summaryContent: string) => void;
|
|
||||||
}) => {
|
|
||||||
const handleViewSummary = async () => {
|
|
||||||
// Await the result to get a string
|
|
||||||
const summary = summaryContent || (await handleContextLengthExceeded());
|
|
||||||
onViewSummary(summary); // Now always passing a string
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col items-start mt-1 pl-4">
|
|
||||||
<span className="text-xs text-gray-400 italic">Session summarized</span>
|
|
||||||
<button
|
|
||||||
onClick={handleViewSummary}
|
|
||||||
className="text-xs text-textStandard cursor-pointer hover:text-textSubtle transition-colors mt-1"
|
|
||||||
>
|
|
||||||
View or edit summary
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Filter out standalone tool response messages for rendering
|
// Filter out standalone tool response messages for rendering
|
||||||
// They will be shown as part of the tool invocation in the assistant message
|
// They will be shown as part of the tool invocation in the assistant message
|
||||||
const filteredMessages = messages.filter((message) => {
|
const filteredMessages = messages.filter((message) => {
|
||||||
// TODO: use this summarized thread in the chat window
|
// Only filter out when display is explicitly false
|
||||||
if (summarizedThread.length > 0) {
|
if (message.display === false) return false;
|
||||||
// we have a summarized thread
|
|
||||||
console.log('summarized thread has been created --', summarizedThread);
|
|
||||||
}
|
|
||||||
// Keep all assistant messages and user messages that aren't just tool responses
|
// Keep all assistant messages and user messages that aren't just tool responses
|
||||||
if (message.role === 'assistant') return true;
|
if (message.role === 'assistant') return true;
|
||||||
|
|
||||||
@@ -440,8 +447,10 @@ export default function ChatView({
|
|||||||
<>
|
<>
|
||||||
{/* Only render GooseMessage if it's not a CLE message (and we are not in alpha mode) */}
|
{/* Only render GooseMessage if it's not a CLE message (and we are not in alpha mode) */}
|
||||||
{process.env.ALPHA && hasContextLengthExceededContent(message) ? (
|
{process.env.ALPHA && hasContextLengthExceededContent(message) ? (
|
||||||
// Render the summarized notification for CLE messages only in alpha mode
|
<ContextLengthExceededHandler
|
||||||
<SummarizedNotification onViewSummary={handleViewSummary} />
|
messages={messages}
|
||||||
|
messageId={message.id ?? message.created.toString()}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<GooseMessage
|
<GooseMessage
|
||||||
messageHistoryIndex={chat?.messageHistoryIndex}
|
messageHistoryIndex={chat?.messageHistoryIndex}
|
||||||
@@ -502,11 +511,10 @@ export default function ChatView({
|
|||||||
{process.env.ALPHA && (
|
{process.env.ALPHA && (
|
||||||
<SessionSummaryModal
|
<SessionSummaryModal
|
||||||
isOpen={isSummaryModalOpen}
|
isOpen={isSummaryModalOpen}
|
||||||
onClose={() => setIsSummaryModalOpen(false)}
|
onClose={closeSummaryModal}
|
||||||
onSave={(editedContent) => {
|
onSave={(editedContent) => {
|
||||||
console.log('Saving summary...');
|
updateSummary(editedContent);
|
||||||
setSummaryContent(editedContent);
|
closeSummaryModal();
|
||||||
setIsSummaryModalOpen(false);
|
|
||||||
}}
|
}}
|
||||||
summaryContent={summaryContent}
|
summaryContent={summaryContent}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
|
import { Message } from '../../types/message';
|
||||||
|
import { useChatContextManager } from './ContextManager';
|
||||||
|
|
||||||
|
interface ContextLengthExceededHandlerProps {
|
||||||
|
messages: Message[];
|
||||||
|
messageId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ContextLengthExceededHandler: React.FC<ContextLengthExceededHandlerProps> = ({
|
||||||
|
messages,
|
||||||
|
messageId,
|
||||||
|
}) => {
|
||||||
|
const { fetchSummary, summaryContent, isLoadingSummary, errorLoadingSummary, openSummaryModal } =
|
||||||
|
useChatContextManager();
|
||||||
|
|
||||||
|
const [hasFetchStarted, setHasFetchStarted] = useState(false);
|
||||||
|
|
||||||
|
// Find the relevant message to check if it's the latest
|
||||||
|
const isCurrentMessageLatest =
|
||||||
|
messageId === messages[messages.length - 1].id ||
|
||||||
|
messageId === messages[messages.length - 1].created.toString();
|
||||||
|
|
||||||
|
// Only allow interaction for the most recent context length exceeded event
|
||||||
|
const shouldAllowSummaryInteraction = isCurrentMessageLatest;
|
||||||
|
|
||||||
|
// Use a ref to track if we've started the fetch
|
||||||
|
const fetchStartedRef = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Automatically fetch summary if conditions are met
|
||||||
|
if (
|
||||||
|
!summaryContent &&
|
||||||
|
!hasFetchStarted &&
|
||||||
|
shouldAllowSummaryInteraction &&
|
||||||
|
!fetchStartedRef.current
|
||||||
|
) {
|
||||||
|
setHasFetchStarted(true);
|
||||||
|
fetchStartedRef.current = true;
|
||||||
|
fetchSummary(messages);
|
||||||
|
}
|
||||||
|
}, [fetchSummary, hasFetchStarted, messages, shouldAllowSummaryInteraction, summaryContent]);
|
||||||
|
|
||||||
|
// Handle retry
|
||||||
|
const handleRetry = () => {
|
||||||
|
if (!shouldAllowSummaryInteraction) return;
|
||||||
|
fetchSummary(messages);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render the notification UI
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-start mt-1 pl-4">
|
||||||
|
{isLoadingSummary && shouldAllowSummaryInteraction ? (
|
||||||
|
// Only show loading indicator during loading state
|
||||||
|
<div className="flex items-center text-xs text-gray-400">
|
||||||
|
<span className="mr-2">Preparing summary...</span>
|
||||||
|
<span className="animate-spin h-3 w-3 border-2 border-gray-400 rounded-full border-t-transparent"></span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// Show different UI based on whether it's already handled
|
||||||
|
<>
|
||||||
|
<span className="text-xs text-gray-400 italic">{'Session summarized'}</span>
|
||||||
|
|
||||||
|
{/* Only show the button if its last message */}
|
||||||
|
{shouldAllowSummaryInteraction && (
|
||||||
|
<button
|
||||||
|
onClick={() => (errorLoadingSummary ? handleRetry() : openSummaryModal())}
|
||||||
|
className="text-xs text-textStandard hover:text-textSubtle transition-colors mt-1 flex items-center"
|
||||||
|
>
|
||||||
|
{errorLoadingSummary ? 'Retry loading summary' : 'View or edit summary'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
170
ui/desktop/src/components/context_management/ContextManager.tsx
Normal file
170
ui/desktop/src/components/context_management/ContextManager.tsx
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
|
import { Message } from '../../types/message';
|
||||||
|
import { manageContextFromBackend, convertApiMessageToFrontendMessage } from './index';
|
||||||
|
|
||||||
|
// Define the context management interface
|
||||||
|
interface ContextManagerState {
|
||||||
|
summaryContent: string;
|
||||||
|
summarizedThread: Message[];
|
||||||
|
isSummaryModalOpen: boolean;
|
||||||
|
isLoadingSummary: boolean;
|
||||||
|
errorLoadingSummary: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ContextManagerActions {
|
||||||
|
fetchSummary: (messages: Message[]) => Promise<void>;
|
||||||
|
updateSummary: (newSummaryContent: string) => void;
|
||||||
|
resetMessagesWithSummary: (
|
||||||
|
messages: Message[],
|
||||||
|
setMessages: (messages: Message[]) => void
|
||||||
|
) => void;
|
||||||
|
openSummaryModal: () => void;
|
||||||
|
closeSummaryModal: () => void;
|
||||||
|
hasContextLengthExceededContent: (message: Message) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the context
|
||||||
|
const ContextManagerContext = createContext<
|
||||||
|
(ContextManagerState & ContextManagerActions) | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
// Create the provider component
|
||||||
|
export const ContextManagerProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
|
const [summaryContent, setSummaryContent] = useState<string>('');
|
||||||
|
const [summarizedThread, setSummarizedThread] = useState<Message[]>([]);
|
||||||
|
const [isSummaryModalOpen, setIsSummaryModalOpen] = useState<boolean>(false);
|
||||||
|
const [isLoadingSummary, setIsLoadingSummary] = useState<boolean>(false);
|
||||||
|
const [errorLoadingSummary, setErrorLoadingSummary] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const fetchSummary = async (messages: Message[]) => {
|
||||||
|
setIsLoadingSummary(true);
|
||||||
|
setErrorLoadingSummary(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await manageContextFromBackend({
|
||||||
|
messages: messages,
|
||||||
|
manageAction: 'summarize',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert API messages to frontend messages
|
||||||
|
const convertedMessages = response.messages.map((apiMessage) =>
|
||||||
|
convertApiMessageToFrontendMessage(apiMessage)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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) => {
|
||||||
|
// Update the summary content
|
||||||
|
setSummaryContent(newSummaryContent);
|
||||||
|
|
||||||
|
// Update the thread if it exists
|
||||||
|
if (summarizedThread.length > 0) {
|
||||||
|
// Create a deep copy of the thread
|
||||||
|
const updatedThread = [...summarizedThread];
|
||||||
|
|
||||||
|
// Create a copy of the first message
|
||||||
|
const firstMessage = { ...updatedThread[0] };
|
||||||
|
|
||||||
|
// Create a copy of the content array
|
||||||
|
const updatedContent = [...firstMessage.content];
|
||||||
|
|
||||||
|
// Update the summary text in the first content item
|
||||||
|
if (updatedContent[0] && updatedContent[0].type === 'text') {
|
||||||
|
updatedContent[0] = {
|
||||||
|
...updatedContent[0],
|
||||||
|
text: newSummaryContent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the message with the new content
|
||||||
|
firstMessage.content = updatedContent;
|
||||||
|
updatedThread[0] = firstMessage;
|
||||||
|
|
||||||
|
// Update the thread
|
||||||
|
setSummarizedThread(updatedThread);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetMessagesWithSummary = (
|
||||||
|
messages: Message[],
|
||||||
|
setMessages: (messages: Message[]) => void
|
||||||
|
) => {
|
||||||
|
// Update summarizedThread with metadata
|
||||||
|
const updatedSummarizedThread = summarizedThread.map((msg) => ({
|
||||||
|
...msg,
|
||||||
|
display: false,
|
||||||
|
sendToLLM: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Update list of messages with other metadata
|
||||||
|
const updatedMessages = messages.map((msg) => ({
|
||||||
|
...msg,
|
||||||
|
display: true,
|
||||||
|
sendToLLM: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Make a copy that combines both
|
||||||
|
const newMessages = [...updatedMessages, ...updatedSummarizedThread];
|
||||||
|
|
||||||
|
// Update the messages state
|
||||||
|
setMessages(newMessages);
|
||||||
|
|
||||||
|
// Clear the summarized thread and content
|
||||||
|
setSummarizedThread([]);
|
||||||
|
setSummaryContent('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasContextLengthExceededContent = (message: Message): boolean => {
|
||||||
|
return message.content.some((content) => content.type === 'contextLengthExceeded');
|
||||||
|
};
|
||||||
|
|
||||||
|
const openSummaryModal = () => {
|
||||||
|
setIsSummaryModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeSummaryModal = () => {
|
||||||
|
setIsSummaryModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = {
|
||||||
|
// State
|
||||||
|
summaryContent,
|
||||||
|
summarizedThread,
|
||||||
|
isSummaryModalOpen,
|
||||||
|
isLoadingSummary,
|
||||||
|
errorLoadingSummary,
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
fetchSummary,
|
||||||
|
updateSummary,
|
||||||
|
resetMessagesWithSummary,
|
||||||
|
openSummaryModal,
|
||||||
|
closeSummaryModal,
|
||||||
|
hasContextLengthExceededContent,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ContextManagerContext.Provider value={value}>{children}</ContextManagerContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a hook to use the context
|
||||||
|
export const useChatContextManager = () => {
|
||||||
|
const context = useContext(ContextManagerContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useContextManager must be used within a ContextManagerProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@@ -1,37 +1,119 @@
|
|||||||
import { Message } from '../../types/message';
|
import {
|
||||||
import { getApiUrl, getSecretKey } from '../../config';
|
Message as FrontendMessage,
|
||||||
|
Content as FrontendContent,
|
||||||
|
MessageContent as FrontendMessageContent,
|
||||||
|
ToolCallResult,
|
||||||
|
ToolCall,
|
||||||
|
} from '../../types/message';
|
||||||
|
import {
|
||||||
|
ContextManageRequest,
|
||||||
|
ContextManageResponse,
|
||||||
|
manageContext,
|
||||||
|
Message as ApiMessage,
|
||||||
|
MessageContent as ApiMessageContent,
|
||||||
|
} from '../../api';
|
||||||
|
import { generateId } from 'ai';
|
||||||
|
|
||||||
export async function manageContext({
|
export async function manageContextFromBackend({
|
||||||
messages,
|
messages,
|
||||||
manageAction,
|
manageAction,
|
||||||
}: {
|
}: {
|
||||||
messages: Message[];
|
messages: FrontendMessage[];
|
||||||
manageAction: 'trunction' | 'summarize';
|
manageAction: 'truncation' | 'summarize';
|
||||||
}) {
|
}): Promise<ContextManageResponse> {
|
||||||
const response = await fetch(getApiUrl('/context/manage'), {
|
try {
|
||||||
method: 'POST',
|
const contextManagementRequest = { manageAction, messages };
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
// Cast to the API-expected type
|
||||||
'X-Secret-Key': getSecretKey(),
|
const result = await manageContext({
|
||||||
},
|
body: contextManagementRequest as unknown as ContextManageRequest,
|
||||||
body: JSON.stringify({
|
});
|
||||||
messages,
|
|
||||||
manageAction,
|
// Check for errors in the result
|
||||||
}),
|
if (result.error) {
|
||||||
});
|
throw new Error(`Context management failed: ${result.error}`);
|
||||||
if (!response.ok) {
|
|
||||||
if (!response.ok) {
|
|
||||||
// Get the status text or a default message
|
|
||||||
const errorText = await response.text().catch(() => 'Unknown error');
|
|
||||||
// log error with status and details
|
|
||||||
console.error(
|
|
||||||
`Context management failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
||||||
);
|
|
||||||
throw new Error(
|
|
||||||
`Context management failed: ${response.status} ${response.statusText} - ${errorText}\n\nStart a new session.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract the actual data from the result
|
||||||
|
if (!result.data) {
|
||||||
|
throw new Error('Context management returned no data');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Context management failed: ${error || 'Unknown error'}`);
|
||||||
|
throw new Error(
|
||||||
|
`Context management failed: ${error || 'Unknown error'}\n\nStart a new session.`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const data = await response.json();
|
}
|
||||||
return data;
|
|
||||||
|
// Function to convert API Message to frontend Message
|
||||||
|
export function convertApiMessageToFrontendMessage(apiMessage: ApiMessage): FrontendMessage {
|
||||||
|
return {
|
||||||
|
display: false,
|
||||||
|
sendToLLM: false,
|
||||||
|
id: generateId(),
|
||||||
|
role: apiMessage.role,
|
||||||
|
created: apiMessage.created,
|
||||||
|
content: apiMessage.content
|
||||||
|
.map((apiContent) => mapApiContentToFrontendMessageContent(apiContent))
|
||||||
|
.filter((content): content is FrontendMessageContent => content !== null),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to convert API MessageContent to frontend MessageContent
|
||||||
|
function mapApiContentToFrontendMessageContent(
|
||||||
|
apiContent: ApiMessageContent
|
||||||
|
): FrontendMessageContent | null {
|
||||||
|
// Handle each content type specifically based on its "type" property
|
||||||
|
if (apiContent.type === 'text') {
|
||||||
|
return {
|
||||||
|
type: 'text',
|
||||||
|
text: apiContent.text,
|
||||||
|
annotations: apiContent.annotations as Record<string, unknown> | undefined,
|
||||||
|
};
|
||||||
|
} else if (apiContent.type === 'image') {
|
||||||
|
return {
|
||||||
|
type: 'image',
|
||||||
|
data: apiContent.data,
|
||||||
|
mimeType: apiContent.mimeType,
|
||||||
|
annotations: apiContent.annotations as Record<string, unknown> | undefined,
|
||||||
|
};
|
||||||
|
} else if (apiContent.type === 'toolRequest') {
|
||||||
|
// Ensure the toolCall has the correct type structure
|
||||||
|
const toolCall = apiContent.toolCall as unknown as ToolCallResult<ToolCall>;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'toolRequest',
|
||||||
|
id: apiContent.id,
|
||||||
|
toolCall: toolCall,
|
||||||
|
};
|
||||||
|
} else if (apiContent.type === 'toolResponse') {
|
||||||
|
// Ensure the toolResult has the correct type structure
|
||||||
|
const toolResult = apiContent.toolResult as unknown as ToolCallResult<FrontendContent[]>;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'toolResponse',
|
||||||
|
id: apiContent.id,
|
||||||
|
toolResult: toolResult,
|
||||||
|
};
|
||||||
|
} else if (apiContent.type === 'toolConfirmationRequest') {
|
||||||
|
return {
|
||||||
|
type: 'toolConfirmationRequest',
|
||||||
|
id: apiContent.id,
|
||||||
|
toolName: apiContent.toolName,
|
||||||
|
arguments: apiContent.arguments as Record<string, unknown>,
|
||||||
|
prompt: apiContent.prompt === null ? undefined : apiContent.prompt,
|
||||||
|
};
|
||||||
|
} else if (apiContent.type === 'contextLengthExceeded') {
|
||||||
|
return {
|
||||||
|
type: 'contextLengthExceeded',
|
||||||
|
msg: apiContent.msg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// For types that exist in API but not in frontend, either skip or convert
|
||||||
|
console.warn(`Skipping unsupported content type: ${apiContent.type}`);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,11 +215,26 @@ export function useMessageStream({
|
|||||||
const parsedEvent = JSON.parse(data) as MessageEvent;
|
const parsedEvent = JSON.parse(data) as MessageEvent;
|
||||||
|
|
||||||
switch (parsedEvent.type) {
|
switch (parsedEvent.type) {
|
||||||
case 'Message':
|
case 'Message': {
|
||||||
|
// Create a new message object with the properties preserved or defaulted
|
||||||
|
const newMessage = {
|
||||||
|
...parsedEvent.message,
|
||||||
|
// Only set to true if it's undefined (preserve false values)
|
||||||
|
display:
|
||||||
|
parsedEvent.message.display === undefined
|
||||||
|
? true
|
||||||
|
: parsedEvent.message.display,
|
||||||
|
sendToLLM:
|
||||||
|
parsedEvent.message.sendToLLM === undefined
|
||||||
|
? true
|
||||||
|
: parsedEvent.message.sendToLLM,
|
||||||
|
};
|
||||||
|
|
||||||
// Update messages with the new message
|
// Update messages with the new message
|
||||||
currentMessages = [...currentMessages, parsedEvent.message];
|
currentMessages = [...currentMessages, newMessage];
|
||||||
mutate(currentMessages, false);
|
mutate(currentMessages, false);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'Error':
|
case 'Error':
|
||||||
throw new Error(parsedEvent.error);
|
throw new Error(parsedEvent.error);
|
||||||
@@ -268,9 +283,12 @@ export function useMessageStream({
|
|||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
abortControllerRef.current = abortController;
|
abortControllerRef.current = abortController;
|
||||||
|
|
||||||
|
// Filter out messages where sendToLLM is explicitly false
|
||||||
|
const filteredMessages = requestMessages.filter((message) => message.sendToLLM !== false);
|
||||||
|
|
||||||
// Log request details for debugging
|
// Log request details for debugging
|
||||||
console.log('Request details:', {
|
console.log('Request details:', {
|
||||||
messages: requestMessages,
|
messages: filteredMessages,
|
||||||
body: extraMetadataRef.current.body,
|
body: extraMetadataRef.current.body,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ export interface Message {
|
|||||||
role: Role;
|
role: Role;
|
||||||
created: number;
|
created: number;
|
||||||
content: MessageContent[];
|
content: MessageContent[];
|
||||||
|
display: boolean;
|
||||||
|
sendToLLM: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions to create messages
|
// Helper functions to create messages
|
||||||
|
|||||||
Reference in New Issue
Block a user