@tanstack/ai-devtools-core 0.0.1
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/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/esm/components/ConversationDetails.d.ts +2 -0
- package/dist/esm/components/ConversationDetails.js +134 -0
- package/dist/esm/components/ConversationDetails.js.map +1 -0
- package/dist/esm/components/ConversationsList.d.ts +2 -0
- package/dist/esm/components/ConversationsList.js +30 -0
- package/dist/esm/components/ConversationsList.js.map +1 -0
- package/dist/esm/components/Shell.d.ts +1 -0
- package/dist/esm/components/Shell.js +94 -0
- package/dist/esm/components/Shell.js.map +1 -0
- package/dist/esm/components/conversation/ChunkBadges.d.ts +7 -0
- package/dist/esm/components/conversation/ChunkBadges.js +54 -0
- package/dist/esm/components/conversation/ChunkBadges.js.map +1 -0
- package/dist/esm/components/conversation/ChunkItem.d.ts +9 -0
- package/dist/esm/components/conversation/ChunkItem.js +247 -0
- package/dist/esm/components/conversation/ChunkItem.js.map +1 -0
- package/dist/esm/components/conversation/ChunksCollapsible.d.ts +7 -0
- package/dist/esm/components/conversation/ChunksCollapsible.js +77 -0
- package/dist/esm/components/conversation/ChunksCollapsible.js.map +1 -0
- package/dist/esm/components/conversation/ChunksTab.d.ts +8 -0
- package/dist/esm/components/conversation/ChunksTab.js +87 -0
- package/dist/esm/components/conversation/ChunksTab.js.map +1 -0
- package/dist/esm/components/conversation/ConversationHeader.d.ts +7 -0
- package/dist/esm/components/conversation/ConversationHeader.js +187 -0
- package/dist/esm/components/conversation/ConversationHeader.js.map +1 -0
- package/dist/esm/components/conversation/ConversationTabs.d.ts +10 -0
- package/dist/esm/components/conversation/ConversationTabs.js +88 -0
- package/dist/esm/components/conversation/ConversationTabs.js.map +1 -0
- package/dist/esm/components/conversation/EmbeddingsTab.d.ts +7 -0
- package/dist/esm/components/conversation/EmbeddingsTab.js +90 -0
- package/dist/esm/components/conversation/EmbeddingsTab.js.map +1 -0
- package/dist/esm/components/conversation/MessageCard.d.ts +7 -0
- package/dist/esm/components/conversation/MessageCard.js +152 -0
- package/dist/esm/components/conversation/MessageCard.js.map +1 -0
- package/dist/esm/components/conversation/MessageGroup.d.ts +15 -0
- package/dist/esm/components/conversation/MessageGroup.js +119 -0
- package/dist/esm/components/conversation/MessageGroup.js.map +1 -0
- package/dist/esm/components/conversation/MessagesTab.d.ts +7 -0
- package/dist/esm/components/conversation/MessagesTab.js +37 -0
- package/dist/esm/components/conversation/MessagesTab.js.map +1 -0
- package/dist/esm/components/conversation/SummariesTab.d.ts +7 -0
- package/dist/esm/components/conversation/SummariesTab.js +110 -0
- package/dist/esm/components/conversation/SummariesTab.js.map +1 -0
- package/dist/esm/components/conversation/ToolCallDisplay.d.ts +7 -0
- package/dist/esm/components/conversation/ToolCallDisplay.js +123 -0
- package/dist/esm/components/conversation/ToolCallDisplay.js.map +1 -0
- package/dist/esm/components/conversation/index.d.ts +7 -0
- package/dist/esm/components/list/ConversationRow.d.ts +7 -0
- package/dist/esm/components/list/ConversationRow.js +122 -0
- package/dist/esm/components/list/ConversationRow.js.map +1 -0
- package/dist/esm/components/list/index.d.ts +1 -0
- package/dist/esm/components/utils/format.d.ts +6 -0
- package/dist/esm/components/utils/format.js +68 -0
- package/dist/esm/components/utils/format.js.map +1 -0
- package/dist/esm/components/utils/index.d.ts +1 -0
- package/dist/esm/core.d.ts +18 -0
- package/dist/esm/core.js +9 -0
- package/dist/esm/core.js.map +1 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/production.d.ts +2 -0
- package/dist/esm/production.js +5 -0
- package/dist/esm/production.js.map +1 -0
- package/dist/esm/store/ai-context.d.ts +126 -0
- package/dist/esm/store/ai-context.js +1081 -0
- package/dist/esm/store/ai-context.js.map +1 -0
- package/dist/esm/store/ai-store.d.ts +1 -0
- package/dist/esm/styles/tokens.d.ts +298 -0
- package/dist/esm/styles/tokens.js +107 -0
- package/dist/esm/styles/tokens.js.map +1 -0
- package/dist/esm/styles/use-styles.d.ts +226 -0
- package/dist/esm/styles/use-styles.js +1538 -0
- package/dist/esm/styles/use-styles.js.map +1 -0
- package/package.json +61 -0
- package/src/components/ConversationDetails.tsx +89 -0
- package/src/components/ConversationsList.tsx +21 -0
- package/src/components/Shell.tsx +93 -0
- package/src/components/conversation/ChunkBadges.tsx +51 -0
- package/src/components/conversation/ChunkItem.tsx +273 -0
- package/src/components/conversation/ChunksCollapsible.tsx +58 -0
- package/src/components/conversation/ChunksTab.tsx +85 -0
- package/src/components/conversation/ConversationHeader.tsx +130 -0
- package/src/components/conversation/ConversationTabs.tsx +104 -0
- package/src/components/conversation/EmbeddingsTab.tsx +80 -0
- package/src/components/conversation/MessageCard.tsx +137 -0
- package/src/components/conversation/MessageGroup.tsx +101 -0
- package/src/components/conversation/MessagesTab.tsx +30 -0
- package/src/components/conversation/SummariesTab.tsx +91 -0
- package/src/components/conversation/ToolCallDisplay.tsx +99 -0
- package/src/components/conversation/index.ts +8 -0
- package/src/components/list/ConversationRow.tsx +85 -0
- package/src/components/list/index.ts +1 -0
- package/src/components/utils/format.ts +61 -0
- package/src/components/utils/index.ts +1 -0
- package/src/core.tsx +10 -0
- package/src/index.ts +8 -0
- package/src/production.ts +3 -0
- package/src/store/ai-context.tsx +1784 -0
- package/src/store/ai-store.ts +2 -0
- package/src/styles/tokens.ts +305 -0
- package/src/styles/use-styles.ts +1537 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { For, Show } from 'solid-js'
|
|
2
|
+
import { useStyles } from '../../styles/use-styles'
|
|
3
|
+
import { formatDuration } from '../utils'
|
|
4
|
+
import type { Component } from 'solid-js'
|
|
5
|
+
import type { EmbeddingOperation } from '../../store/ai-context'
|
|
6
|
+
|
|
7
|
+
interface EmbeddingsTabProps {
|
|
8
|
+
embeddings: Array<EmbeddingOperation>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const EmbeddingsTab: Component<EmbeddingsTabProps> = (props) => {
|
|
12
|
+
const styles = useStyles()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Show
|
|
16
|
+
when={props.embeddings.length > 0}
|
|
17
|
+
fallback={
|
|
18
|
+
<div class={styles().conversationDetails.emptyMessages}>
|
|
19
|
+
No embedding operations yet
|
|
20
|
+
</div>
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
<div class={styles().conversationDetails.messagesList}>
|
|
24
|
+
<For each={props.embeddings}>
|
|
25
|
+
{(embedding) => (
|
|
26
|
+
<div class={styles().conversationDetails.operationCard}>
|
|
27
|
+
<div class={styles().conversationDetails.operationHeader}>
|
|
28
|
+
<div class={styles().conversationDetails.operationIcon}>🔢</div>
|
|
29
|
+
<div class={styles().conversationDetails.operationTitle}>
|
|
30
|
+
Embedding
|
|
31
|
+
</div>
|
|
32
|
+
<div
|
|
33
|
+
class={`${styles().conversationDetails.operationStatus} ${
|
|
34
|
+
embedding.status === 'completed'
|
|
35
|
+
? styles().conversationDetails.operationStatusCompleted
|
|
36
|
+
: styles().conversationDetails.operationStatusPending
|
|
37
|
+
}`}
|
|
38
|
+
>
|
|
39
|
+
{embedding.status}
|
|
40
|
+
</div>
|
|
41
|
+
<Show when={embedding.duration > 0}>
|
|
42
|
+
<div class={styles().conversationDetails.durationBadge}>
|
|
43
|
+
⏱️ {formatDuration(embedding.duration)}
|
|
44
|
+
</div>
|
|
45
|
+
</Show>
|
|
46
|
+
</div>
|
|
47
|
+
<div class={styles().conversationDetails.operationDetails}>
|
|
48
|
+
<div class={styles().conversationDetails.operationDetail}>
|
|
49
|
+
<span class={styles().conversationDetails.operationLabel}>
|
|
50
|
+
Model:
|
|
51
|
+
</span>
|
|
52
|
+
<span class={styles().conversationDetails.operationValue}>
|
|
53
|
+
{embedding.model}
|
|
54
|
+
</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div class={styles().conversationDetails.operationDetail}>
|
|
57
|
+
<span class={styles().conversationDetails.operationLabel}>
|
|
58
|
+
Inputs:
|
|
59
|
+
</span>
|
|
60
|
+
<span class={styles().conversationDetails.operationValue}>
|
|
61
|
+
{embedding.inputCount} item
|
|
62
|
+
{embedding.inputCount !== 1 ? 's' : ''}
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
<div class={styles().conversationDetails.operationDetail}>
|
|
66
|
+
<span class={styles().conversationDetails.operationLabel}>
|
|
67
|
+
Started:
|
|
68
|
+
</span>
|
|
69
|
+
<span class={styles().conversationDetails.operationValue}>
|
|
70
|
+
{new Date(embedding.timestamp).toLocaleTimeString()}
|
|
71
|
+
</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</For>
|
|
77
|
+
</div>
|
|
78
|
+
</Show>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { For, Show } from 'solid-js'
|
|
2
|
+
import { useStyles } from '../../styles/use-styles'
|
|
3
|
+
import { formatTimestamp } from '../utils'
|
|
4
|
+
import { ToolCallDisplay } from './ToolCallDisplay'
|
|
5
|
+
import { ChunksCollapsible } from './ChunksCollapsible'
|
|
6
|
+
import type { Message } from '../../store/ai-store'
|
|
7
|
+
import type { Component } from 'solid-js'
|
|
8
|
+
|
|
9
|
+
interface MessageCardProps {
|
|
10
|
+
message: Message
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const MessageCard: Component<MessageCardProps> = (props) => {
|
|
14
|
+
const styles = useStyles()
|
|
15
|
+
// Access message through props directly for proper SolidJS reactivity
|
|
16
|
+
const msg = () => props.message
|
|
17
|
+
|
|
18
|
+
// Check if message is from client (explicitly marked) or server (default)
|
|
19
|
+
const isClientMessage = () => msg().source === 'client'
|
|
20
|
+
|
|
21
|
+
// Determine card class based on role and source
|
|
22
|
+
const getCardClass = () => {
|
|
23
|
+
const base = styles().conversationDetails.messageCard
|
|
24
|
+
if (msg().role === 'user') {
|
|
25
|
+
return `${base} ${styles().conversationDetails.messageCardUser}`
|
|
26
|
+
}
|
|
27
|
+
// For assistant messages: client if explicitly marked, otherwise server
|
|
28
|
+
if (isClientMessage()) {
|
|
29
|
+
return `${base} ${styles().conversationDetails.messageCardClient}`
|
|
30
|
+
}
|
|
31
|
+
return `${base} ${styles().conversationDetails.messageCardServer}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if this is a non-user message (needs source banner and content wrapper)
|
|
35
|
+
const isSourcedMessage = () => msg().role !== 'user'
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div class={getCardClass()}>
|
|
39
|
+
{/* Source indicator banner at top of card */}
|
|
40
|
+
<Show when={isSourcedMessage()}>
|
|
41
|
+
<div
|
|
42
|
+
class={`${styles().conversationDetails.sourceBanner} ${
|
|
43
|
+
isClientMessage()
|
|
44
|
+
? styles().conversationDetails.sourceBannerClient
|
|
45
|
+
: styles().conversationDetails.sourceBannerServer
|
|
46
|
+
}`}
|
|
47
|
+
>
|
|
48
|
+
<span class={styles().conversationDetails.sourceBannerIcon}>
|
|
49
|
+
{isClientMessage() ? '📱' : '☁️'}
|
|
50
|
+
</span>
|
|
51
|
+
<span class={styles().conversationDetails.sourceBannerText}>
|
|
52
|
+
{isClientMessage() ? 'Client Message' : 'Server Message'}
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
55
|
+
</Show>
|
|
56
|
+
{/* Content wrapper with padding */}
|
|
57
|
+
<div
|
|
58
|
+
class={
|
|
59
|
+
isSourcedMessage()
|
|
60
|
+
? styles().conversationDetails.messageCardContent
|
|
61
|
+
: ''
|
|
62
|
+
}
|
|
63
|
+
>
|
|
64
|
+
<div class={styles().conversationDetails.messageHeader}>
|
|
65
|
+
<div
|
|
66
|
+
class={
|
|
67
|
+
msg().role === 'user'
|
|
68
|
+
? styles().conversationDetails.avatarUser
|
|
69
|
+
: isClientMessage()
|
|
70
|
+
? styles().conversationDetails.avatarClient
|
|
71
|
+
: styles().conversationDetails.avatarServer
|
|
72
|
+
}
|
|
73
|
+
>
|
|
74
|
+
{msg().role === 'user' ? 'U' : msg().role === 'tool' ? '🔧' : '🤖'}
|
|
75
|
+
</div>
|
|
76
|
+
<div class={styles().conversationDetails.roleLabel}>
|
|
77
|
+
<div
|
|
78
|
+
class={
|
|
79
|
+
msg().role === 'user'
|
|
80
|
+
? styles().conversationDetails.roleLabelUser
|
|
81
|
+
: isClientMessage()
|
|
82
|
+
? styles().conversationDetails.roleLabelClient
|
|
83
|
+
: styles().conversationDetails.roleLabelServer
|
|
84
|
+
}
|
|
85
|
+
>
|
|
86
|
+
{msg().role}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
<div class={styles().conversationDetails.timestamp}>
|
|
90
|
+
{formatTimestamp(msg().timestamp)}
|
|
91
|
+
</div>
|
|
92
|
+
{/* Per-message token usage */}
|
|
93
|
+
<Show when={msg().usage}>
|
|
94
|
+
<div class={styles().conversationDetails.messageUsage}>
|
|
95
|
+
<span class={styles().conversationDetails.messageUsageIcon}>
|
|
96
|
+
🎯
|
|
97
|
+
</span>
|
|
98
|
+
<span>{msg().usage?.promptTokens.toLocaleString()} in</span>
|
|
99
|
+
<span>•</span>
|
|
100
|
+
<span>{msg().usage?.completionTokens.toLocaleString()} out</span>
|
|
101
|
+
</div>
|
|
102
|
+
</Show>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
{/* Thinking content (for extended thinking models) */}
|
|
106
|
+
<Show when={msg().thinkingContent}>
|
|
107
|
+
<details class={styles().conversationDetails.thinkingDetails}>
|
|
108
|
+
<summary class={styles().conversationDetails.thinkingSummary}>
|
|
109
|
+
💭 Thinking...
|
|
110
|
+
</summary>
|
|
111
|
+
<div class={styles().conversationDetails.thinkingContent}>
|
|
112
|
+
{msg().thinkingContent}
|
|
113
|
+
</div>
|
|
114
|
+
</details>
|
|
115
|
+
</Show>
|
|
116
|
+
|
|
117
|
+
<div class={styles().conversationDetails.messageContent}>
|
|
118
|
+
{msg().content}
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{/* Tool Calls Display */}
|
|
122
|
+
<Show when={msg().toolCalls && msg().toolCalls!.length > 0}>
|
|
123
|
+
<div class={styles().conversationDetails.toolCallsContainer}>
|
|
124
|
+
<For each={msg().toolCalls}>
|
|
125
|
+
{(tool) => <ToolCallDisplay tool={tool} />}
|
|
126
|
+
</For>
|
|
127
|
+
</div>
|
|
128
|
+
</Show>
|
|
129
|
+
|
|
130
|
+
{/* Chunks Display (for client conversations with server chunks) */}
|
|
131
|
+
<Show when={msg().chunks && msg().chunks!.length > 0}>
|
|
132
|
+
<ChunksCollapsible chunks={msg().chunks!} />
|
|
133
|
+
</Show>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { For, Show } from 'solid-js'
|
|
2
|
+
import { useStyles } from '../../styles/use-styles'
|
|
3
|
+
import { ChunkItem } from './ChunkItem'
|
|
4
|
+
import { ChunkBadges } from './ChunkBadges'
|
|
5
|
+
import type { Component } from 'solid-js'
|
|
6
|
+
import type { Chunk } from '../../store/ai-store'
|
|
7
|
+
|
|
8
|
+
interface TokenUsage {
|
|
9
|
+
promptTokens: number
|
|
10
|
+
completionTokens: number
|
|
11
|
+
totalTokens: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface MessageGroupProps {
|
|
15
|
+
messageId: string
|
|
16
|
+
chunks: Array<Chunk>
|
|
17
|
+
groupIndex: number
|
|
18
|
+
usage?: TokenUsage
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const MessageGroup: Component<MessageGroupProps> = (props) => {
|
|
22
|
+
const styles = useStyles()
|
|
23
|
+
|
|
24
|
+
const accumulatedContent = () =>
|
|
25
|
+
props.chunks
|
|
26
|
+
.filter((c) => c.type === 'content' && (c.content || c.delta))
|
|
27
|
+
.map((c) => c.content)
|
|
28
|
+
.join('')
|
|
29
|
+
|
|
30
|
+
// Total raw chunks = sum of all chunkCounts
|
|
31
|
+
const totalRawChunks = () =>
|
|
32
|
+
props.chunks.reduce((sum, c) => sum + (c.chunkCount || 1), 0)
|
|
33
|
+
// Consolidated entries = number of entries in the chunks array
|
|
34
|
+
const consolidatedEntries = () => props.chunks.length
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<details class={styles().conversationDetails.messageGroupDetails}>
|
|
38
|
+
<summary class={styles().conversationDetails.messageGroupSummary}>
|
|
39
|
+
<div class={styles().conversationDetails.messageGroupContent}>
|
|
40
|
+
{/* Header */}
|
|
41
|
+
<div class={styles().conversationDetails.messageGroupHeader}>
|
|
42
|
+
<span>Message #{props.groupIndex + 1}</span>
|
|
43
|
+
<div
|
|
44
|
+
class={`${styles().conversationDetails.chunkBadge} ${styles().conversationDetails.chunkBadgeCount}`}
|
|
45
|
+
>
|
|
46
|
+
📦 {totalRawChunks()} chunks
|
|
47
|
+
<Show when={consolidatedEntries() !== totalRawChunks()}>
|
|
48
|
+
<span style={{ opacity: 0.7, 'margin-left': '4px' }}>
|
|
49
|
+
({consolidatedEntries()} entries)
|
|
50
|
+
</span>
|
|
51
|
+
</Show>
|
|
52
|
+
</div>
|
|
53
|
+
<ChunkBadges chunks={props.chunks} />
|
|
54
|
+
{/* Usage info */}
|
|
55
|
+
<Show when={props.usage}>
|
|
56
|
+
<div class={styles().conversationDetails.messageUsage}>
|
|
57
|
+
<span class={styles().conversationDetails.messageUsageIcon}>
|
|
58
|
+
🎯
|
|
59
|
+
</span>
|
|
60
|
+
<span>{props.usage?.promptTokens.toLocaleString()} in</span>
|
|
61
|
+
<span>•</span>
|
|
62
|
+
<span>
|
|
63
|
+
{props.usage?.completionTokens.toLocaleString()} out
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
|
+
</Show>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{/* Message ID */}
|
|
70
|
+
<div
|
|
71
|
+
class={styles().conversationDetails.messageId}
|
|
72
|
+
title={props.messageId}
|
|
73
|
+
>
|
|
74
|
+
ID: {props.messageId}
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
{/* Accumulated Content Preview */}
|
|
78
|
+
<Show when={accumulatedContent()}>
|
|
79
|
+
<div
|
|
80
|
+
class={styles().conversationDetails.contentPreview}
|
|
81
|
+
title={accumulatedContent()}
|
|
82
|
+
>
|
|
83
|
+
{accumulatedContent()}
|
|
84
|
+
</div>
|
|
85
|
+
</Show>
|
|
86
|
+
</div>
|
|
87
|
+
</summary>
|
|
88
|
+
|
|
89
|
+
{/* Chunks in this group */}
|
|
90
|
+
<div class={styles().conversationDetails.chunksContainer}>
|
|
91
|
+
<div class={styles().conversationDetails.chunksList}>
|
|
92
|
+
<For each={props.chunks}>
|
|
93
|
+
{(chunk, chunkIndex) => (
|
|
94
|
+
<ChunkItem chunk={chunk} index={chunkIndex()} variant="large" />
|
|
95
|
+
)}
|
|
96
|
+
</For>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</details>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { For, Show } from 'solid-js'
|
|
2
|
+
import { useStyles } from '../../styles/use-styles'
|
|
3
|
+
import { MessageCard } from './MessageCard'
|
|
4
|
+
import type { Component } from 'solid-js'
|
|
5
|
+
import type { Message } from '../../store/ai-store'
|
|
6
|
+
|
|
7
|
+
interface MessagesTabProps {
|
|
8
|
+
messages: Array<Message>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const MessagesTab: Component<MessagesTabProps> = (props) => {
|
|
12
|
+
const styles = useStyles()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Show
|
|
16
|
+
when={props.messages.length > 0}
|
|
17
|
+
fallback={
|
|
18
|
+
<div class={styles().conversationDetails.emptyMessages}>
|
|
19
|
+
No messages yet. Start a conversation to see messages here.
|
|
20
|
+
</div>
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
<div class={styles().conversationDetails.messagesList}>
|
|
24
|
+
<For each={props.messages}>
|
|
25
|
+
{(msg) => <MessageCard message={msg} />}
|
|
26
|
+
</For>
|
|
27
|
+
</div>
|
|
28
|
+
</Show>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { For, Show } from 'solid-js'
|
|
2
|
+
import { useStyles } from '../../styles/use-styles'
|
|
3
|
+
import { formatDuration } from '../utils'
|
|
4
|
+
import type { Component } from 'solid-js'
|
|
5
|
+
import type { SummarizeOperation } from '../../store/ai-context'
|
|
6
|
+
|
|
7
|
+
interface SummariesTabProps {
|
|
8
|
+
summaries: Array<SummarizeOperation>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const SummariesTab: Component<SummariesTabProps> = (props) => {
|
|
12
|
+
const styles = useStyles()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Show
|
|
16
|
+
when={props.summaries.length > 0}
|
|
17
|
+
fallback={
|
|
18
|
+
<div class={styles().conversationDetails.emptyMessages}>
|
|
19
|
+
No summarize operations yet
|
|
20
|
+
</div>
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
<div class={styles().conversationDetails.messagesList}>
|
|
24
|
+
<For each={props.summaries}>
|
|
25
|
+
{(summary) => (
|
|
26
|
+
<div class={styles().conversationDetails.operationCard}>
|
|
27
|
+
<div class={styles().conversationDetails.operationHeader}>
|
|
28
|
+
<div class={styles().conversationDetails.operationIcon}>📝</div>
|
|
29
|
+
<div class={styles().conversationDetails.operationTitle}>
|
|
30
|
+
Summarize
|
|
31
|
+
</div>
|
|
32
|
+
<div
|
|
33
|
+
class={`${styles().conversationDetails.operationStatus} ${
|
|
34
|
+
summary.status === 'completed'
|
|
35
|
+
? styles().conversationDetails.operationStatusCompleted
|
|
36
|
+
: styles().conversationDetails.operationStatusPending
|
|
37
|
+
}`}
|
|
38
|
+
>
|
|
39
|
+
{summary.status}
|
|
40
|
+
</div>
|
|
41
|
+
<Show
|
|
42
|
+
when={summary.duration !== undefined && summary.duration > 0}
|
|
43
|
+
>
|
|
44
|
+
<div class={styles().conversationDetails.durationBadge}>
|
|
45
|
+
⏱️ {formatDuration(summary.duration)}
|
|
46
|
+
</div>
|
|
47
|
+
</Show>
|
|
48
|
+
</div>
|
|
49
|
+
<div class={styles().conversationDetails.operationDetails}>
|
|
50
|
+
<div class={styles().conversationDetails.operationDetail}>
|
|
51
|
+
<span class={styles().conversationDetails.operationLabel}>
|
|
52
|
+
Model:
|
|
53
|
+
</span>
|
|
54
|
+
<span class={styles().conversationDetails.operationValue}>
|
|
55
|
+
{summary.model}
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
<div class={styles().conversationDetails.operationDetail}>
|
|
59
|
+
<span class={styles().conversationDetails.operationLabel}>
|
|
60
|
+
Input:
|
|
61
|
+
</span>
|
|
62
|
+
<span class={styles().conversationDetails.operationValue}>
|
|
63
|
+
{summary.inputLength.toLocaleString()} chars
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
|
+
<Show when={summary.outputLength !== undefined}>
|
|
67
|
+
<div class={styles().conversationDetails.operationDetail}>
|
|
68
|
+
<span class={styles().conversationDetails.operationLabel}>
|
|
69
|
+
Output:
|
|
70
|
+
</span>
|
|
71
|
+
<span class={styles().conversationDetails.operationValue}>
|
|
72
|
+
{summary.outputLength?.toLocaleString()} chars
|
|
73
|
+
</span>
|
|
74
|
+
</div>
|
|
75
|
+
</Show>
|
|
76
|
+
<div class={styles().conversationDetails.operationDetail}>
|
|
77
|
+
<span class={styles().conversationDetails.operationLabel}>
|
|
78
|
+
Started:
|
|
79
|
+
</span>
|
|
80
|
+
<span class={styles().conversationDetails.operationValue}>
|
|
81
|
+
{new Date(summary.timestamp).toLocaleTimeString()}
|
|
82
|
+
</span>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
</For>
|
|
88
|
+
</div>
|
|
89
|
+
</Show>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Show } from 'solid-js'
|
|
2
|
+
import { JsonTree } from '@tanstack/devtools-ui'
|
|
3
|
+
import { useStyles } from '../../styles/use-styles'
|
|
4
|
+
import { formatDuration } from '../utils'
|
|
5
|
+
import type { Component } from 'solid-js'
|
|
6
|
+
import type { ToolCall } from '../../store/ai-context'
|
|
7
|
+
|
|
8
|
+
interface ToolCallDisplayProps {
|
|
9
|
+
tool: ToolCall
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const ToolCallDisplay: Component<ToolCallDisplayProps> = (props) => {
|
|
13
|
+
const styles = useStyles()
|
|
14
|
+
const tool = () => props.tool
|
|
15
|
+
|
|
16
|
+
// Parse arguments if they're a string
|
|
17
|
+
const parsedArguments = () => {
|
|
18
|
+
const args = tool().arguments
|
|
19
|
+
if (typeof args === 'string') {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(args)
|
|
22
|
+
} catch {
|
|
23
|
+
return args
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return args
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<details
|
|
31
|
+
class={`${styles().conversationDetails.toolCall} ${
|
|
32
|
+
tool().approvalRequired
|
|
33
|
+
? styles().conversationDetails.toolCallApproval
|
|
34
|
+
: styles().conversationDetails.toolCallNormal
|
|
35
|
+
}`}
|
|
36
|
+
>
|
|
37
|
+
<summary class={styles().conversationDetails.toolCallHeader}>
|
|
38
|
+
<div
|
|
39
|
+
class={`${styles().conversationDetails.toolCallName} ${
|
|
40
|
+
tool().approvalRequired
|
|
41
|
+
? styles().conversationDetails.toolCallNameApproval
|
|
42
|
+
: styles().conversationDetails.toolCallNameNormal
|
|
43
|
+
}`}
|
|
44
|
+
>
|
|
45
|
+
{tool().approvalRequired ? '⚠️' : '🔧'} {tool().name}
|
|
46
|
+
</div>
|
|
47
|
+
<div
|
|
48
|
+
class={`${styles().conversationDetails.toolStateBadge} ${
|
|
49
|
+
tool().approvalRequired
|
|
50
|
+
? styles().conversationDetails.toolStateBadgeApproval
|
|
51
|
+
: styles().conversationDetails.toolStateBadgeNormal
|
|
52
|
+
}`}
|
|
53
|
+
>
|
|
54
|
+
{tool().state}
|
|
55
|
+
</div>
|
|
56
|
+
<Show when={tool().duration !== undefined && tool().duration! > 0}>
|
|
57
|
+
<div class={styles().conversationDetails.durationBadge}>
|
|
58
|
+
⏱️ {formatDuration(tool().duration)}
|
|
59
|
+
</div>
|
|
60
|
+
</Show>
|
|
61
|
+
<Show when={tool().approvalRequired}>
|
|
62
|
+
<div class={styles().conversationDetails.approvalRequiredBadge}>
|
|
63
|
+
APPROVAL REQUIRED
|
|
64
|
+
</div>
|
|
65
|
+
</Show>
|
|
66
|
+
</summary>
|
|
67
|
+
<div class={styles().conversationDetails.toolCallContent}>
|
|
68
|
+
<Show when={tool().arguments}>
|
|
69
|
+
<div class={styles().conversationDetails.toolSection}>
|
|
70
|
+
<div class={styles().conversationDetails.toolSectionLabel}>
|
|
71
|
+
Arguments
|
|
72
|
+
</div>
|
|
73
|
+
<div class={styles().conversationDetails.toolJsonContainer}>
|
|
74
|
+
<JsonTree
|
|
75
|
+
value={parsedArguments() as Record<string, unknown>}
|
|
76
|
+
defaultExpansionDepth={2}
|
|
77
|
+
copyable
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</Show>
|
|
82
|
+
<Show when={tool().result !== undefined}>
|
|
83
|
+
<div class={styles().conversationDetails.toolSection}>
|
|
84
|
+
<div class={styles().conversationDetails.toolSectionLabel}>
|
|
85
|
+
Result
|
|
86
|
+
</div>
|
|
87
|
+
<div class={styles().conversationDetails.toolJsonContainer}>
|
|
88
|
+
<JsonTree
|
|
89
|
+
value={tool().result as Record<string, unknown>}
|
|
90
|
+
defaultExpansionDepth={2}
|
|
91
|
+
copyable
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</Show>
|
|
96
|
+
</div>
|
|
97
|
+
</details>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { ConversationHeader } from './ConversationHeader'
|
|
2
|
+
export { ConversationTabs } from './ConversationTabs'
|
|
3
|
+
export type { TabType } from './ConversationTabs'
|
|
4
|
+
|
|
5
|
+
export { MessagesTab } from './MessagesTab'
|
|
6
|
+
export { ChunksTab } from './ChunksTab'
|
|
7
|
+
export { EmbeddingsTab } from './EmbeddingsTab'
|
|
8
|
+
export { SummariesTab } from './SummariesTab'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Show } from 'solid-js'
|
|
2
|
+
import { useStyles } from '../../styles/use-styles'
|
|
3
|
+
import { useAIStore } from '../../store/ai-context'
|
|
4
|
+
import { getStatusColor, getTypeColor } from '../utils'
|
|
5
|
+
import type { Conversation } from '../../store/ai-context'
|
|
6
|
+
import type { Component } from 'solid-js'
|
|
7
|
+
|
|
8
|
+
interface ConversationRowProps {
|
|
9
|
+
conversation: Conversation
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const ConversationRow: Component<ConversationRowProps> = (props) => {
|
|
13
|
+
const { state, selectConversation } = useAIStore()
|
|
14
|
+
const styles = useStyles()
|
|
15
|
+
const conv = () => props.conversation
|
|
16
|
+
|
|
17
|
+
const hasToolCalls = () => {
|
|
18
|
+
return conv().messages.some(
|
|
19
|
+
(msg) => msg.toolCalls && msg.toolCalls.length > 0,
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const countToolCalls = () => {
|
|
24
|
+
return conv().messages.reduce((total, msg) => {
|
|
25
|
+
return total + (msg.toolCalls?.length || 0)
|
|
26
|
+
}, 0)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Total raw chunks = sum of all chunkCounts from all chunks
|
|
30
|
+
const totalRawChunks = () => {
|
|
31
|
+
return conv().chunks.reduce((sum, c) => sum + (c.chunkCount || 1), 0)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
class={`${styles().utilRow} ${state.activeConversationId === conv().id ? styles().utilRowSelected : ''}`}
|
|
37
|
+
onClick={() => selectConversation(conv().id)}
|
|
38
|
+
>
|
|
39
|
+
<div class={styles().conversationsList.rowMain}>
|
|
40
|
+
<div class={styles().conversationsList.rowTop}>
|
|
41
|
+
<div class={styles().conversationsList.rowInfo}>
|
|
42
|
+
<div
|
|
43
|
+
class={styles().conversationsList.typeDot}
|
|
44
|
+
style={{ background: getTypeColor(conv().type) }}
|
|
45
|
+
/>
|
|
46
|
+
<div class={styles().conversationsList.label}>{conv().label}</div>
|
|
47
|
+
<Show when={hasToolCalls()}>
|
|
48
|
+
<div
|
|
49
|
+
class={styles().conversationsList.toolCallsBadge}
|
|
50
|
+
title={`${countToolCalls()} tool call${countToolCalls() !== 1 ? 's' : ''}`}
|
|
51
|
+
>
|
|
52
|
+
🔧 {countToolCalls()}
|
|
53
|
+
</div>
|
|
54
|
+
</Show>
|
|
55
|
+
</div>
|
|
56
|
+
<div class={styles().conversationsList.rowRight}>
|
|
57
|
+
<Show when={conv().status === 'active'}>
|
|
58
|
+
<div class={styles().conversationsList.loadingIndicator}>⟳</div>
|
|
59
|
+
</Show>
|
|
60
|
+
<div
|
|
61
|
+
class={styles().conversationsList.statusDot}
|
|
62
|
+
style={{ background: getStatusColor(conv().status) }}
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<div class={styles().conversationsList.stats}>
|
|
67
|
+
<div class={styles().conversationsList.statItem}>
|
|
68
|
+
💬 {conv().messages.length}
|
|
69
|
+
</div>
|
|
70
|
+
<div class={styles().conversationsList.statItem}>
|
|
71
|
+
📦 {totalRawChunks()}
|
|
72
|
+
</div>
|
|
73
|
+
<Show when={conv().usage}>
|
|
74
|
+
<div
|
|
75
|
+
class={styles().conversationsList.tokensBadge}
|
|
76
|
+
title={`Prompt: ${conv().usage?.promptTokens || 0} | Completion: ${conv().usage?.completionTokens || 0}`}
|
|
77
|
+
>
|
|
78
|
+
🎯 {conv().usage?.totalTokens.toLocaleString() || 0}
|
|
79
|
+
</div>
|
|
80
|
+
</Show>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ConversationRow } from './ConversationRow'
|