@brainfish-ai/components 0.16.10 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/convos.d.ts +62 -7
- package/dist/esm/components/convos.js +269 -29
- package/dist/esm/components/convos.js.map +1 -1
- package/dist/esm/components/ui/dialog.js +4 -3
- package/dist/esm/components/ui/dialog.js.map +1 -1
- package/dist/esm/convos.css +1 -1
- package/dist/esm/index.css +1 -1
- package/dist/stats.html +1 -1
- package/package.json +1 -1
package/dist/convos.d.ts
CHANGED
|
@@ -6,6 +6,17 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
6
6
|
|
|
7
7
|
declare function AccordionItem({ className, ...props }: React_2.ComponentProps<typeof AccordionPrimitive.Item>): React_2.JSX.Element;
|
|
8
8
|
|
|
9
|
+
declare type AnswerDiagnostics = {
|
|
10
|
+
confidence_score: string;
|
|
11
|
+
answer_generation_type?: AnswerGenerationType;
|
|
12
|
+
query_context_relevance?: QueryContextRelevance;
|
|
13
|
+
context_sufficiency?: ContextSufficiency;
|
|
14
|
+
confidence_justification?: string;
|
|
15
|
+
evidence_from_context?: EvidenceFromContext[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
declare type AnswerGenerationType = 'Direct Extraction' | 'Summarization' | 'Inference' | 'Combination' | 'Cannot Answer';
|
|
19
|
+
|
|
9
20
|
declare function Badge({ className, variant, ...props }: BadgeProps): React_2.JSX.Element;
|
|
10
21
|
|
|
11
22
|
declare interface BadgeProps extends React_2.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
@@ -15,6 +26,8 @@ declare const badgeVariants: (props?: ({
|
|
|
15
26
|
variant?: "default" | "destructive" | "success" | "warning" | null | undefined;
|
|
16
27
|
} & ClassProp) | undefined) => string;
|
|
17
28
|
|
|
29
|
+
declare type ContextSufficiency = 'Sufficient' | 'Partially Sufficient' | 'Insufficient';
|
|
30
|
+
|
|
18
31
|
/**
|
|
19
32
|
* Convo component - A single conversation item in an accordion.
|
|
20
33
|
* Displays conversation metadata and messages, with support for lazy loading.
|
|
@@ -22,7 +35,7 @@ declare const badgeVariants: (props?: ({
|
|
|
22
35
|
* This is a presentational component - it receives messages and loading state as props
|
|
23
36
|
* rather than managing fetching logic itself.
|
|
24
37
|
*/
|
|
25
|
-
export declare function Convo({ locale, value: queryId, question, timestamp, location, source, status, statusMessage, hasAttributes,
|
|
38
|
+
export declare function Convo({ locale: localeOverride, value: queryId, question, timestamp, location, source, status, statusMessage, hasAttributes, onClick, className, ...props }: ConvoProps): default_2.JSX.Element;
|
|
26
39
|
|
|
27
40
|
declare interface ConvoInput {
|
|
28
41
|
value: string;
|
|
@@ -33,7 +46,7 @@ declare interface ConvoInput {
|
|
|
33
46
|
status?: default_2.ComponentProps<typeof StatusBadge>['variant'];
|
|
34
47
|
statusMessage?: string;
|
|
35
48
|
hasAttributes?: boolean;
|
|
36
|
-
attributes?: Record<string, any
|
|
49
|
+
attributes?: Record<string, any>[];
|
|
37
50
|
locale?: string;
|
|
38
51
|
}
|
|
39
52
|
|
|
@@ -46,11 +59,47 @@ export declare type ConvoProps = default_2.ComponentPropsWithRef<typeof Accordio
|
|
|
46
59
|
* @param convos - Array of conversation data
|
|
47
60
|
* @param fetchMessages - Async function to fetch messages for a conversation
|
|
48
61
|
*/
|
|
49
|
-
export declare function Convos({ convos, fetchMessages }: ConvosProps): default_2.JSX.Element;
|
|
62
|
+
export declare function Convos({ convos, fetchMessages, fetchAttributes, fetchAnswerDiagnostics, onDiscard, onApprove, }: ConvosProps): default_2.JSX.Element;
|
|
63
|
+
|
|
64
|
+
declare interface ConvosContextValue {
|
|
65
|
+
fetchMessages: (conversationId: string) => Promise<MessageProps[]>;
|
|
66
|
+
fetchAttributes: (conversationId: string) => Promise<Record<string, any>[]>;
|
|
67
|
+
getMessages: (conversationId: string) => MessageProps[];
|
|
68
|
+
isLoading: (conversationId: string) => boolean;
|
|
69
|
+
handleFetchMessages: (conversationId: string) => void;
|
|
70
|
+
locale?: string;
|
|
71
|
+
fetchAnswerDiagnostics: (messageId: string) => Promise<AnswerDiagnostics>;
|
|
72
|
+
onDiscard: (id: string, reason: DiscardReason, suggestedAnswer?: string) => Promise<void>;
|
|
73
|
+
onApprove: (id: string) => Promise<void>;
|
|
74
|
+
}
|
|
50
75
|
|
|
51
76
|
declare interface ConvosProps {
|
|
52
77
|
convos: ConvoInput[];
|
|
53
78
|
fetchMessages: (conversationId: string) => Promise<MessageProps[]>;
|
|
79
|
+
fetchAttributes: (conversationId: string) => Promise<Record<string, any>[]>;
|
|
80
|
+
fetchAnswerDiagnostics: (messageId: string) => Promise<AnswerDiagnostics>;
|
|
81
|
+
onDiscard: (id: string, reason: DiscardReason, suggestedAnswer?: string) => Promise<void>;
|
|
82
|
+
onApprove: (id: string) => Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export declare function ConvosProvider({ children, value }: ConvosProviderProps): default_2.JSX.Element;
|
|
86
|
+
|
|
87
|
+
declare interface ConvosProviderProps {
|
|
88
|
+
children: default_2.ReactNode;
|
|
89
|
+
value: ConvosContextValue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
declare enum DiscardReason {
|
|
93
|
+
INCORRECT_ANSWER = "incorrect_answer",
|
|
94
|
+
HALLUCINATED_ANSWER = "hallucinated_answer",
|
|
95
|
+
OVERSHARING = "oversharing",
|
|
96
|
+
WRONG_ARTICLE_SURFACED = "wrong_article_surfaced",
|
|
97
|
+
OTHER = "other"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
declare interface EvidenceFromContext {
|
|
101
|
+
supporting_quote?: string;
|
|
102
|
+
url?: string;
|
|
54
103
|
}
|
|
55
104
|
|
|
56
105
|
export declare enum Feedback {
|
|
@@ -58,7 +107,7 @@ export declare enum Feedback {
|
|
|
58
107
|
Negative = "negative"
|
|
59
108
|
}
|
|
60
109
|
|
|
61
|
-
export declare function Message({ type, role, content, relatedArticles, feedback, timestamp, locale }: MessageProps): default_2.JSX.Element;
|
|
110
|
+
export declare function Message({ id, type, role, content, relatedArticles, feedback, timestamp, locale: localeOverride, discarded, discardedMeta, }: MessageProps): default_2.JSX.Element;
|
|
62
111
|
|
|
63
112
|
export declare interface MessageProps {
|
|
64
113
|
id: string;
|
|
@@ -69,6 +118,11 @@ export declare interface MessageProps {
|
|
|
69
118
|
feedback?: Feedback;
|
|
70
119
|
type?: MessageType;
|
|
71
120
|
locale?: string;
|
|
121
|
+
discarded?: boolean;
|
|
122
|
+
discardedMeta?: {
|
|
123
|
+
reason: DiscardReason;
|
|
124
|
+
suggestedAnswer?: string;
|
|
125
|
+
};
|
|
72
126
|
}
|
|
73
127
|
|
|
74
128
|
export declare enum MessageType {
|
|
@@ -89,12 +143,11 @@ declare interface Props_2 {
|
|
|
89
143
|
status?: default_2.ComponentProps<typeof StatusBadge>['variant'];
|
|
90
144
|
statusMessage?: string;
|
|
91
145
|
hasAttributes?: boolean;
|
|
92
|
-
attributes?: Record<string, any>;
|
|
93
|
-
messages?: MessageProps[];
|
|
94
|
-
isLoading?: boolean;
|
|
95
146
|
onClick?: () => void;
|
|
96
147
|
}
|
|
97
148
|
|
|
149
|
+
declare type QueryContextRelevance = 'High' | 'Medium' | 'Low';
|
|
150
|
+
|
|
98
151
|
export declare interface RelatedArticle {
|
|
99
152
|
id: string;
|
|
100
153
|
title: string;
|
|
@@ -104,4 +157,6 @@ export declare interface RelatedArticle {
|
|
|
104
157
|
|
|
105
158
|
export declare function StatusBadge({ ...props }: Props): default_2.JSX.Element;
|
|
106
159
|
|
|
160
|
+
export declare function useConvosContext(): ConvosContextValue;
|
|
161
|
+
|
|
107
162
|
export { }
|
|
@@ -1,16 +1,156 @@
|
|
|
1
|
-
import React__default from 'react';
|
|
2
|
-
import { Circle, ArrowSquareOut, UserCircle, UserFocus } from '@phosphor-icons/react';
|
|
1
|
+
import React__default, { useState, useEffect } from 'react';
|
|
2
|
+
import { Circle, ArrowSquareOut, ThumbsDown, ThumbsUp, UserCircle, UserFocus } from '@phosphor-icons/react';
|
|
3
3
|
import { Badge } from './ui/badge.js';
|
|
4
4
|
import { F as FormattedMessage } from '../chunks/FormattedMessage.BcAkzCZt.js';
|
|
5
|
+
import { useForm, Controller } from 'react-hook-form';
|
|
6
|
+
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogClose } from './ui/dialog.js';
|
|
7
|
+
import { Button } from './ui/button.js';
|
|
8
|
+
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './ui/select.js';
|
|
9
|
+
import { Label } from './ui/label.js';
|
|
10
|
+
import { ButtonGroup } from '../componentns/ui/button-group.js';
|
|
11
|
+
import { Textarea } from './ui/textarea.js';
|
|
12
|
+
import { ScrollArea } from './ui/scroll-area.js';
|
|
13
|
+
import { Spinner } from './ui/spinner.js';
|
|
14
|
+
import { c as cn } from '../chunks/utils.Cwtlq8dh.js';
|
|
5
15
|
import { G as GeneratingStar } from '../chunks/generating-star.COkD0gHd.js';
|
|
6
16
|
import { Item, ItemHeader, ItemTitle, ItemContent, ItemDescription } from './ui/item.js';
|
|
7
17
|
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './ui/accordion.js';
|
|
8
|
-
import {
|
|
18
|
+
import { Table, TableBody, TableRow, TableCell } from './ui/table.js';
|
|
9
19
|
|
|
10
20
|
import '../convos.css';function StatusBadge({ ...props }) {
|
|
11
21
|
return /* @__PURE__ */ React__default.createElement(Badge, { ...props }, /* @__PURE__ */ React__default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React__default.createElement(Circle, { weight: "fill", size: 8, className: "size-2" }), props.children));
|
|
12
22
|
}
|
|
13
23
|
|
|
24
|
+
function AnswerDiagnosticsSection({ messageId }) {
|
|
25
|
+
const { fetchAnswerDiagnostics } = useConvosContext();
|
|
26
|
+
const [answerDiagnostics, setAnswerDiagnostics] = useState(null);
|
|
27
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
void fetchAnswerDiagnostics(messageId).then((diagnostics) => setAnswerDiagnostics(diagnostics)).finally(() => setIsLoading(false));
|
|
30
|
+
}, [fetchAnswerDiagnostics, messageId]);
|
|
31
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
32
|
+
ScrollArea,
|
|
33
|
+
{
|
|
34
|
+
type: "always",
|
|
35
|
+
className: "h-[440px] border border-dark-300 rounded-lg",
|
|
36
|
+
"data-name": "discard-dialog-answer-diagnostic"
|
|
37
|
+
},
|
|
38
|
+
/* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col gap-4 p-4 flex-shrink-0 bg-surface" }, /* @__PURE__ */ React__default.createElement("h3", { className: "text-lg font-medium my-0" }, "Answer diagnostic"), isLoading ? /* @__PURE__ */ React__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React__default.createElement(Spinner, null)) : /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, answerDiagnostics && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement("dl", { className: "grid grid-cols-[1fr_2fr] grid-rows-2 gap-5" }, /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React__default.createElement("dt", { className: "font-medium" }, "Confidence"), /* @__PURE__ */ React__default.createElement("dd", { className: "text-xs" }, answerDiagnostics.confidence_score)), answerDiagnostics.answer_generation_type && /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React__default.createElement("dt", { className: "font-medium" }, "Answer Type"), /* @__PURE__ */ React__default.createElement("dd", { className: "text-xs" }, answerDiagnostics.answer_generation_type)), answerDiagnostics.query_context_relevance && /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React__default.createElement("dt", { className: "font-medium" }, "Relevance"), /* @__PURE__ */ React__default.createElement("dd", { className: "text-xs" }, answerDiagnostics.query_context_relevance)), answerDiagnostics.context_sufficiency && /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React__default.createElement("dt", { className: "font-medium" }, "Context"), /* @__PURE__ */ React__default.createElement("dd", { className: "text-xs" }, answerDiagnostics.context_sufficiency))), answerDiagnostics.confidence_justification && /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col gap-2", "data-name": "diagnostic-justification" }, /* @__PURE__ */ React__default.createElement("span", { className: "font-medium" }, "Justification"), /* @__PURE__ */ React__default.createElement("p", { className: "text-sm my-0" }, answerDiagnostics.confidence_justification)), answerDiagnostics.evidence_from_context && answerDiagnostics.evidence_from_context.length > 0 && /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col gap-2", "data-name": "diagnostic-evidence" }, /* @__PURE__ */ React__default.createElement("span", { className: "font-medium" }, "Evidence (from Knowledge Base)"), answerDiagnostics.evidence_from_context.map((evidence, index) => /* @__PURE__ */ React__default.createElement("div", { key: index, className: "flex flex-col gap-1" }, evidence.supporting_quote && /* @__PURE__ */ React__default.createElement("p", { className: "text-sm my-0" }, evidence.supporting_quote), evidence.url && /* @__PURE__ */ React__default.createElement(
|
|
39
|
+
"a",
|
|
40
|
+
{
|
|
41
|
+
href: evidence.url,
|
|
42
|
+
target: "_blank",
|
|
43
|
+
rel: "noopener noreferrer",
|
|
44
|
+
className: "flex gap-1 text-sm self-end items-center"
|
|
45
|
+
},
|
|
46
|
+
/* @__PURE__ */ React__default.createElement("span", null, "Source"),
|
|
47
|
+
/* @__PURE__ */ React__default.createElement(ArrowSquareOut, { size: 16 })
|
|
48
|
+
)))))))
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function DiscardDialog({
|
|
53
|
+
id,
|
|
54
|
+
discarded,
|
|
55
|
+
discardedMeta,
|
|
56
|
+
className,
|
|
57
|
+
open: externalOpen,
|
|
58
|
+
onOpenChange: externalOnOpenChange,
|
|
59
|
+
...args
|
|
60
|
+
}) {
|
|
61
|
+
const { onDiscard, onApprove } = useConvosContext();
|
|
62
|
+
const [isSelectOpen, setIsSelectOpen] = useState(false);
|
|
63
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
64
|
+
const isOpen = externalOpen ?? internalOpen;
|
|
65
|
+
const setIsOpen = externalOnOpenChange ?? setInternalOpen;
|
|
66
|
+
const {
|
|
67
|
+
control,
|
|
68
|
+
register,
|
|
69
|
+
handleSubmit,
|
|
70
|
+
reset,
|
|
71
|
+
formState: { isSubmitting }
|
|
72
|
+
} = useForm({
|
|
73
|
+
defaultValues: {
|
|
74
|
+
reason: discardedMeta?.reason,
|
|
75
|
+
preferredAnswer: discardedMeta?.suggestedAnswer || ""
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
reset({
|
|
80
|
+
reason: discardedMeta?.reason,
|
|
81
|
+
preferredAnswer: discardedMeta?.suggestedAnswer || ""
|
|
82
|
+
});
|
|
83
|
+
}, [discardedMeta, reset]);
|
|
84
|
+
const onSubmitForm = async (data) => {
|
|
85
|
+
await onDiscard(id, data.reason, data.preferredAnswer).then(() => setIsOpen(false));
|
|
86
|
+
};
|
|
87
|
+
return /* @__PURE__ */ React__default.createElement(ButtonGroup, { className }, /* @__PURE__ */ React__default.createElement(Dialog, { ...args, open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React__default.createElement(DialogTrigger, { asChild: true }, /* @__PURE__ */ React__default.createElement(
|
|
88
|
+
Button,
|
|
89
|
+
{
|
|
90
|
+
variant: "shadow",
|
|
91
|
+
className: cn(
|
|
92
|
+
"size-7",
|
|
93
|
+
discarded ? "bg-destructive text-destructive !opacity-100" : "bg-surface text-dark-800"
|
|
94
|
+
),
|
|
95
|
+
size: "icon"
|
|
96
|
+
},
|
|
97
|
+
/* @__PURE__ */ React__default.createElement(ThumbsDown, { weight: discarded ? "fill" : "regular" })
|
|
98
|
+
)), /* @__PURE__ */ React__default.createElement(
|
|
99
|
+
DialogContent,
|
|
100
|
+
{
|
|
101
|
+
className: "min-w-[826px] bg-dark-100 pt-0 pb-4",
|
|
102
|
+
onEscapeKeyDown: (e) => {
|
|
103
|
+
if (isSelectOpen) {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
setIsSelectOpen(false);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
/* @__PURE__ */ React__default.createElement(DialogHeader, { className: "py-6" }, /* @__PURE__ */ React__default.createElement(DialogTitle, { className: "heading-xxl !text-2xl" }, "Answer feedback")),
|
|
110
|
+
/* @__PURE__ */ React__default.createElement("form", { onSubmit: handleSubmit(onSubmitForm) }, /* @__PURE__ */ React__default.createElement("div", { className: "grid grid-cols-[1fr_2fr] gap-6 pb-4" }, /* @__PURE__ */ React__default.createElement("div", { className: "w-[340px] p-4", "data-name": "discard-dialog-reason" }, /* @__PURE__ */ React__default.createElement("fieldset", { className: "space-y-4" }, /* @__PURE__ */ React__default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React__default.createElement(Label, { htmlFor: "reason", className: "text-sm" }, "Reason"), /* @__PURE__ */ React__default.createElement(
|
|
111
|
+
Controller,
|
|
112
|
+
{
|
|
113
|
+
control,
|
|
114
|
+
name: "reason",
|
|
115
|
+
rules: { required: true },
|
|
116
|
+
render: ({ field }) => /* @__PURE__ */ React__default.createElement(
|
|
117
|
+
Select,
|
|
118
|
+
{
|
|
119
|
+
value: field.value,
|
|
120
|
+
onValueChange: field.onChange,
|
|
121
|
+
open: isSelectOpen,
|
|
122
|
+
onOpenChange: setIsSelectOpen,
|
|
123
|
+
required: true
|
|
124
|
+
},
|
|
125
|
+
/* @__PURE__ */ React__default.createElement(SelectTrigger, { className: "border-dark-300 bg-surface" }, /* @__PURE__ */ React__default.createElement(SelectValue, { placeholder: "Select a reason" })),
|
|
126
|
+
/* @__PURE__ */ React__default.createElement(SelectContent, null, /* @__PURE__ */ React__default.createElement(SelectItem, { value: "incorrect_answer" /* INCORRECT_ANSWER */ }, "Incorrect answer"), /* @__PURE__ */ React__default.createElement(SelectItem, { value: "hallucinated_answer" /* HALLUCINATED_ANSWER */ }, "Hallucinated answer"), /* @__PURE__ */ React__default.createElement(SelectItem, { value: "oversharing" /* OVERSHARING */ }, "Oversharing"), /* @__PURE__ */ React__default.createElement(SelectItem, { value: "wrong_article_surfaced" /* WRONG_ARTICLE_SURFACED */ }, "Wrong article surfaced"), /* @__PURE__ */ React__default.createElement(SelectItem, { value: "other" /* OTHER */ }, "Other"))
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
)), /* @__PURE__ */ React__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React__default.createElement(Label, { htmlFor: "preferredAnswer", className: "text-base font-normal" }, "Preferred answer"), /* @__PURE__ */ React__default.createElement(
|
|
130
|
+
Textarea,
|
|
131
|
+
{
|
|
132
|
+
id: "preferredAnswer",
|
|
133
|
+
required: true,
|
|
134
|
+
minLength: 10,
|
|
135
|
+
placeholder: "Explain what you'd like Brainfish to do next time in this case....",
|
|
136
|
+
rows: 4,
|
|
137
|
+
className: "h-52 resize-none bg-surface",
|
|
138
|
+
...register("preferredAnswer")
|
|
139
|
+
}
|
|
140
|
+
), /* @__PURE__ */ React__default.createElement("span", { className: "text-xs text-dark-1000 hidden ml-1" }, 'Tell Brainfish why this is the incorrect answer. You can use "@" to reference the right Knowledge Article.')))), /* @__PURE__ */ React__default.createElement(AnswerDiagnosticsSection, { messageId: id })), /* @__PURE__ */ React__default.createElement(DialogFooter, { className: "py-6" }, /* @__PURE__ */ React__default.createElement(DialogClose, { asChild: true }, /* @__PURE__ */ React__default.createElement(Button, { variant: "link", size: "sm", type: "button" }, "Cancel")), /* @__PURE__ */ React__default.createElement(Button, { type: "submit", variant: "shadow", size: "sm", disabled: isSubmitting }, isSubmitting ? "Submitting..." : "Submit")))
|
|
141
|
+
)), /* @__PURE__ */ React__default.createElement(
|
|
142
|
+
Button,
|
|
143
|
+
{
|
|
144
|
+
variant: "shadow",
|
|
145
|
+
className: cn("bg-surface size-7 text-dark-800", { "!opacity-100 bg-dark-300": discarded }),
|
|
146
|
+
size: "icon",
|
|
147
|
+
disabled: discarded,
|
|
148
|
+
onClick: () => onApprove(id)
|
|
149
|
+
},
|
|
150
|
+
/* @__PURE__ */ React__default.createElement(ThumbsUp, null)
|
|
151
|
+
));
|
|
152
|
+
}
|
|
153
|
+
|
|
14
154
|
function formatDate(date, locale) {
|
|
15
155
|
const defaultLocale = "en-US";
|
|
16
156
|
if (!locale) {
|
|
@@ -42,7 +182,20 @@ var Feedback = /* @__PURE__ */ ((Feedback2) => {
|
|
|
42
182
|
Feedback2["Negative"] = "negative";
|
|
43
183
|
return Feedback2;
|
|
44
184
|
})(Feedback || {});
|
|
45
|
-
function Message({
|
|
185
|
+
function Message({
|
|
186
|
+
id,
|
|
187
|
+
type,
|
|
188
|
+
role,
|
|
189
|
+
content,
|
|
190
|
+
relatedArticles,
|
|
191
|
+
feedback,
|
|
192
|
+
timestamp,
|
|
193
|
+
locale: localeOverride,
|
|
194
|
+
discarded,
|
|
195
|
+
discardedMeta
|
|
196
|
+
}) {
|
|
197
|
+
const { locale: contextLocale } = useConvosContext();
|
|
198
|
+
const locale = localeOverride || contextLocale;
|
|
46
199
|
const formattedDate = formatDate(timestamp, locale);
|
|
47
200
|
if (role === "ai") {
|
|
48
201
|
return /* @__PURE__ */ React__default.createElement("li", { className: "relative before-line after-line" }, /* @__PURE__ */ React__default.createElement(Item, { className: "message-item" }, /* @__PURE__ */ React__default.createElement(ItemHeader, { className: "justify-start gap-0" }, /* @__PURE__ */ React__default.createElement(GeneratingStar, { variant: "gradient", className: "relative -left-[6px]" }), /* @__PURE__ */ React__default.createElement(ItemTitle, { className: "text-xs font-bold" }, "Generated answer")), /* @__PURE__ */ React__default.createElement(
|
|
@@ -53,6 +206,15 @@ function Message({ type, role, content, relatedArticles, feedback, timestamp, lo
|
|
|
53
206
|
"pt-6 pb-0": relatedArticles?.length
|
|
54
207
|
})
|
|
55
208
|
},
|
|
209
|
+
/* @__PURE__ */ React__default.createElement(
|
|
210
|
+
DiscardDialog,
|
|
211
|
+
{
|
|
212
|
+
id,
|
|
213
|
+
discarded,
|
|
214
|
+
discardedMeta,
|
|
215
|
+
className: "absolute -top-4 right-5"
|
|
216
|
+
}
|
|
217
|
+
),
|
|
56
218
|
/* @__PURE__ */ React__default.createElement("div", { className: "message-content-wrapper" }, /* @__PURE__ */ React__default.createElement(FormattedMessage, { message: { content } }), /* @__PURE__ */ React__default.createElement("span", { className: "message-timestamp float-right mt-2" }, formattedDate)),
|
|
57
219
|
!!relatedArticles?.length && /* @__PURE__ */ React__default.createElement(Accordion, { type: "single", collapsible: true, className: "p-0 custom-dashed-border-t-dark-300" }, /* @__PURE__ */ React__default.createElement(AccordionItem, { value: "related-articles" }, /* @__PURE__ */ React__default.createElement(AccordionTrigger, { className: "px-6" }, "Related articles"), /* @__PURE__ */ React__default.createElement(AccordionContent, null, /* @__PURE__ */ React__default.createElement("ul", { className: "message-article-list" }, relatedArticles.map((article) => /* @__PURE__ */ React__default.createElement("li", { key: article.id, className: "message-meta" }, /* @__PURE__ */ React__default.createElement(Badge, { variant: "warning" }, Math.round(article.score * 100), "%"), /* @__PURE__ */ React__default.createElement(
|
|
58
220
|
"a",
|
|
@@ -70,8 +232,51 @@ function Message({ type, role, content, relatedArticles, feedback, timestamp, lo
|
|
|
70
232
|
return /* @__PURE__ */ React__default.createElement("li", null, /* @__PURE__ */ React__default.createElement(Item, { className: "message-item" }, /* @__PURE__ */ React__default.createElement(ItemHeader, { className: "basis-0" }, /* @__PURE__ */ React__default.createElement(UserCircle, { weight: "fill", size: 24 })), /* @__PURE__ */ React__default.createElement(ItemContent, { className: "message-user-content" }, type === "question" /* QUESTION */ && /* @__PURE__ */ React__default.createElement("div", { className: "message-header" }, /* @__PURE__ */ React__default.createElement(FormattedMessage, { message: { content } }), /* @__PURE__ */ React__default.createElement("span", { className: "message-timestamp" }, formattedDate)), type === "feedback" /* FEEDBACK */ && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement("div", { className: "message-header" }, /* @__PURE__ */ React__default.createElement("div", { className: "message-feedback-text" }, feedback === "positive" /* Positive */ ? "Upvoted as helpful" : "Downvoted as not helpful"), /* @__PURE__ */ React__default.createElement("div", { className: "message-meta" }, /* @__PURE__ */ React__default.createElement("span", { className: "message-timestamp-simple" }, formattedDate), /* @__PURE__ */ React__default.createElement(StatusBadge, { variant: feedback === "positive" /* Positive */ ? "success" : "destructive" }, feedback === "positive" /* Positive */ ? "Answered" : "Unable to help"))), content && /* @__PURE__ */ React__default.createElement("div", { className: "text-base" }, content)), type === "action" /* ACTION */ && /* @__PURE__ */ React__default.createElement("div", { className: "message-header" }, /* @__PURE__ */ React__default.createElement("span", { className: "text-subtle text-sm italic" }, content), /* @__PURE__ */ React__default.createElement("div", { className: "message-meta" }, /* @__PURE__ */ React__default.createElement("span", { className: "message-timestamp-simple" }, formattedDate), /* @__PURE__ */ React__default.createElement(StatusBadge, { variant: "warning" }, "Action"))))));
|
|
71
233
|
}
|
|
72
234
|
|
|
235
|
+
function groupAttributesByKey(attributes) {
|
|
236
|
+
return attributes.reduce(
|
|
237
|
+
(acc, attr) => {
|
|
238
|
+
Object.entries(attr).forEach(([key, value]) => {
|
|
239
|
+
if (!acc[key]) {
|
|
240
|
+
acc[key] = [];
|
|
241
|
+
}
|
|
242
|
+
acc[key].push(value);
|
|
243
|
+
});
|
|
244
|
+
return acc;
|
|
245
|
+
},
|
|
246
|
+
{}
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function AttributesDialog({ conversationId, ...args }) {
|
|
251
|
+
const [attributes, setAttributes] = useState();
|
|
252
|
+
const { fetchAttributes } = useConvosContext();
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
void fetchAttributes(conversationId).then((attrs) => setAttributes(attrs)).catch(() => {
|
|
255
|
+
});
|
|
256
|
+
}, [fetchAttributes, conversationId]);
|
|
257
|
+
if (!attributes?.length) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
const groupedAttributes = groupAttributesByKey(attributes);
|
|
261
|
+
return /* @__PURE__ */ React__default.createElement(Dialog, { ...args }, /* @__PURE__ */ React__default.createElement(DialogTrigger, { asChild: true }, /* @__PURE__ */ React__default.createElement(
|
|
262
|
+
Button,
|
|
263
|
+
{
|
|
264
|
+
variant: "ghost",
|
|
265
|
+
className: cn("size-7"),
|
|
266
|
+
size: "icon",
|
|
267
|
+
onClick: (e) => {
|
|
268
|
+
e.stopPropagation();
|
|
269
|
+
},
|
|
270
|
+
onKeyUp: (e) => {
|
|
271
|
+
e.stopPropagation();
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
/* @__PURE__ */ React__default.createElement(UserFocus, { size: 16 })
|
|
275
|
+
)), /* @__PURE__ */ React__default.createElement(DialogContent, { className: "min-w-[826px] bg-dark-100 pt-0 pb-4" }, /* @__PURE__ */ React__default.createElement(DialogHeader, { className: "py-6" }, /* @__PURE__ */ React__default.createElement(DialogTitle, { className: "heading-xxl !text-2xl" }, "Custom attributes")), /* @__PURE__ */ React__default.createElement("div", { className: "pb-4" }, /* @__PURE__ */ React__default.createElement(Table, { className: "bg-surface" }, /* @__PURE__ */ React__default.createElement(TableBody, { className: "space-y-1" }, Object.entries(groupedAttributes).map(([key, values]) => /* @__PURE__ */ React__default.createElement(TableRow, { key, className: "border-none" }, /* @__PURE__ */ React__default.createElement(TableCell, { className: "capitalize font-bold" }, key), values.map((value, index) => /* @__PURE__ */ React__default.createElement(TableCell, { key: `${key}-${index}` }, value)))))))));
|
|
276
|
+
}
|
|
277
|
+
|
|
73
278
|
function Convo({
|
|
74
|
-
locale,
|
|
279
|
+
locale: localeOverride,
|
|
75
280
|
value: queryId,
|
|
76
281
|
question,
|
|
77
282
|
timestamp,
|
|
@@ -80,33 +285,46 @@ function Convo({
|
|
|
80
285
|
status,
|
|
81
286
|
statusMessage,
|
|
82
287
|
hasAttributes,
|
|
83
|
-
attributes,
|
|
84
|
-
messages = [],
|
|
85
|
-
isLoading = false,
|
|
86
288
|
onClick,
|
|
87
289
|
className,
|
|
88
290
|
...props
|
|
89
291
|
}) {
|
|
292
|
+
const { getMessages, isLoading: isLoadingFromContext, locale: contextLocale } = useConvosContext();
|
|
293
|
+
const locale = localeOverride || contextLocale;
|
|
294
|
+
const messages = getMessages(queryId);
|
|
295
|
+
const isLoading = isLoadingFromContext(queryId);
|
|
90
296
|
const formattedDate = formatDate(timestamp, locale);
|
|
91
297
|
const description = [formattedDate, location, source].filter(Boolean).join(" • ");
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
e.stopPropagation();
|
|
98
|
-
console.log("display custom attributes", attributes);
|
|
99
|
-
},
|
|
100
|
-
onKeyUp: (e) => {
|
|
101
|
-
e.stopPropagation();
|
|
102
|
-
console.log("display custom attributes", attributes);
|
|
103
|
-
},
|
|
104
|
-
tabIndex: 0
|
|
298
|
+
const handleAccordionClick = (e) => {
|
|
299
|
+
if (e.target.closest('[role="dialog"]')) {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
e.stopPropagation();
|
|
302
|
+
return;
|
|
105
303
|
}
|
|
106
|
-
|
|
304
|
+
onClick?.();
|
|
305
|
+
};
|
|
306
|
+
return /* @__PURE__ */ React__default.createElement(AccordionItem, { value: queryId, className: cn("border-none rounded-lg bg-surface", className), ...props }, /* @__PURE__ */ React__default.createElement(AccordionTrigger, { className: "flex-row-reverse px-4 gap-1 items-center", onClick: handleAccordionClick }, /* @__PURE__ */ React__default.createElement(Item, { size: "sm", className: "flex-1 p-0" }, /* @__PURE__ */ React__default.createElement(ItemContent, null, /* @__PURE__ */ React__default.createElement(ItemTitle, { className: "text-base text-default" }, question), /* @__PURE__ */ React__default.createElement(ItemDescription, { className: "text-xs text-default" }, description)), /* @__PURE__ */ React__default.createElement(StatusBadge, { variant: status }, statusMessage), hasAttributes && /* @__PURE__ */ React__default.createElement(AttributesDialog, { conversationId: queryId }))), /* @__PURE__ */ React__default.createElement(AccordionContent, { className: "flex flex-col gap-4 text-balance px-3" }, isLoading ? /* @__PURE__ */ React__default.createElement("div", { className: "text-center text-subtle py-4" }, "Loading messages...") : /* @__PURE__ */ React__default.createElement("ul", { className: "list-none space-y-4" }, messages.slice(1).map((message) => /* @__PURE__ */ React__default.createElement(Message, { key: message.id, ...message })))));
|
|
107
307
|
}
|
|
108
308
|
|
|
109
|
-
|
|
309
|
+
const ConvosContext = React__default.createContext(void 0);
|
|
310
|
+
function useConvosContext() {
|
|
311
|
+
const context = React__default.useContext(ConvosContext);
|
|
312
|
+
if (context === void 0) {
|
|
313
|
+
throw new Error("useConvosContext must be used within a ConvosProvider");
|
|
314
|
+
}
|
|
315
|
+
return context;
|
|
316
|
+
}
|
|
317
|
+
function ConvosProvider({ children, value }) {
|
|
318
|
+
return /* @__PURE__ */ React__default.createElement(ConvosContext.Provider, { value }, children);
|
|
319
|
+
}
|
|
320
|
+
function Convos({
|
|
321
|
+
convos,
|
|
322
|
+
fetchMessages,
|
|
323
|
+
fetchAttributes,
|
|
324
|
+
fetchAnswerDiagnostics,
|
|
325
|
+
onDiscard,
|
|
326
|
+
onApprove
|
|
327
|
+
}) {
|
|
110
328
|
const [messagesMap, setMessagesMap] = React__default.useState({});
|
|
111
329
|
const [loadingMap, setLoadingMap] = React__default.useState({});
|
|
112
330
|
const fetchedSetRef = React__default.useRef(/* @__PURE__ */ new Set());
|
|
@@ -133,7 +351,32 @@ function Convos({ convos, fetchMessages }) {
|
|
|
133
351
|
},
|
|
134
352
|
[fetchMessages]
|
|
135
353
|
);
|
|
136
|
-
|
|
354
|
+
const defaultLocale = convos[0]?.locale;
|
|
355
|
+
const contextValue = React__default.useMemo(
|
|
356
|
+
() => ({
|
|
357
|
+
fetchMessages,
|
|
358
|
+
fetchAttributes,
|
|
359
|
+
getMessages: (conversationId) => messagesMap[conversationId] || [],
|
|
360
|
+
isLoading: (conversationId) => loadingMap[conversationId] || false,
|
|
361
|
+
handleFetchMessages,
|
|
362
|
+
locale: defaultLocale,
|
|
363
|
+
fetchAnswerDiagnostics: (messageId) => fetchAnswerDiagnostics(messageId),
|
|
364
|
+
onDiscard,
|
|
365
|
+
onApprove
|
|
366
|
+
}),
|
|
367
|
+
[
|
|
368
|
+
fetchMessages,
|
|
369
|
+
fetchAttributes,
|
|
370
|
+
messagesMap,
|
|
371
|
+
loadingMap,
|
|
372
|
+
handleFetchMessages,
|
|
373
|
+
defaultLocale,
|
|
374
|
+
fetchAnswerDiagnostics,
|
|
375
|
+
onDiscard,
|
|
376
|
+
onApprove
|
|
377
|
+
]
|
|
378
|
+
);
|
|
379
|
+
return /* @__PURE__ */ React__default.createElement(ConvosProvider, { value: contextValue }, /* @__PURE__ */ React__default.createElement(Accordion, { type: "single", collapsible: true, className: "w-full space-y-2" }, convos.map((convo) => /* @__PURE__ */ React__default.createElement(
|
|
137
380
|
Convo,
|
|
138
381
|
{
|
|
139
382
|
key: convo.value,
|
|
@@ -145,14 +388,11 @@ function Convos({ convos, fetchMessages }) {
|
|
|
145
388
|
status: convo.status,
|
|
146
389
|
statusMessage: convo.statusMessage,
|
|
147
390
|
hasAttributes: convo.hasAttributes,
|
|
148
|
-
attributes: convo.attributes,
|
|
149
391
|
locale: convo.locale,
|
|
150
|
-
messages: messagesMap[convo.value] || [],
|
|
151
|
-
isLoading: loadingMap[convo.value] || false,
|
|
152
392
|
onClick: () => handleFetchMessages(convo.value)
|
|
153
393
|
}
|
|
154
|
-
)));
|
|
394
|
+
))));
|
|
155
395
|
}
|
|
156
396
|
|
|
157
|
-
export { Convo, Convos, Feedback, Message, MessageType, StatusBadge };
|
|
397
|
+
export { Convo, Convos, ConvosProvider, Feedback, Message, MessageType, StatusBadge, useConvosContext };
|
|
158
398
|
//# sourceMappingURL=convos.js.map
|