@classic-homes/agent-chat 0.6.0 → 0.7.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/README.md +2 -1
- package/dist/components/AgentChat.svelte +6 -0
- package/dist/components/AgentChat.svelte.d.ts.map +1 -1
- package/dist/components/AssistantMessage.svelte +6 -0
- package/dist/components/AssistantMessage.svelte.d.ts +3 -0
- package/dist/components/AssistantMessage.svelte.d.ts.map +1 -1
- package/dist/components/MessageActions.svelte +11 -1
- package/dist/components/MessageActions.svelte.d.ts +4 -0
- package/dist/components/MessageActions.svelte.d.ts.map +1 -1
- package/dist/components/MessageFeedback.svelte +89 -0
- package/dist/components/MessageFeedback.svelte.d.ts +13 -0
- package/dist/components/MessageFeedback.svelte.d.ts.map +1 -0
- package/dist/components/MessageItem.svelte +2 -0
- package/dist/components/MessageItem.svelte.d.ts +2 -0
- package/dist/components/MessageItem.svelte.d.ts.map +1 -1
- package/dist/components/MessageList.svelte +12 -0
- package/dist/components/MessageList.svelte.d.ts +3 -0
- package/dist/components/MessageList.svelte.d.ts.map +1 -1
- package/dist/components/feedback.svelte.test.d.ts +2 -0
- package/dist/components/feedback.svelte.test.d.ts.map +1 -0
- package/dist/components/feedback.svelte.test.js +99 -0
- package/dist/reducer.d.ts +3 -1
- package/dist/reducer.d.ts.map +1 -1
- package/dist/session.svelte.d.ts +6 -1
- package/dist/session.svelte.d.ts.map +1 -1
- package/dist/session.svelte.js +32 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -44,7 +44,8 @@ agent-api origin that serves `/frame`; artifacts and MCP-UI resources only ever
|
|
|
44
44
|
|
|
45
45
|
- `ChatHeader`, `ConversationSidebar`: title and conversation actions; the history rail.
|
|
46
46
|
- `MessageList`: transcript with day separators and a jump-to-latest button.
|
|
47
|
-
- `MessageItem` → `UserMessage` / `AssistantMessage`, with `MessageActions` (copy, time
|
|
47
|
+
- `MessageItem` → `UserMessage` / `AssistantMessage`, with `MessageActions` (copy, time,
|
|
48
|
+
`MessageFeedback` thumbs up/down with an optional note, Regenerate and Edit on the latest turn),
|
|
48
49
|
`ArtifactChip`, `MessageFiles` (image lightbox, downloads) and `ProposalCard` (read-only
|
|
49
50
|
document edits; applying stays in the Office add-in).
|
|
50
51
|
- `Composer`: attach by button, drop or paste; `prompts` turns on the `/` saved-prompt menu
|
|
@@ -256,6 +256,12 @@
|
|
|
256
256
|
onretry={retry}
|
|
257
257
|
onregenerate={(requestId) => void session.regenerate(requestId)}
|
|
258
258
|
onedit={(requestId, text) => void session.edit(requestId, text)}
|
|
259
|
+
onrate={session.conversationId
|
|
260
|
+
? (requestId, rating, comment) =>
|
|
261
|
+
session
|
|
262
|
+
.rate(requestId, rating, comment)
|
|
263
|
+
.catch(() => onnotify?.('Could not save your feedback. Try again.'))
|
|
264
|
+
: undefined}
|
|
259
265
|
oncontinue={() => void session.send('Continue.')}
|
|
260
266
|
empty={emptyState}
|
|
261
267
|
/>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentChat.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AgentChat.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EAEhB,MAAM,gCAAgC,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAYlE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAQrD,UAAU,KAAK;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,+EAA+E;IAC/E,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;IACtD,qBAAqB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,gFAAgF;IAChF,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,IAAI,CAAC;IACX,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,iGAAiG;IACjG,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAClC,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;IACnC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"AgentChat.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AgentChat.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EAEhB,MAAM,gCAAgC,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAYlE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAQrD,UAAU,KAAK;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,+EAA+E;IAC/E,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;IACtD,qBAAqB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,gFAAgF;IAChF,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,IAAI,CAAC;IACX,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,iGAAiG;IACjG,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAClC,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;IACnC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAmNH,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
-->
|
|
6
6
|
<script lang="ts">
|
|
7
7
|
import { Alert, Button, Icon } from '@classic-homes/theme-svelte';
|
|
8
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
8
9
|
import type { AssistantMessage } from '../reducer.js';
|
|
9
10
|
import { errorTitle } from '../reducer.js';
|
|
10
11
|
import { parseMarkdown } from '../markdown.js';
|
|
@@ -32,6 +33,8 @@
|
|
|
32
33
|
onretry?: () => void;
|
|
33
34
|
/** Ask for a new answer to the same question (the latest finished answer). */
|
|
34
35
|
onregenerate?: () => void;
|
|
36
|
+
/** Rate this answer; offered once it is done. */
|
|
37
|
+
onrate?: (rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
35
38
|
/** Ask the agent to pick up a stopped or cut-short answer. */
|
|
36
39
|
oncontinue?: () => void;
|
|
37
40
|
}
|
|
@@ -49,6 +52,7 @@
|
|
|
49
52
|
onnotify,
|
|
50
53
|
onretry,
|
|
51
54
|
onregenerate,
|
|
55
|
+
onrate,
|
|
52
56
|
oncontinue,
|
|
53
57
|
}: Props = $props();
|
|
54
58
|
|
|
@@ -183,6 +187,8 @@
|
|
|
183
187
|
createdAt={message.createdAt}
|
|
184
188
|
visible={last}
|
|
185
189
|
{onregenerate}
|
|
190
|
+
rating={message.feedback}
|
|
191
|
+
onrate={message.status === 'done' ? onrate : undefined}
|
|
186
192
|
class="-ml-1.5"
|
|
187
193
|
/>
|
|
188
194
|
{/if}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
1
2
|
import type { AssistantMessage } from '../reducer.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
message: AssistantMessage;
|
|
@@ -15,6 +16,8 @@ interface Props {
|
|
|
15
16
|
onretry?: () => void;
|
|
16
17
|
/** Ask for a new answer to the same question (the latest finished answer). */
|
|
17
18
|
onregenerate?: () => void;
|
|
19
|
+
/** Rate this answer; offered once it is done. */
|
|
20
|
+
onrate?: (rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
18
21
|
/** Ask the agent to pick up a stopped or cut-short answer. */
|
|
19
22
|
oncontinue?: () => void;
|
|
20
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantMessage.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AssistantMessage.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAYpD,UAAU,KAAK;IACb,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"AssistantMessage.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AssistantMessage.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAYpD,UAAU,KAAK;IACb,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AA2JH;;;GAGG;AACH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
have no hover.
|
|
7
7
|
-->
|
|
8
8
|
<script lang="ts">
|
|
9
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
9
10
|
import { Button, cn, CopyButton, Icon, Tooltip } from '@classic-homes/theme-svelte';
|
|
11
|
+
import MessageFeedback from './MessageFeedback.svelte';
|
|
10
12
|
import { formatDateTime, formatTime } from '../message-format.js';
|
|
11
13
|
|
|
12
14
|
interface Props {
|
|
@@ -19,6 +21,9 @@
|
|
|
19
21
|
onregenerate?: () => void;
|
|
20
22
|
/** Offer "Edit" (the latest question). */
|
|
21
23
|
onedit?: () => void;
|
|
24
|
+
/** The answer's rating; with `onrate`, thumbs up/down are offered. */
|
|
25
|
+
rating?: TurnRating;
|
|
26
|
+
onrate?: (rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
22
27
|
class?: string;
|
|
23
28
|
}
|
|
24
29
|
let {
|
|
@@ -28,13 +33,15 @@
|
|
|
28
33
|
align = 'start',
|
|
29
34
|
onregenerate,
|
|
30
35
|
onedit,
|
|
36
|
+
rating,
|
|
37
|
+
onrate,
|
|
31
38
|
class: className,
|
|
32
39
|
}: Props = $props();
|
|
33
40
|
</script>
|
|
34
41
|
|
|
35
42
|
<div
|
|
36
43
|
class={cn(
|
|
37
|
-
'flex items-center gap-0.5 text-xs text-muted-foreground transition-opacity',
|
|
44
|
+
'flex flex-wrap items-center gap-0.5 text-xs text-muted-foreground transition-opacity',
|
|
38
45
|
align === 'end' && 'justify-end',
|
|
39
46
|
!visible &&
|
|
40
47
|
'opacity-0 group-hover/message:opacity-100 group-focus-within/message:opacity-100 [@media(hover:none)]:opacity-100',
|
|
@@ -52,6 +59,9 @@
|
|
|
52
59
|
class="h-7 w-7"
|
|
53
60
|
/>
|
|
54
61
|
{/if}
|
|
62
|
+
{#if onrate}
|
|
63
|
+
<MessageFeedback {rating} {onrate} />
|
|
64
|
+
{/if}
|
|
55
65
|
{#if onedit}
|
|
56
66
|
<Button
|
|
57
67
|
variant="ghost"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
1
2
|
interface Props {
|
|
2
3
|
text: string;
|
|
3
4
|
createdAt: number;
|
|
@@ -8,6 +9,9 @@ interface Props {
|
|
|
8
9
|
onregenerate?: () => void;
|
|
9
10
|
/** Offer "Edit" (the latest question). */
|
|
10
11
|
onedit?: () => void;
|
|
12
|
+
/** The answer's rating; with `onrate`, thumbs up/down are offered. */
|
|
13
|
+
rating?: TurnRating;
|
|
14
|
+
onrate?: (rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
11
15
|
class?: string;
|
|
12
16
|
}
|
|
13
17
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageActions.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/MessageActions.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageActions.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/MessageActions.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAM/D,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACxB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,sEAAsE;IACtE,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqDH;;;;;GAKG;AACH,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Thumbs up / down for one answer. Choosing the current rating again withdraws it. After a thumbs
|
|
4
|
+
down, an optional note can follow ("What went wrong?").
|
|
5
|
+
-->
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
8
|
+
import { Button, cn, Icon, Input } from '@classic-homes/theme-svelte';
|
|
9
|
+
import { tick } from 'svelte';
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
rating?: TurnRating;
|
|
13
|
+
onrate: (rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
let { rating, onrate }: Props = $props();
|
|
16
|
+
|
|
17
|
+
let asking = $state(false);
|
|
18
|
+
let note = $state('');
|
|
19
|
+
const noteId = $props.id();
|
|
20
|
+
const NOTE_MAX = 1000;
|
|
21
|
+
|
|
22
|
+
async function choose(value: TurnRating): Promise<void> {
|
|
23
|
+
const next = rating === value ? null : value;
|
|
24
|
+
asking = next === 'down';
|
|
25
|
+
note = '';
|
|
26
|
+
await onrate(next);
|
|
27
|
+
if (asking) {
|
|
28
|
+
await tick();
|
|
29
|
+
document.getElementById(noteId)?.focus();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function sendNote(): Promise<void> {
|
|
33
|
+
const text = note.trim();
|
|
34
|
+
asking = false;
|
|
35
|
+
if (text) await onrate('down', text);
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<span class="inline-flex items-center" role="group" aria-label="Rate this answer">
|
|
40
|
+
<Button
|
|
41
|
+
variant="ghost"
|
|
42
|
+
size="icon"
|
|
43
|
+
class={cn('h-7 w-7', rating === 'up' && 'text-foreground')}
|
|
44
|
+
aria-label="Good answer"
|
|
45
|
+
aria-pressed={rating === 'up'}
|
|
46
|
+
title="Good answer"
|
|
47
|
+
onclick={() => void choose('up')}
|
|
48
|
+
>
|
|
49
|
+
<Icon name="thumbs-up" size={14} class={cn(rating === 'up' && 'fill-current')} />
|
|
50
|
+
</Button>
|
|
51
|
+
<Button
|
|
52
|
+
variant="ghost"
|
|
53
|
+
size="icon"
|
|
54
|
+
class={cn('h-7 w-7', rating === 'down' && 'text-foreground')}
|
|
55
|
+
aria-label="Bad answer"
|
|
56
|
+
aria-pressed={rating === 'down'}
|
|
57
|
+
title="Bad answer"
|
|
58
|
+
onclick={() => void choose('down')}
|
|
59
|
+
>
|
|
60
|
+
<Icon name="thumbs-down" size={14} class={cn(rating === 'down' && 'fill-current')} />
|
|
61
|
+
</Button>
|
|
62
|
+
</span>
|
|
63
|
+
{#if asking}
|
|
64
|
+
<form
|
|
65
|
+
class="flex basis-full items-center gap-2 pt-1"
|
|
66
|
+
aria-label="Feedback note"
|
|
67
|
+
onsubmit={(e) => {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
void sendNote();
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<Input
|
|
73
|
+
id={noteId}
|
|
74
|
+
bind:value={note}
|
|
75
|
+
maxlength={NOTE_MAX}
|
|
76
|
+
placeholder="What went wrong? (optional)"
|
|
77
|
+
aria-label="What went wrong?"
|
|
78
|
+
class="h-8 max-w-sm text-sm"
|
|
79
|
+
onkeydown={(e: KeyboardEvent) => {
|
|
80
|
+
if (e.key === 'Escape') {
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
asking = false;
|
|
83
|
+
}
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
<Button type="submit" size="sm" variant="outline" disabled={!note.trim()}>Send</Button>
|
|
87
|
+
<Button type="button" size="sm" variant="ghost" onclick={() => (asking = false)}>Skip</Button>
|
|
88
|
+
</form>
|
|
89
|
+
{/if}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
2
|
+
interface Props {
|
|
3
|
+
rating?: TurnRating;
|
|
4
|
+
onrate: (rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Thumbs up / down for one answer. Choosing the current rating again withdraws it. After a thumbs
|
|
8
|
+
* down, an optional note can follow ("What went wrong?").
|
|
9
|
+
*/
|
|
10
|
+
declare const MessageFeedback: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type MessageFeedback = ReturnType<typeof MessageFeedback>;
|
|
12
|
+
export default MessageFeedback;
|
|
13
|
+
//# sourceMappingURL=MessageFeedback.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageFeedback.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/MessageFeedback.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAK/D,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAyDH;;;GAGG;AACH,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
One transcript entry: a `UserMessage` or an `AssistantMessage`.
|
|
4
4
|
-->
|
|
5
5
|
<script lang="ts">
|
|
6
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
6
7
|
import type { ChatMessage } from '../reducer.js';
|
|
7
8
|
import AssistantMessage from './AssistantMessage.svelte';
|
|
8
9
|
import UserMessage from './UserMessage.svelte';
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
onnotify?: (message: string) => void;
|
|
22
23
|
onretry?: () => void;
|
|
23
24
|
onregenerate?: () => void;
|
|
25
|
+
onrate?: (rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
24
26
|
oncontinue?: () => void;
|
|
25
27
|
/** User messages only: edit and resend. */
|
|
26
28
|
onedit?: (text: string) => void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
1
2
|
import type { ChatMessage } from '../reducer.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
message: ChatMessage;
|
|
@@ -13,6 +14,7 @@ interface Props {
|
|
|
13
14
|
onnotify?: (message: string) => void;
|
|
14
15
|
onretry?: () => void;
|
|
15
16
|
onregenerate?: () => void;
|
|
17
|
+
onrate?: (rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
16
18
|
oncontinue?: () => void;
|
|
17
19
|
/** User messages only: edit and resend. */
|
|
18
20
|
onedit?: (text: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageItem.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/MessageItem.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAK/C,UAAU,KAAK;IACb,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;
|
|
1
|
+
{"version":3,"file":"MessageItem.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/MessageItem.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAK/C,UAAU,KAAK;IACb,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAmBH,sEAAsE;AACtE,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { tick } from 'svelte';
|
|
3
3
|
import { Button, Icon } from '@classic-homes/theme-svelte';
|
|
4
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
4
5
|
import type { ChatMessage } from '../reducer.js';
|
|
5
6
|
import { daySeparators, formatDay } from '../message-format.js';
|
|
6
7
|
import MessageItem from './MessageItem.svelte';
|
|
@@ -19,6 +20,12 @@
|
|
|
19
20
|
onretry?: () => void;
|
|
20
21
|
/** Regenerate the latest answer, by its request id. */
|
|
21
22
|
onregenerate?: (requestId: string) => void;
|
|
23
|
+
/** Rate an answer, by its request id. */
|
|
24
|
+
onrate?: (
|
|
25
|
+
requestId: string,
|
|
26
|
+
rating: TurnRating | null,
|
|
27
|
+
comment?: string,
|
|
28
|
+
) => void | Promise<void>;
|
|
22
29
|
/** Edit the latest question, by its request id. */
|
|
23
30
|
onedit?: (requestId: string, text: string) => void;
|
|
24
31
|
oncontinue?: () => void;
|
|
@@ -37,6 +44,7 @@
|
|
|
37
44
|
onnotify,
|
|
38
45
|
onretry,
|
|
39
46
|
onregenerate,
|
|
47
|
+
onrate,
|
|
40
48
|
onedit,
|
|
41
49
|
oncontinue,
|
|
42
50
|
empty,
|
|
@@ -163,6 +171,10 @@
|
|
|
163
171
|
message.status === 'done'
|
|
164
172
|
? () => onregenerate(latest)
|
|
165
173
|
: undefined}
|
|
174
|
+
onrate={onrate && message.role === 'assistant'
|
|
175
|
+
? (rating: TurnRating | null, comment?: string) =>
|
|
176
|
+
onrate(message.requestId, rating, comment)
|
|
177
|
+
: undefined}
|
|
166
178
|
onedit={latest && onedit && i === messages.length - 2
|
|
167
179
|
? (text: string) => onedit(latest, text)
|
|
168
180
|
: undefined}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TurnRating } from '@classic-homes/agent-contracts';
|
|
1
2
|
import type { ChatMessage } from '../reducer.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
messages: ChatMessage[];
|
|
@@ -13,6 +14,8 @@ interface Props {
|
|
|
13
14
|
onretry?: () => void;
|
|
14
15
|
/** Regenerate the latest answer, by its request id. */
|
|
15
16
|
onregenerate?: (requestId: string) => void;
|
|
17
|
+
/** Rate an answer, by its request id. */
|
|
18
|
+
onrate?: (requestId: string, rating: TurnRating | null, comment?: string) => void | Promise<void>;
|
|
16
19
|
/** Edit the latest question, by its request id. */
|
|
17
20
|
onedit?: (requestId: string, text: string) => void;
|
|
18
21
|
oncontinue?: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/MessageList.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAK/C,UAAU,KAAK;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,uDAAuD;IACvD,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,mDAAmD;IACnD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;CAClC;
|
|
1
|
+
{"version":3,"file":"MessageList.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/MessageList.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAK/C,UAAU,KAAK;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,uDAAuD;IACvD,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,yCAAyC;IACzC,MAAM,CAAC,EAAE,CACP,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,OAAO,CAAC,EAAE,MAAM,KACb,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,mDAAmD;IACnD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;CAClC;AAkJH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.svelte.test.d.ts","sourceRoot":"","sources":["../../src/lib/components/feedback.svelte.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { cleanup, render, screen, waitFor } from '@testing-library/svelte';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { ChatSession } from '../session.svelte.js';
|
|
5
|
+
import AgentChat from './AgentChat.svelte';
|
|
6
|
+
afterEach(() => cleanup());
|
|
7
|
+
function fakeClient(sendFeedback = vi.fn().mockResolvedValue(undefined)) {
|
|
8
|
+
return {
|
|
9
|
+
fileUrl: (id) => `#${id}`,
|
|
10
|
+
uploadFile: vi.fn(),
|
|
11
|
+
cancel: vi.fn(),
|
|
12
|
+
resumeTurn: vi.fn(),
|
|
13
|
+
sendFeedback,
|
|
14
|
+
getConversation: vi.fn().mockResolvedValue({
|
|
15
|
+
conversation: { conversationId: 'c-1' },
|
|
16
|
+
messages: [
|
|
17
|
+
{ id: 'u-r1', seq: 0, role: 'user', content: [{ type: 'text', text: 'Q' }], createdAt: 1 },
|
|
18
|
+
{
|
|
19
|
+
id: 'a-r1',
|
|
20
|
+
seq: 1,
|
|
21
|
+
role: 'assistant',
|
|
22
|
+
content: [{ type: 'text', text: 'A' }],
|
|
23
|
+
createdAt: 2,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
files: [],
|
|
27
|
+
feedback: { r1: 'up' },
|
|
28
|
+
}),
|
|
29
|
+
streamChat: vi.fn(async (req, opts) => {
|
|
30
|
+
[
|
|
31
|
+
{ type: 'start', conversationId: 'c-1', mode: 'live' },
|
|
32
|
+
{ type: 'delta', text: 'Answer' },
|
|
33
|
+
{ type: 'done', finishReason: 'stop' },
|
|
34
|
+
].forEach((e, seq) => opts.onEvent({ requestId: req.requestId, seq, ...e }));
|
|
35
|
+
return { kind: 'done' };
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
describe('ChatSession.rate', () => {
|
|
40
|
+
it('loads stored ratings and sends changes', async () => {
|
|
41
|
+
const client = fakeClient();
|
|
42
|
+
const session = new ChatSession({ client: client });
|
|
43
|
+
await session.load('c-1');
|
|
44
|
+
expect(session.messages[1]).toMatchObject({ feedback: 'up' });
|
|
45
|
+
await session.rate('r1', 'down', ' Wrong lot ');
|
|
46
|
+
expect(client.sendFeedback).toHaveBeenCalledWith('c-1', 'r1', {
|
|
47
|
+
rating: 'down',
|
|
48
|
+
comment: 'Wrong lot',
|
|
49
|
+
});
|
|
50
|
+
expect(session.messages[1]).toMatchObject({ feedback: 'down' });
|
|
51
|
+
await session.rate('r1', null);
|
|
52
|
+
expect(session.messages[1]).not.toHaveProperty('feedback');
|
|
53
|
+
});
|
|
54
|
+
it('puts the old rating back when the runtime refuses', async () => {
|
|
55
|
+
const client = fakeClient(vi.fn().mockRejectedValue(new Error('nope')));
|
|
56
|
+
const session = new ChatSession({ client: client });
|
|
57
|
+
await session.load('c-1');
|
|
58
|
+
await expect(session.rate('r1', 'down')).rejects.toThrow('nope');
|
|
59
|
+
expect(session.messages[1]).toMatchObject({ feedback: 'up' });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('AgentChat feedback', () => {
|
|
63
|
+
it('rates an answer, asks for a note after a thumbs down, and toggles off', async () => {
|
|
64
|
+
const user = userEvent.setup();
|
|
65
|
+
const client = fakeClient();
|
|
66
|
+
render(AgentChat, { props: { client: client, sandboxOrigin: 'https://frame.test' } });
|
|
67
|
+
await user.type(screen.getByRole('textbox', { name: 'Message' }), 'hi{Enter}');
|
|
68
|
+
await screen.findAllByText('Answer');
|
|
69
|
+
const requestId = client.streamChat.mock.calls[0][0].requestId;
|
|
70
|
+
const down = screen.getByRole('button', { name: 'Bad answer' });
|
|
71
|
+
await user.click(down);
|
|
72
|
+
expect(client.sendFeedback).toHaveBeenLastCalledWith('c-1', requestId, { rating: 'down' });
|
|
73
|
+
expect(down.getAttribute('aria-pressed')).toBe('true');
|
|
74
|
+
const note = await screen.findByRole('textbox', { name: 'What went wrong?' });
|
|
75
|
+
await waitFor(() => expect(document.activeElement).toBe(note));
|
|
76
|
+
await user.type(note, 'Old prices{Enter}');
|
|
77
|
+
expect(client.sendFeedback).toHaveBeenLastCalledWith('c-1', requestId, {
|
|
78
|
+
rating: 'down',
|
|
79
|
+
comment: 'Old prices',
|
|
80
|
+
});
|
|
81
|
+
expect(screen.queryByRole('textbox', { name: 'What went wrong?' })).toBeNull();
|
|
82
|
+
await user.click(screen.getByRole('button', { name: 'Bad answer' }));
|
|
83
|
+
expect(client.sendFeedback).toHaveBeenLastCalledWith('c-1', requestId, { rating: null });
|
|
84
|
+
expect(screen.getByRole('button', { name: 'Bad answer' }).getAttribute('aria-pressed')).toBe('false');
|
|
85
|
+
});
|
|
86
|
+
it('tells the host when a rating could not be saved', async () => {
|
|
87
|
+
const user = userEvent.setup();
|
|
88
|
+
const onnotify = vi.fn();
|
|
89
|
+
const client = fakeClient(vi.fn().mockRejectedValue(new Error('down')));
|
|
90
|
+
render(AgentChat, {
|
|
91
|
+
props: { client: client, sandboxOrigin: 'https://frame.test', onnotify },
|
|
92
|
+
});
|
|
93
|
+
await user.type(screen.getByRole('textbox', { name: 'Message' }), 'hi{Enter}');
|
|
94
|
+
await screen.findAllByText('Answer');
|
|
95
|
+
await user.click(screen.getByRole('button', { name: 'Good answer' }));
|
|
96
|
+
await waitFor(() => expect(onnotify).toHaveBeenCalledWith(expect.stringMatching(/feedback/)));
|
|
97
|
+
expect(screen.getByRole('button', { name: 'Good answer' }).getAttribute('aria-pressed')).toBe('false');
|
|
98
|
+
});
|
|
99
|
+
});
|
package/dist/reducer.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ArtifactMediaType, BackendMode, ChatEvent, ClientKind, DocumentContext, FileRef, ProposedOperation, ToolResultStatus } from '@classic-homes/agent-contracts';
|
|
1
|
+
import type { ArtifactMediaType, BackendMode, ChatEvent, ClientKind, DocumentContext, FileRef, ProposedOperation, ToolResultStatus, TurnRating } from '@classic-homes/agent-contracts';
|
|
2
2
|
import type { ClientErrorCode } from '@classic-homes/agent-client';
|
|
3
3
|
/**
|
|
4
4
|
* Pure state model for one conversation view. `applyEvent` folds stream events into the
|
|
@@ -78,6 +78,8 @@ export interface AssistantMessage {
|
|
|
78
78
|
};
|
|
79
79
|
finishReason?: 'stop' | 'length' | 'cancelled' | 'error';
|
|
80
80
|
mode?: BackendMode;
|
|
81
|
+
/** The user's rating of this answer, when given. */
|
|
82
|
+
feedback?: TurnRating;
|
|
81
83
|
createdAt: number;
|
|
82
84
|
}
|
|
83
85
|
export type ChatMessage = UserMessage | AssistantMessage;
|
package/dist/reducer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../src/lib/reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,WAAW,EACX,SAAS,EACT,UAAU,EACV,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../src/lib/reducer.ts"],"names":[],"mappings":"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"}
|
package/dist/session.svelte.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentApiClient } from '@classic-homes/agent-client';
|
|
2
|
-
import type { AgentKey, BackendMode, ChatEvent, ClientKind, DocumentContext, FileRef } from '@classic-homes/agent-contracts';
|
|
2
|
+
import type { AgentKey, BackendMode, ChatEvent, ClientKind, DocumentContext, FileRef, TurnRating } from '@classic-homes/agent-contracts';
|
|
3
3
|
import { type AssistantMessage, type ChatMessage } from './reducer.js';
|
|
4
4
|
export interface ChatSessionOptions {
|
|
5
5
|
client: AgentApiClient;
|
|
@@ -69,6 +69,11 @@ export declare class ChatSession {
|
|
|
69
69
|
edit(requestId: string, text: string): Promise<void>;
|
|
70
70
|
/** Re-attach to a turn after reload: replays events after the last seq we hold (none here). */
|
|
71
71
|
resume(requestId: string): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Rate an answer (`null` withdraws the rating). Shown at once; put back and rethrown when the
|
|
74
|
+
* runtime refuses it.
|
|
75
|
+
*/
|
|
76
|
+
rate(requestId: string, rating: TurnRating | null, comment?: string): Promise<void>;
|
|
72
77
|
cancel(): void;
|
|
73
78
|
private run;
|
|
74
79
|
private toolName;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.svelte.d.ts","sourceRoot":"","sources":["../src/lib/session.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EACV,QAAQ,EACR,WAAW,EACX,SAAS,EAET,UAAU,EACV,eAAe,EACf,OAAO,
|
|
1
|
+
{"version":3,"file":"session.svelte.d.ts","sourceRoot":"","sources":["../src/lib/session.svelte.ts"],"names":[],"mappings":"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"}
|
package/dist/session.svelte.js
CHANGED
|
@@ -47,6 +47,9 @@ export class ChatSession {
|
|
|
47
47
|
...newAssistantMessage(m.id.replace(/^a-/, ''), m.createdAt),
|
|
48
48
|
text: m.content.map((c) => c.text).join('\n'),
|
|
49
49
|
status: 'done',
|
|
50
|
+
...(detail.feedback?.[m.id.replace(/^a-/, '')]
|
|
51
|
+
? { feedback: detail.feedback[m.id.replace(/^a-/, '')] }
|
|
52
|
+
: {}),
|
|
50
53
|
});
|
|
51
54
|
const active = detail.conversation.activeRequestId;
|
|
52
55
|
this.status = 'idle';
|
|
@@ -133,6 +136,35 @@ export class ChatSession {
|
|
|
133
136
|
const conversationId = this.conversationId;
|
|
134
137
|
await this.run(requestId, (signal, onEvent) => this.options.client.resumeTurn(conversationId, requestId, -1, { signal, onEvent }));
|
|
135
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Rate an answer (`null` withdraws the rating). Shown at once; put back and rethrown when the
|
|
141
|
+
* runtime refuses it.
|
|
142
|
+
*/
|
|
143
|
+
async rate(requestId, rating, comment) {
|
|
144
|
+
const conversationId = this.conversationId;
|
|
145
|
+
if (!conversationId)
|
|
146
|
+
return;
|
|
147
|
+
const before = this.messages.find((m) => m.role === 'assistant' && m.requestId === requestId)?.feedback;
|
|
148
|
+
const set = (value) => this.update(requestId, (m) => {
|
|
149
|
+
const next = { ...m };
|
|
150
|
+
if (value)
|
|
151
|
+
next.feedback = value;
|
|
152
|
+
else
|
|
153
|
+
delete next.feedback;
|
|
154
|
+
return next;
|
|
155
|
+
});
|
|
156
|
+
set(rating ?? undefined);
|
|
157
|
+
try {
|
|
158
|
+
await this.options.client.sendFeedback(conversationId, requestId, {
|
|
159
|
+
rating,
|
|
160
|
+
...(comment?.trim() ? { comment: comment.trim() } : {}),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
set(before);
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
136
168
|
cancel() {
|
|
137
169
|
const current = this.current;
|
|
138
170
|
if (!this.controller || !current)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classic-homes/agent-chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Svelte 5 chat UI for Classic Agent: session state, message list, composer, tool timeline, artifacts and MCP-UI sandbox frames. Consumed by the Classic App.",
|
|
@@ -17,16 +17,16 @@
|
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@classic-homes/agent-client": "0.
|
|
21
|
-
"@classic-homes/agent-contracts": "0.
|
|
20
|
+
"@classic-homes/agent-client": "0.7.0",
|
|
21
|
+
"@classic-homes/agent-contracts": "0.7.0",
|
|
22
22
|
"dompurify": "3.4.17"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@classic-homes/theme-svelte": "^1.
|
|
26
|
-
"svelte": "^5.
|
|
25
|
+
"@classic-homes/theme-svelte": "^1.42.2",
|
|
26
|
+
"svelte": "^5.31.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@classic-homes/theme-svelte": "1.
|
|
29
|
+
"@classic-homes/theme-svelte": "1.42.2",
|
|
30
30
|
"@sveltejs/package": "3.0.2",
|
|
31
31
|
"@sveltejs/vite-plugin-svelte": "7.3.1",
|
|
32
32
|
"@testing-library/svelte": "5.4.2",
|