agentlytics 0.0.7 → 0.0.8
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/package.json +1 -1
- package/server.js +45 -0
- package/ui/src/lib/api.js +1 -1
- package/ui/src/pages/ChatDetail.jsx +13 -3
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -69,6 +69,51 @@ app.get('/api/chats/:id', (req, res) => {
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
app.get('/api/chats/:id/markdown', (req, res) => {
|
|
73
|
+
try {
|
|
74
|
+
const result = cache.getCachedChat(req.params.id);
|
|
75
|
+
if (!result) return res.status(404).json({ error: 'Chat not found' });
|
|
76
|
+
|
|
77
|
+
const lines = [];
|
|
78
|
+
const title = result.name || 'Untitled Session';
|
|
79
|
+
lines.push(`# ${title}\n`);
|
|
80
|
+
|
|
81
|
+
// Metadata
|
|
82
|
+
const meta = [];
|
|
83
|
+
if (result.source) meta.push(`**Editor:** ${result.source}`);
|
|
84
|
+
if (result.mode) meta.push(`**Mode:** ${result.mode}`);
|
|
85
|
+
if (result.folder) meta.push(`**Project:** ${result.folder}`);
|
|
86
|
+
if (result.createdAt) meta.push(`**Created:** ${new Date(result.createdAt).toISOString()}`);
|
|
87
|
+
if (result.lastUpdatedAt) meta.push(`**Updated:** ${new Date(result.lastUpdatedAt).toISOString()}`);
|
|
88
|
+
if (result.stats) {
|
|
89
|
+
meta.push(`**Messages:** ${result.stats.totalMessages}`);
|
|
90
|
+
if (result.stats.totalInputTokens) meta.push(`**Input Tokens:** ${result.stats.totalInputTokens}`);
|
|
91
|
+
if (result.stats.totalOutputTokens) meta.push(`**Output Tokens:** ${result.stats.totalOutputTokens}`);
|
|
92
|
+
const models = [...new Set(result.stats.models || [])];
|
|
93
|
+
if (models.length > 0) meta.push(`**Models:** ${models.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
if (meta.length > 0) lines.push(meta.join(' \n') + '\n');
|
|
96
|
+
|
|
97
|
+
lines.push('---\n');
|
|
98
|
+
|
|
99
|
+
// Messages
|
|
100
|
+
for (const msg of result.messages) {
|
|
101
|
+
const label = msg.role === 'user' ? '## User' : msg.role === 'assistant' ? '## Assistant' : `## ${msg.role.charAt(0).toUpperCase() + msg.role.slice(1)}`;
|
|
102
|
+
const modelTag = msg.model ? ` *(${msg.model})*` : '';
|
|
103
|
+
lines.push(`${label}${modelTag}\n`);
|
|
104
|
+
lines.push(msg.content + '\n');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const md = lines.join('\n');
|
|
108
|
+
const filename = title.replace(/[^a-zA-Z0-9_-]/g, '_').substring(0, 80) + '.md';
|
|
109
|
+
res.setHeader('Content-Type', 'text/markdown; charset=utf-8');
|
|
110
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
111
|
+
res.send(md);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
res.status(500).json({ error: err.message });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
72
117
|
app.get('/api/projects', (req, res) => {
|
|
73
118
|
try {
|
|
74
119
|
res.json(cache.getCachedProjects());
|
package/ui/src/lib/api.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react'
|
|
2
2
|
import { useParams, useNavigate } from 'react-router-dom'
|
|
3
|
-
import { ArrowLeft, User, Bot, Wrench, Settings, Play, CheckCircle, ChevronRight, ChevronDown } from 'lucide-react'
|
|
3
|
+
import { ArrowLeft, User, Bot, Wrench, Settings, Play, CheckCircle, ChevronRight, ChevronDown, Download } from 'lucide-react'
|
|
4
4
|
import ReactMarkdown from 'react-markdown'
|
|
5
5
|
import remarkGfm from 'remark-gfm'
|
|
6
|
-
import { fetchChat } from '../lib/api'
|
|
6
|
+
import { fetchChat, BASE } from '../lib/api'
|
|
7
7
|
import { editorColor, editorLabel, formatDateTime, formatNumber } from '../lib/constants'
|
|
8
8
|
import KpiCard from '../components/KpiCard'
|
|
9
9
|
|
|
@@ -184,7 +184,7 @@ export default function ChatDetail() {
|
|
|
184
184
|
{/* Header */}
|
|
185
185
|
<div className="card p-5 mb-4">
|
|
186
186
|
<div className="flex items-start justify-between">
|
|
187
|
-
<div>
|
|
187
|
+
<div className="flex-1 min-w-0">
|
|
188
188
|
<h2 className="text-xl font-semibold mb-1" style={{ color: 'var(--c-white)' }}>{chat.name || '(untitled)'}</h2>
|
|
189
189
|
<div className="flex items-center gap-3 text-xs" style={{ color: 'var(--c-text2)' }}>
|
|
190
190
|
<span className="inline-flex items-center gap-1.5">
|
|
@@ -200,6 +200,16 @@ export default function ChatDetail() {
|
|
|
200
200
|
<span className="ml-3 font-mono" style={{ color: 'var(--c-text3)' }}>{chat.id}</span>
|
|
201
201
|
</div>
|
|
202
202
|
</div>
|
|
203
|
+
<a
|
|
204
|
+
href={`${BASE}/api/chats/${chat.id}/markdown`}
|
|
205
|
+
download
|
|
206
|
+
className="flex items-center gap-1.5 px-3 py-1.5 rounded text-xs font-medium transition shrink-0"
|
|
207
|
+
style={{ background: 'var(--c-bg3)', color: 'var(--c-text)', border: '1px solid var(--c-border)' }}
|
|
208
|
+
onMouseEnter={e => e.currentTarget.style.background = 'var(--c-bg2)'}
|
|
209
|
+
onMouseLeave={e => e.currentTarget.style.background = 'var(--c-bg3)'}
|
|
210
|
+
>
|
|
211
|
+
<Download size={13} /> Export .md
|
|
212
|
+
</a>
|
|
203
213
|
</div>
|
|
204
214
|
</div>
|
|
205
215
|
|