From 8f04431507741eef6d93a79993a92d0ab8909f8b Mon Sep 17 00:00:00 2001 From: Nithin Bose Date: Fri, 1 Aug 2025 01:02:38 -0700 Subject: [PATCH 1/3] Add collapsible user input display for long messages - Implement smart headline generation that extracts email subjects or truncates text intelligently - Add collapsible UI for messages over 150 characters to preserve screen space for answers - Maintain original large text styling for short messages (backward compatible) - Use existing Radix UI Collapsible components with smooth expand/collapse animations - Include visual indicators (chevron icons) for interaction state Fixes issue where long user inputs dominated the results page with oversized text. --- .../components/chat-interface/messages.tsx | 82 ++++++++++++++++++- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/frontend/app/components/chat-interface/messages.tsx b/frontend/app/components/chat-interface/messages.tsx index d79ff2d4d..e09863988 100644 --- a/frontend/app/components/chat-interface/messages.tsx +++ b/frontend/app/components/chat-interface/messages.tsx @@ -6,19 +6,95 @@ import { useThreadRuntime, } from "@assistant-ui/react"; import { useState, type FC } from "react"; +import { ChevronDownIcon, ChevronRightIcon } from "lucide-react"; import { MarkdownText } from "../ui/assistant-ui/markdown-text"; import { useGraphContext } from "@/app/contexts/GraphContext"; import { useRuns } from "@/app/hooks/useRuns"; import { TooltipIconButton } from "../ui/assistant-ui/tooltip-icon-button"; import { ThumbsDownIcon, ThumbsUpIcon } from "lucide-react"; +import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "../ui/collapsible"; + +// Helper function to generate a brief headline from long text +function generateHeadline(text: string, maxLength: number = 60): string { + if (text.length <= maxLength) return text; + + // Try to find the first sentence or logical break + const firstSentence = text.match(/^[^.!?]*[.!?]/); + if (firstSentence && firstSentence[0].length <= maxLength) { + return firstSentence[0].trim(); + } + + // Try to find subject line if it looks like an email + const subjectMatch = text.match(/^Subject:\s*([^\n\r]+)/i); + if (subjectMatch && subjectMatch[1].length <= maxLength) { + return subjectMatch[1].trim(); + } + + // Fallback to truncated text at word boundary + const truncated = text.substring(0, maxLength); + const lastSpace = truncated.lastIndexOf(' '); + return lastSpace > maxLength * 0.7 + ? truncated.substring(0, lastSpace) + "..." + : truncated + "..."; +} export const UserMessage: FC = () => { + const message = useMessage(); + const [isExpanded, setIsExpanded] = useState(false); + + // Get the text content from the message + const textContent = message.content?.[0]?.type === "text" ? message.content[0].text : ""; + const isLongText = textContent.length > 150; // Threshold for collapsible behavior + + if (!isLongText) { + // For short text, use the original styling + return ( + +
+ +
+
+ ); + } + + const headline = generateHeadline(textContent); + return ( -
- -
+ + +
+
+ {headline} +
+
+ {isExpanded ? ( + + ) : ( + + )} +
+
+
+ + +
+
+
+ Full Message +
+
+ {textContent} +
+
+
+
+
); }; From 4450b89ffde220289b9250cf89119ad227a76214 Mon Sep 17 00:00:00 2001 From: Nithin Bose Date: Fri, 1 Aug 2025 01:47:19 -0700 Subject: [PATCH 2/3] Fix deprecated LangChain imports and update GitHub Actions versions - Update deprecated import paths to use langchain_core and provider-specific packages - Update GitHub Actions from v3 to v4 for better compatibility - Fixes failing eval workflow in CI --- .github/actions/poetry_setup/action.yml | 4 ++-- .github/workflows/eval.yml | 2 +- _scripts/evaluate_chains.py | 3 ++- _scripts/evaluate_chains_agent.py | 2 +- _scripts/evaluate_chains_improved_chain.py | 3 ++- _scripts/evaluate_chat_langchain.py | 3 ++- backend/utils.py | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/actions/poetry_setup/action.yml b/.github/actions/poetry_setup/action.yml index df04e1e71..bf169d9a0 100644 --- a/.github/actions/poetry_setup/action.yml +++ b/.github/actions/poetry_setup/action.yml @@ -28,7 +28,7 @@ runs: with: python-version: ${{ inputs.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: cache-bin-poetry name: Cache Poetry binary - Python ${{ inputs.python-version }} env: @@ -75,7 +75,7 @@ runs: run: pipx install "poetry==$POETRY_VERSION" --python '${{ steps.setup-python.outputs.python-path }}' --verbose - name: Restore pip and poetry cached dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 env: SEGMENT_DOWNLOAD_TIMEOUT_MIN: "4" with: diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index 85ff4dfe2..efff7224d 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -19,7 +19,7 @@ jobs: environment: Evaluation steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python + Poetry uses: "./.github/actions/poetry_setup" diff --git a/_scripts/evaluate_chains.py b/_scripts/evaluate_chains.py index 15c28316b..6098a9207 100644 --- a/_scripts/evaluate_chains.py +++ b/_scripts/evaluate_chains.py @@ -7,7 +7,8 @@ import weaviate from langchain import load as langchain_load -from langchain.chat_models import ChatAnthropic, ChatOpenAI +from langchain_anthropic import ChatAnthropic +from langchain_openai import ChatOpenAI from langchain.embeddings import OpenAIEmbeddings from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate from langchain.schema.output_parser import StrOutputParser diff --git a/_scripts/evaluate_chains_agent.py b/_scripts/evaluate_chains_agent.py index fab072dd2..497b1ae10 100644 --- a/_scripts/evaluate_chains_agent.py +++ b/_scripts/evaluate_chains_agent.py @@ -10,7 +10,7 @@ AgentTokenBufferMemory, ) from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent -from langchain.chat_models import ChatOpenAI +from langchain_openai import ChatOpenAI from langchain.embeddings import OpenAIEmbeddings from langchain.prompts import MessagesPlaceholder from langchain.schema.messages import SystemMessage diff --git a/_scripts/evaluate_chains_improved_chain.py b/_scripts/evaluate_chains_improved_chain.py index a723d5457..b22a7104d 100644 --- a/_scripts/evaluate_chains_improved_chain.py +++ b/_scripts/evaluate_chains_improved_chain.py @@ -7,7 +7,8 @@ import weaviate from langchain import load as langchain_load -from langchain.chat_models import ChatAnthropic, ChatOpenAI +from langchain_anthropic import ChatAnthropic +from langchain_openai import ChatOpenAI from langchain.embeddings import OpenAIEmbeddings from langchain.output_parsers import CommaSeparatedListOutputParser from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate diff --git a/_scripts/evaluate_chat_langchain.py b/_scripts/evaluate_chat_langchain.py index 9724ac716..bd1492f4f 100644 --- a/_scripts/evaluate_chat_langchain.py +++ b/_scripts/evaluate_chat_langchain.py @@ -2,7 +2,8 @@ # This is ugly import argparse -from langchain.chat_models import ChatAnthropic, ChatOpenAI +from langchain_anthropic import ChatAnthropic +from langchain_openai import ChatOpenAI from langchain.smith import RunEvalConfig from langsmith import Client diff --git a/backend/utils.py b/backend/utils.py index d4dbc5bff..8603cf4ce 100644 --- a/backend/utils.py +++ b/backend/utils.py @@ -8,7 +8,7 @@ import uuid from typing import Any, Literal, Optional, Union -from langchain.chat_models import init_chat_model +from langchain_core.chat_models import init_chat_model from langchain_core.documents import Document from langchain_core.language_models import BaseChatModel From e4fa8ff9f268dce3202f5ae3881c1c60787ad9a6 Mon Sep 17 00:00:00 2001 From: Nithin Bose Date: Fri, 1 Aug 2025 02:13:04 -0700 Subject: [PATCH 3/3] Fix import sorting and module import issues from commit 4450b89 - Fix I001 import sorting errors in evaluation scripts by moving provider-specific imports after langchain.* imports - Fix ModuleNotFoundError by correcting import path from langchain_core.chat_models to langchain.chat_models in backend/utils.py - Apply code formatting fixes using ruff and prettier - Addresses failing tests and linting issues in CI --- _scripts/clear_index.py | 1 + _scripts/evaluate_chains.py | 4 +-- _scripts/evaluate_chains_agent.py | 2 +- _scripts/evaluate_chains_improved_chain.py | 4 +-- _scripts/evaluate_chat_langchain.py | 2 +- backend/ingest.py | 1 + backend/utils.py | 2 +- .../components/chat-interface/messages.tsx | 29 +++++++++++-------- 8 files changed, 26 insertions(+), 19 deletions(-) diff --git a/_scripts/clear_index.py b/_scripts/clear_index.py index 07221e895..b68c3c074 100644 --- a/_scripts/clear_index.py +++ b/_scripts/clear_index.py @@ -1,4 +1,5 @@ """Clear Weaviate index.""" + import logging import os diff --git a/_scripts/evaluate_chains.py b/_scripts/evaluate_chains.py index 6098a9207..e6da1bd18 100644 --- a/_scripts/evaluate_chains.py +++ b/_scripts/evaluate_chains.py @@ -7,8 +7,6 @@ import weaviate from langchain import load as langchain_load -from langchain_anthropic import ChatAnthropic -from langchain_openai import ChatOpenAI from langchain.embeddings import OpenAIEmbeddings from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate from langchain.schema.output_parser import StrOutputParser @@ -16,6 +14,8 @@ from langchain.schema.runnable import Runnable, RunnableMap from langchain.smith import RunEvalConfig from langchain.vectorstores import Weaviate +from langchain_anthropic import ChatAnthropic +from langchain_openai import ChatOpenAI from langsmith import Client, RunEvaluator from langsmith.evaluation.evaluator import EvaluationResult from langsmith.schemas import Example, Run diff --git a/_scripts/evaluate_chains_agent.py b/_scripts/evaluate_chains_agent.py index 497b1ae10..8092e9b1a 100644 --- a/_scripts/evaluate_chains_agent.py +++ b/_scripts/evaluate_chains_agent.py @@ -10,12 +10,12 @@ AgentTokenBufferMemory, ) from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent -from langchain_openai import ChatOpenAI from langchain.embeddings import OpenAIEmbeddings from langchain.prompts import MessagesPlaceholder from langchain.schema.messages import SystemMessage from langchain.smith import RunEvalConfig, run_on_dataset from langchain.vectorstores import Weaviate +from langchain_openai import ChatOpenAI from langsmith import Client, RunEvaluator from langsmith.evaluation.evaluator import EvaluationResult from langsmith.schemas import Example, Run diff --git a/_scripts/evaluate_chains_improved_chain.py b/_scripts/evaluate_chains_improved_chain.py index b22a7104d..3079eb112 100644 --- a/_scripts/evaluate_chains_improved_chain.py +++ b/_scripts/evaluate_chains_improved_chain.py @@ -7,8 +7,6 @@ import weaviate from langchain import load as langchain_load -from langchain_anthropic import ChatAnthropic -from langchain_openai import ChatOpenAI from langchain.embeddings import OpenAIEmbeddings from langchain.output_parsers import CommaSeparatedListOutputParser from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate @@ -17,6 +15,8 @@ from langchain.schema.runnable import Runnable, RunnableMap from langchain.smith import RunEvalConfig from langchain.vectorstores import Weaviate +from langchain_anthropic import ChatAnthropic +from langchain_openai import ChatOpenAI from langsmith import Client, RunEvaluator from langsmith.evaluation.evaluator import EvaluationResult from langsmith.schemas import Example, Run diff --git a/_scripts/evaluate_chat_langchain.py b/_scripts/evaluate_chat_langchain.py index bd1492f4f..6b5515aa0 100644 --- a/_scripts/evaluate_chat_langchain.py +++ b/_scripts/evaluate_chat_langchain.py @@ -2,9 +2,9 @@ # This is ugly import argparse +from langchain.smith import RunEvalConfig from langchain_anthropic import ChatAnthropic from langchain_openai import ChatOpenAI -from langchain.smith import RunEvalConfig from langsmith import Client # Ugly. Requires PYTHONATH=$(PWD) to run diff --git a/backend/ingest.py b/backend/ingest.py index 80c265995..f961f0681 100644 --- a/backend/ingest.py +++ b/backend/ingest.py @@ -1,4 +1,5 @@ """Load html from files, clean up, split, ingest into Weaviate.""" + import logging import os import re diff --git a/backend/utils.py b/backend/utils.py index 8603cf4ce..d4dbc5bff 100644 --- a/backend/utils.py +++ b/backend/utils.py @@ -8,7 +8,7 @@ import uuid from typing import Any, Literal, Optional, Union -from langchain_core.chat_models import init_chat_model +from langchain.chat_models import init_chat_model from langchain_core.documents import Document from langchain_core.language_models import BaseChatModel diff --git a/frontend/app/components/chat-interface/messages.tsx b/frontend/app/components/chat-interface/messages.tsx index e09863988..da425a7f3 100644 --- a/frontend/app/components/chat-interface/messages.tsx +++ b/frontend/app/components/chat-interface/messages.tsx @@ -13,28 +13,32 @@ import { useGraphContext } from "@/app/contexts/GraphContext"; import { useRuns } from "@/app/hooks/useRuns"; import { TooltipIconButton } from "../ui/assistant-ui/tooltip-icon-button"; import { ThumbsDownIcon, ThumbsUpIcon } from "lucide-react"; -import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "../ui/collapsible"; +import { + Collapsible, + CollapsibleTrigger, + CollapsibleContent, +} from "../ui/collapsible"; // Helper function to generate a brief headline from long text function generateHeadline(text: string, maxLength: number = 60): string { if (text.length <= maxLength) return text; - + // Try to find the first sentence or logical break const firstSentence = text.match(/^[^.!?]*[.!?]/); if (firstSentence && firstSentence[0].length <= maxLength) { return firstSentence[0].trim(); } - + // Try to find subject line if it looks like an email const subjectMatch = text.match(/^Subject:\s*([^\n\r]+)/i); if (subjectMatch && subjectMatch[1].length <= maxLength) { return subjectMatch[1].trim(); } - + // Fallback to truncated text at word boundary const truncated = text.substring(0, maxLength); - const lastSpace = truncated.lastIndexOf(' '); - return lastSpace > maxLength * 0.7 + const lastSpace = truncated.lastIndexOf(" "); + return lastSpace > maxLength * 0.7 ? truncated.substring(0, lastSpace) + "..." : truncated + "..."; } @@ -42,11 +46,12 @@ function generateHeadline(text: string, maxLength: number = 60): string { export const UserMessage: FC = () => { const message = useMessage(); const [isExpanded, setIsExpanded] = useState(false); - + // Get the text content from the message - const textContent = message.content?.[0]?.type === "text" ? message.content[0].text : ""; + const textContent = + message.content?.[0]?.type === "text" ? message.content[0].text : ""; const isLongText = textContent.length > 150; // Threshold for collapsible behavior - + if (!isLongText) { // For short text, use the original styling return ( @@ -62,8 +67,8 @@ export const UserMessage: FC = () => { return ( - @@ -81,7 +86,7 @@ export const UserMessage: FC = () => { - +