Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clients/search-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"react-scan": "^0.3.2",
"react-snap-carousel": "^0.5.0",
"tailwind-merge": "^3.0.2",
"trieve-ts-sdk": "^0.0.69"
"trieve-ts-sdk": "0.0.73"
},
"peerDependencies": {
"react": "^18.3.1 || ^19.0.0-rc",
Expand Down
38 changes: 28 additions & 10 deletions clients/search-component/src/TrieveModal/Chat/FollowupQueries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,47 @@ import { useAutoAnimate } from "@formkit/auto-animate/react";
import { useModalState } from "../../utils/hooks/modal-context";

export const FollowupQueries = () => {
const { props } = useModalState();
const { isDoneReading, askQuestion } = useChatState();

const { props, trieveSDK, fingerprint } = useModalState();
const { isDoneReading, askQuestion, messages } = useChatState();
const { suggestedQuestions, isLoadingSuggestedQueries } =
useFollowupQuestions();

const [parent] = useAutoAnimate();

if (!isDoneReading || props.previewTopicId) {
return null;
}

const handleFollowupQuery = async (q: string) => {
const requestId = messages[messages.length - 1].queryId;

if (requestId) {
await trieveSDK.sendAnalyticsEvent({
event_name: `site-followup_query`,
event_type: "click",
user_id: fingerprint,
location: window.location.href,
metadata: {
followup_query: q,
component_props: props,
},
request: {
request_id: requestId,
request_type: "rag",
},
});
};
askQuestion(q);

}

return (
<div ref={parent} className="followup-questions">
{suggestedQuestions?.map((q) => (
<button
onClick={() => {
askQuestion(q);
}}
onClick={() => handleFollowupQuery(q)}
key={q}
className={`followup-question ${
isLoadingSuggestedQueries ? "loading" : ""
}`}
className={`followup-question ${isLoadingSuggestedQueries ? "loading" : ""
}`}
>
<SparklesIcon className="followup-icon" />
{q}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,40 @@ export const SuggestedQuestions = ({
const { suggestedQuestions, isLoadingSuggestedQueries, getQuestions } =
useSuggestedQuestions();

const { props } = useModalState();
const { props, trieveSDK, fingerprint } = useModalState();
const [parent] = useAutoAnimate({ duration: 100 });

if (messages.length) {
return null;
}

const handleSuggestedQuestion = async (q: string) => {
const requestId = messages[messages.length - 1].queryId;

if (requestId) {
await trieveSDK.sendAnalyticsEvent({
event_name: `site-followup_query`,
event_type: "click",
user_id: fingerprint,
location: window.location.href,
metadata: {
followup_query: q,
component_props: props,
},
request: {
request_id: requestId,
request_type: "rag",
},
});
};

setCurrentQuestion(q);
askQuestion(q);
if (onMessageSend) {
onMessageSend();
}
};

return (
<div className="ai-message initial-message">
<AIInitialMessage />
Expand Down Expand Up @@ -54,16 +81,11 @@ export const SuggestedQuestions = ({
{suggestedQuestions?.map((q) => (
<button
onClick={() => {
setCurrentQuestion(q);
askQuestion(q);
if (onMessageSend) {
onMessageSend();
}
handleSuggestedQuestion(q);
}}
key={q}
className={`suggested-question tv-flex tv-gap-1 tv-items-center${
isLoadingSuggestedQueries ? " loading" : ""
}`}
className={`suggested-question tv-flex tv-gap-1 tv-items-center${isLoadingSuggestedQueries ? " loading" : ""
}`}
>
<SparklesIcon width={15} height={15} />
{q}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useTrieve } from "app/context/trieveContext";
import { popularSuggestedQueriesQuery } from "app/queries/analytics/chat";
import { useEffect, useState } from "react";
import { TopicAnalyticsFilter } from "trieve-ts-sdk";
import { BasicTableComponent } from "../BasicTableComponent";

export const PopularSuggestedQueries = ({
filters,
}: {
filters: TopicAnalyticsFilter;
}) => {
const { trieve } = useTrieve();
const [page, setPage] = useState(1);
const { data } = useQuery(
popularSuggestedQueriesQuery(trieve, filters, page),
);

const client = useQueryClient();
useEffect(() => {
client.prefetchQuery(
popularSuggestedQueriesQuery(trieve, filters, page + 1),
);
}, [page, filters]);

const mappedData = data
? data.top_queries.map((query: { query: string; count: number }) => [
query.query,
query.count,
])
: [];

return (
<BasicTableComponent
data={mappedData}
page={page}
setPage={setPage}
label="Most Popular Suggested Queries"
tooltipContent="The most popular suggested queries by number of requests."
tableContentTypes={["text", "numeric"]}
tableHeadings={["Query", "Count"]}
hasNext={data?.top_queries.length == 10}
/>
);
};
19 changes: 19 additions & 0 deletions clients/trieve-shopify-extension/app/queries/analytics/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
EventNameAndCountsResponse,
TopicAnalyticsFilter,
PopularChatsResponse,
FollowupQueriesResponse,
} from "trieve-ts-sdk";

export const topicsUsageQuery = (
Expand Down Expand Up @@ -187,3 +188,21 @@ export const popularChatsQuery = (
},
} satisfies QueryOptions;
};

export const popularSuggestedQueriesQuery = (
trieve: TrieveSDK,
filters: RAGAnalyticsFilter,
page: number,
) => {
return {
queryKey: ["popularSuggestedQueries", filters, page],
queryFn: async () => {
const result = await trieve.getRagAnalytics({
filter: filters,
type: "followup_queries",
page: page,
});
return result as FollowupQueriesResponse;
},
} satisfies QueryOptions;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ChatRevenue } from "app/components/analytics/chat/ChatRevenue";
import { ChatUserJourneyFunnel } from "app/components/analytics/chat/ChatUserJourneyFunnel";
import { MessagesPerUser } from "app/components/analytics/chat/MessagesPerUser";
import { PopularChatsTable } from "app/components/analytics/chat/PopularChatsTable";
import { PopularSuggestedQueries } from "app/components/analytics/chat/PopularSuggestedQueries";
import { TopicCTRRate } from "app/components/analytics/chat/TopicCTRRate";
import { TopicsUsage } from "app/components/analytics/chat/TopicsGraph";
import { SearchFilterBar } from "app/components/analytics/FilterBar";
Expand All @@ -17,10 +18,7 @@ export default function ChatAnalyticsPage() {
const [granularity, setGranularity] = useState<Granularity>("day");

return (
<Page
fullWidth={true}
title="Chat Analytics"
>
<Page fullWidth={true} title="Chat Analytics">
<SearchFilterBar
granularity={granularity}
setGranularity={setGranularity}
Expand All @@ -30,16 +28,25 @@ export default function ChatAnalyticsPage() {
<Grid>
<Grid.Cell columnSpan={{ xs: 6, sm: 6, md: 6, lg: 6, xl: 6 }}>
<div className="flex flex-col gap-4">
<ChatRevenue filters={filters} granularity={granularity} direct={true} />
<ChatRevenue
filters={filters}
granularity={granularity}
direct={true}
/>
<TopicsUsage filters={filters} granularity={granularity} />
<MessagesPerUser filters={filters} granularity={granularity} />
<ChatConversionRate filters={filters} granularity={granularity} />
<PopularChatsTable filters={filters} />
<PopularSuggestedQueries filters={filters} />
</div>
</Grid.Cell>
<Grid.Cell columnSpan={{ xs: 6, sm: 6, md: 6, lg: 6, xl: 6 }}>
<div className="flex flex-col gap-4">
<ChatRevenue filters={filters} granularity={granularity} direct={false} />
<ChatRevenue
filters={filters}
granularity={granularity}
direct={false}
/>
<ChatUserJourneyFunnel filters={filters} />
<TopicCTRRate filters={filters} granularity={granularity} />
<ChatAverageRating filters={filters} granularity={granularity} />
Expand Down
66 changes: 64 additions & 2 deletions clients/ts-sdk/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -11217,11 +11217,11 @@
},
"event_name": {
"type": "string",
"description": "The name of the event, e.g. \"Added to Cart\", \"Purchased\", \"Viewed Home Page\", \"Clicked\", \"Filter Clicked\"."
"description": "The name of the event, e.g. \"Added to Cart\", \"Purchased\", \"Viewed Home Page\", \"Clicked\", \"Filter Clicked\", \"Followup Query\"."
},
"event_type": {
"type": "string",
"description": "The type of event, \"add_to_cart\", \"purchase\", \"view\", \"click\", \"filter_clicked\"."
"description": "The type of event, \"add_to_cart\", \"purchase\", \"view\", \"click\", \"filter_clicked\", \"followup_query\""
},
"id": {
"type": "string",
Expand Down Expand Up @@ -12365,6 +12365,36 @@
}
}
},
"FollowupQueriesResponse": {
"type": "object",
"required": [
"top_queries"
],
"properties": {
"top_queries": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FollowupQuery"
}
}
}
},
"FollowupQuery": {
"type": "object",
"required": [
"query",
"count"
],
"properties": {
"count": {
"type": "integer",
"format": "int64"
},
"query": {
"type": "string"
}
}
},
"FullTextBoost": {
"type": "object",
"description": "Boost the presence of certain tokens for fulltext (SPLADE) and keyword (BM25) search. I.e. boosting title phrases to priortize title matches or making sure that the listing for AirBNB itself ranks higher than companies who make software for AirBNB hosts by boosting the in-document-frequency of the AirBNB token (AKA word) for its official listing. Conceptually it multiples the in-document-importance second value in the tuples of the SPLADE or BM25 sparse vector of the chunk_html innerText for all tokens present in the boost phrase by the boost factor like so: (token, in-document-importance) -> (token, in-document-importance*boost_factor).",
Expand Down Expand Up @@ -14947,6 +14977,35 @@
}
}
},
{
"type": "object",
"title": "FollowupQueries",
"required": [
"type"
],
"properties": {
"filter": {
"allOf": [
{
"$ref": "#/components/schemas/RAGAnalyticsFilter"
}
],
"nullable": true
},
"page": {
"type": "integer",
"format": "int32",
"nullable": true,
"minimum": 0
},
"type": {
"type": "string",
"enum": [
"followup_queries"
]
}
}
},
{
"type": "object",
"title": "TopicAnalytics",
Expand Down Expand Up @@ -15334,6 +15393,9 @@
{
"$ref": "#/components/schemas/RagQueryRatingsResponse"
},
{
"$ref": "#/components/schemas/FollowupQueriesResponse"
},
{
"$ref": "#/components/schemas/TopicQueriesResponse"
},
Expand Down
19 changes: 16 additions & 3 deletions clients/ts-sdk/src/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1390,11 +1390,11 @@ export type EventData = {
*/
dataset_id: string;
/**
* The name of the event, e.g. "Added to Cart", "Purchased", "Viewed Home Page", "Clicked", "Filter Clicked".
* The name of the event, e.g. "Added to Cart", "Purchased", "Viewed Home Page", "Clicked", "Filter Clicked", "Followup Query".
*/
event_name: string;
/**
* The type of event, "add_to_cart", "purchase", "view", "click", "filter_clicked".
* The type of event, "add_to_cart", "purchase", "view", "click", "filter_clicked", "followup_query"
*/
event_type: string;
/**
Expand Down Expand Up @@ -1806,6 +1806,15 @@ export type FloatTimePoint = {
time_stamp: string;
};

export type FollowupQueriesResponse = {
top_queries: Array<FollowupQuery>;
};

export type FollowupQuery = {
count: number;
query: string;
};

/**
* Boost the presence of certain tokens for fulltext (SPLADE) and keyword (BM25) search. I.e. boosting title phrases to priortize title matches or making sure that the listing for AirBNB itself ranks higher than companies who make software for AirBNB hosts by boosting the in-document-frequency of the AirBNB token (AKA word) for its official listing. Conceptually it multiples the in-document-importance second value in the tuples of the SPLADE or BM25 sparse vector of the chunk_html innerText for all tokens present in the boost phrase by the boost factor like so: (token, in-document-importance) -> (token, in-document-importance*boost_factor).
*/
Expand Down Expand Up @@ -2676,6 +2685,10 @@ export type RAGAnalytics = {
} | {
filter?: ((RAGAnalyticsFilter) | null);
type: 'rag_query_ratings';
} | {
filter?: ((RAGAnalyticsFilter) | null);
page?: (number) | null;
type: 'followup_queries';
} | {
filter?: ((TopicAnalyticsFilter) | null);
has_clicks?: (boolean) | null;
Expand Down Expand Up @@ -2733,7 +2746,7 @@ export type RAGAnalyticsFilter = {
rag_type?: ((RagTypes) | null);
};

export type RAGAnalyticsResponse = RagQueryResponse | RAGUsageResponse | RAGUsageGraphResponse | RagQueryEvent | RagQueryRatingsResponse | TopicQueriesResponse | TopicDetailsResponse | TopicsOverTimeResponse | CTRMetricsOverTimeResponse | MessagesPerUserResponse | ChatAverageRatingResponse | ChatConversionRateResponse | EventNameAndCountsResponse | EventsForTopicResponse | ChatRevenueResponse | PopularChatsResponse;
export type RAGAnalyticsResponse = RagQueryResponse | RAGUsageResponse | RAGUsageGraphResponse | RagQueryEvent | RagQueryRatingsResponse | FollowupQueriesResponse | TopicQueriesResponse | TopicDetailsResponse | TopicsOverTimeResponse | CTRMetricsOverTimeResponse | MessagesPerUserResponse | ChatAverageRatingResponse | ChatConversionRateResponse | EventNameAndCountsResponse | EventsForTopicResponse | ChatRevenueResponse | PopularChatsResponse;

export type RAGSortBy = 'hallucination_score' | 'top_score' | 'created_at';

Expand Down
Loading