copilot-chat-analyzer 0.0.2 → 0.0.4
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/AI_USAGE.md +93 -0
- package/LICENSE +1 -1
- package/README.md +90 -7
- package/dist/index.cjs +290 -10
- package/dist/index.d.cts +123 -1
- package/dist/index.d.ts +123 -1
- package/dist/index.mjs +290 -10
- package/package.json +43 -4
package/AI_USAGE.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# AI Usage Guide for copilot-chat-analyzer
|
|
2
|
+
|
|
3
|
+
This is a TypeScript/JavaScript library for analyzing GitHub Copilot chat exports.
|
|
4
|
+
|
|
5
|
+
## Quick Start for AI Assistants
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import CopilotChatAnalyzer, { DialogStatus } from "copilot-chat-analyzer";
|
|
9
|
+
import { readFileSync } from "fs";
|
|
10
|
+
|
|
11
|
+
// Load chat data from JSON export
|
|
12
|
+
const chatData = JSON.parse(readFileSync("chat.json", "utf8"));
|
|
13
|
+
const analyzer = new CopilotChatAnalyzer();
|
|
14
|
+
|
|
15
|
+
// Basic analysis
|
|
16
|
+
const status = analyzer.getDialogStatus(chatData);
|
|
17
|
+
const requestCount = analyzer.getRequestsCount(chatData);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Core Methods
|
|
21
|
+
|
|
22
|
+
### getDialogStatus(chatData)
|
|
23
|
+
|
|
24
|
+
Returns one of four status strings:
|
|
25
|
+
|
|
26
|
+
- `"pending"` - Chat created but no requests made (empty requests array)
|
|
27
|
+
- `"in_progress"` - Chat has requests but not completed
|
|
28
|
+
- `"completed"` - Chat finished successfully (has followups: [])
|
|
29
|
+
- `"canceled"` - Chat was canceled (isCanceled: true)
|
|
30
|
+
|
|
31
|
+
### getRequestsCount(chatData)
|
|
32
|
+
|
|
33
|
+
Returns number of requests in the chat.
|
|
34
|
+
|
|
35
|
+
### getDialogStatusDetails(chatData)
|
|
36
|
+
|
|
37
|
+
Returns detailed object:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
{
|
|
41
|
+
status: DialogStatusType,
|
|
42
|
+
statusText: string, // Human readable status
|
|
43
|
+
hasResult: boolean, // Has result data
|
|
44
|
+
hasFollowups: boolean, // Has followups property
|
|
45
|
+
isCanceled: boolean, // Was canceled
|
|
46
|
+
lastRequestId?: string // ID of last request
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### getMcpToolMonitoring(chatData, toolName?)
|
|
51
|
+
|
|
52
|
+
Monitor MCP (Model Context Protocol) tool usage:
|
|
53
|
+
|
|
54
|
+
- Without toolName: Returns summary of all tools
|
|
55
|
+
- With toolName: Returns detailed stats for specific tool
|
|
56
|
+
|
|
57
|
+
## Chat Data Structure
|
|
58
|
+
|
|
59
|
+
Expected JSON structure from GitHub Copilot export:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
{
|
|
63
|
+
requesterUsername: string,
|
|
64
|
+
responderUsername: string,
|
|
65
|
+
requests: Array<{
|
|
66
|
+
requestId: string,
|
|
67
|
+
isCanceled?: boolean,
|
|
68
|
+
followups?: any[],
|
|
69
|
+
result?: any,
|
|
70
|
+
response?: any[]
|
|
71
|
+
}>
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Status Detection Logic
|
|
76
|
+
|
|
77
|
+
1. No requests or empty array → `"pending"`
|
|
78
|
+
2. Last request has `isCanceled: true` → `"canceled"`
|
|
79
|
+
3. Last request has `followups: []` (empty array) → `"completed"`
|
|
80
|
+
4. Otherwise → `"in_progress"`
|
|
81
|
+
|
|
82
|
+
## MCP Tool Analysis
|
|
83
|
+
|
|
84
|
+
The library can extract and analyze Model Context Protocol tool calls from chat responses, providing:
|
|
85
|
+
|
|
86
|
+
- Tool success rates
|
|
87
|
+
- Call counts
|
|
88
|
+
- Input/output data
|
|
89
|
+
- Error tracking
|
|
90
|
+
|
|
91
|
+
## Examples Directory
|
|
92
|
+
|
|
93
|
+
Check `/examples` folder for complete usage examples with sample data.
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -4,14 +4,44 @@
|
|
|
4
4
|
[](https://github.com/dealenx/copilot-chat-analyzer/actions/workflows/quality.yml)
|
|
5
5
|
[](https://badge.fury.io/js/copilot-chat-analyzer)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
TypeScript библиотека для **анализа** экспортированных чатов GitHub Copilot с поддержкой мониторинга MCP инструментов.
|
|
8
|
+
|
|
9
|
+
> **⚠️ Важно**: Эта библиотека предназначена только для **анализа** уже экспортированных JSON-файлов чата. Она НЕ экспортирует данные чата из VS Code. Для экспорта используйте команду VS Code `workbench.action.chat.export`.
|
|
10
|
+
|
|
11
|
+
## 🤖 AI Assistant Quick Reference
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// Basic import and usage
|
|
15
|
+
import CopilotChatAnalyzer, { DialogStatus } from "copilot-chat-analyzer";
|
|
16
|
+
|
|
17
|
+
const analyzer = new CopilotChatAnalyzer();
|
|
18
|
+
const chatData = JSON.parse(fs.readFileSync("chat.json", "utf8"));
|
|
19
|
+
|
|
20
|
+
// Core methods:
|
|
21
|
+
analyzer.getDialogStatus(chatData); // Returns: 'pending' | 'in_progress' | 'completed' | 'canceled' | 'failed'
|
|
22
|
+
analyzer.getRequestsCount(chatData); // Returns: number
|
|
23
|
+
analyzer.getDialogStatusDetails(chatData); // Returns: { status, statusText, hasResult, hasFollowups, isCanceled, isFailed, lastRequestId, errorCode?, errorMessage? }
|
|
24
|
+
analyzer.getSessionId(chatData); // Returns: string | null - Extract session ID from chat
|
|
25
|
+
analyzer.getSessionInfo(chatData); // Returns: { sessionId, agentId?, modelId? } | null
|
|
26
|
+
analyzer.getMcpToolMonitoring(chatData); // Returns: MCP tool usage statistics
|
|
27
|
+
|
|
28
|
+
// Dialog statuses:
|
|
29
|
+
// - 'pending': Empty requests array, chat not started
|
|
30
|
+
// - 'in_progress': Has requests but not finished
|
|
31
|
+
// - 'completed': Has followups:[] and not canceled
|
|
32
|
+
// - 'canceled': isCanceled:true in last request
|
|
33
|
+
// - 'failed': Has result.errorDetails in last request (API error)
|
|
34
|
+
```
|
|
8
35
|
|
|
9
36
|
## Особенности
|
|
10
37
|
|
|
11
|
-
- 📊
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
38
|
+
- 📊 **Анализ статусов диалога** - автоматическое определение состояния чата
|
|
39
|
+
- 🔢 **Подсчет запросов** - точный подсчет количества запросов в диалоге
|
|
40
|
+
- 🔍 **Детальная диагностика** - получение развернутой информации о статусе
|
|
41
|
+
- �️ **MCP мониторинг** - отслеживание использования Model Context Protocol инструментов
|
|
42
|
+
- 📈 **Статистика успешности** - анализ успешности выполнения MCP вызовов
|
|
43
|
+
- 🚀 **Простой API** - интуитивно понятный интерфейс
|
|
44
|
+
- 💪 **TypeScript** - полная поддержка типов
|
|
15
45
|
|
|
16
46
|
## Установка
|
|
17
47
|
|
|
@@ -59,7 +89,7 @@ console.log(`Количество запросов: ${requestsCount}`);
|
|
|
59
89
|
|
|
60
90
|
// Определение статуса диалога
|
|
61
91
|
const status = analyzer.getDialogStatus(chatData);
|
|
62
|
-
console.log(`Статус: ${status}`); // 'completed', 'canceled', 'in_progress'
|
|
92
|
+
console.log(`Статус: ${status}`); // 'pending', 'completed', 'canceled', 'in_progress'
|
|
63
93
|
|
|
64
94
|
// Получение детальной информации о статусе
|
|
65
95
|
const details = analyzer.getDialogStatusDetails(chatData);
|
|
@@ -73,10 +103,43 @@ console.log({
|
|
|
73
103
|
});
|
|
74
104
|
```
|
|
75
105
|
|
|
106
|
+
### Мониторинг MCP инструментов
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
// Получить список всех MCP инструментов в чате
|
|
110
|
+
const toolNames = analyzer.getMcpToolNames(chatData);
|
|
111
|
+
console.log("Инструменты:", toolNames);
|
|
112
|
+
|
|
113
|
+
// Получить все вызовы конкретного инструмента
|
|
114
|
+
const calls = analyzer.getMcpToolCalls(chatData, "update_entry_fields");
|
|
115
|
+
calls.forEach((call, i) => {
|
|
116
|
+
console.log(
|
|
117
|
+
`${i + 1}. ${call.isError ? "❌ Ошибка" : "✅ Успех"}: ${JSON.stringify(
|
|
118
|
+
call.input
|
|
119
|
+
)}`
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Получить только успешные или только ошибочные вызовы
|
|
124
|
+
const successCalls = analyzer.getMcpToolSuccessfulCalls(
|
|
125
|
+
chatData,
|
|
126
|
+
"update_entry_fields"
|
|
127
|
+
);
|
|
128
|
+
const errorCalls = analyzer.getMcpToolErrorCalls(
|
|
129
|
+
chatData,
|
|
130
|
+
"update_entry_fields"
|
|
131
|
+
);
|
|
132
|
+
```
|
|
133
|
+
|
|
76
134
|
## Статусы диалога
|
|
77
135
|
|
|
78
136
|
Библиотека автоматически определяет текущий статус чата при экспорте:
|
|
79
137
|
|
|
138
|
+
- **`DialogStatus.PENDING`** (`"pending"`) - Диалог еще не начат
|
|
139
|
+
|
|
140
|
+
- Массив `requests` пустой или отсутствует
|
|
141
|
+
- Еще не было сделано ни одного запроса к Copilot
|
|
142
|
+
|
|
80
143
|
- **`DialogStatus.COMPLETED`** (`"completed"`) - Диалог завершен успешно
|
|
81
144
|
|
|
82
145
|
- Есть поле `followups: []` (пустой массив)
|
|
@@ -88,11 +151,31 @@ console.log({
|
|
|
88
151
|
- `isCanceled: true`
|
|
89
152
|
- Может быть с `followups: []` или без него
|
|
90
153
|
|
|
154
|
+
- **`DialogStatus.FAILED`** (`"failed"`) - Диалог завершился с ошибкой
|
|
155
|
+
|
|
156
|
+
- Есть поле `result.errorDetails` в последнем запросе
|
|
157
|
+
- Содержит `errorCode` и `errorMessage` в деталях статуса
|
|
158
|
+
- Обычно указывает на ошибку API или аутентификации
|
|
159
|
+
|
|
91
160
|
- **`DialogStatus.IN_PROGRESS`** (`"in_progress"`) - Диалог в процессе
|
|
92
161
|
- Отсутствует поле `followups`
|
|
93
162
|
- `isCanceled: false`
|
|
94
163
|
- **Такой статус получается при экспорте во время диалога**
|
|
95
164
|
|
|
165
|
+
## Извлечение информации о сессии
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
// Получить ID сессии (уникален для каждого диалога)
|
|
169
|
+
const sessionId = analyzer.getSessionId(chatData);
|
|
170
|
+
console.log(`Session ID: ${sessionId}`);
|
|
171
|
+
// Пример: "ff72bca6-0dec-4953-b130-a103a97e5380"
|
|
172
|
+
|
|
173
|
+
// Получить полную информацию о сессии
|
|
174
|
+
const sessionInfo = analyzer.getSessionInfo(chatData);
|
|
175
|
+
console.log(sessionInfo);
|
|
176
|
+
// { sessionId: "ff72bca6-...", agentId: "github.copilot.editsAgent", modelId: "copilot/gemini-2.5-pro" }
|
|
177
|
+
```
|
|
178
|
+
|
|
96
179
|
## Примеры использования
|
|
97
180
|
|
|
98
181
|
Смотрите файлы в папке `examples/parse-export-chat-json/`:
|
|
@@ -158,7 +241,7 @@ interface CopilotChatData {
|
|
|
158
241
|
[key: string]: any;
|
|
159
242
|
}
|
|
160
243
|
|
|
161
|
-
type DialogStatusType = "completed" | "canceled" | "in_progress";
|
|
244
|
+
type DialogStatusType = "pending" | "completed" | "canceled" | "in_progress";
|
|
162
245
|
|
|
163
246
|
interface DialogStatusDetails {
|
|
164
247
|
status: DialogStatusType;
|
package/dist/index.cjs
CHANGED
|
@@ -26,9 +26,11 @@ __export(index_exports, {
|
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
28
|
var DialogStatus = {
|
|
29
|
+
PENDING: "pending",
|
|
29
30
|
COMPLETED: "completed",
|
|
30
31
|
CANCELED: "canceled",
|
|
31
|
-
IN_PROGRESS: "in_progress"
|
|
32
|
+
IN_PROGRESS: "in_progress",
|
|
33
|
+
FAILED: "failed"
|
|
32
34
|
};
|
|
33
35
|
var CopilotChatAnalyzer = class {
|
|
34
36
|
getRequestsCount(chatData) {
|
|
@@ -37,6 +39,43 @@ var CopilotChatAnalyzer = class {
|
|
|
37
39
|
}
|
|
38
40
|
return chatData.requests.length;
|
|
39
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Extract sessionId from chat data.
|
|
44
|
+
* The sessionId is stored in result.metadata.sessionId of any request.
|
|
45
|
+
* All requests in a dialog share the same sessionId.
|
|
46
|
+
*/
|
|
47
|
+
getSessionId(chatData) {
|
|
48
|
+
if (!chatData?.requests?.length) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
for (const request of chatData.requests) {
|
|
52
|
+
const sessionId = request?.result?.metadata?.sessionId;
|
|
53
|
+
if (sessionId && typeof sessionId === "string") {
|
|
54
|
+
return sessionId;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get full session information including agentId and modelId.
|
|
61
|
+
*/
|
|
62
|
+
getSessionInfo(chatData) {
|
|
63
|
+
const sessionId = this.getSessionId(chatData);
|
|
64
|
+
if (!sessionId) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
for (const request of chatData.requests) {
|
|
68
|
+
const metadata = request?.result?.metadata;
|
|
69
|
+
if (metadata?.sessionId === sessionId) {
|
|
70
|
+
return {
|
|
71
|
+
sessionId,
|
|
72
|
+
agentId: metadata?.agentId,
|
|
73
|
+
modelId: metadata?.modelId
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { sessionId };
|
|
78
|
+
}
|
|
40
79
|
hasRequests(chatData) {
|
|
41
80
|
return Array.isArray(chatData.requests) && chatData.requests.length > 0;
|
|
42
81
|
}
|
|
@@ -48,15 +87,22 @@ var CopilotChatAnalyzer = class {
|
|
|
48
87
|
}
|
|
49
88
|
getDialogStatus(chatData) {
|
|
50
89
|
if (!this.hasRequests(chatData)) {
|
|
51
|
-
return DialogStatus.
|
|
90
|
+
return DialogStatus.PENDING;
|
|
52
91
|
}
|
|
53
92
|
const lastRequest = this.getLastRequest(chatData);
|
|
54
93
|
if (!lastRequest) {
|
|
55
|
-
return DialogStatus.
|
|
94
|
+
return DialogStatus.PENDING;
|
|
56
95
|
}
|
|
57
96
|
if (lastRequest.isCanceled === true) {
|
|
58
97
|
return DialogStatus.CANCELED;
|
|
59
98
|
}
|
|
99
|
+
const errorDetails = lastRequest?.result?.errorDetails;
|
|
100
|
+
if (errorDetails) {
|
|
101
|
+
if (errorDetails.code === "canceled") {
|
|
102
|
+
return DialogStatus.CANCELED;
|
|
103
|
+
}
|
|
104
|
+
return DialogStatus.FAILED;
|
|
105
|
+
}
|
|
60
106
|
if ("followups" in lastRequest && Array.isArray(lastRequest.followups)) {
|
|
61
107
|
if (lastRequest.followups.length === 0) {
|
|
62
108
|
return DialogStatus.COMPLETED;
|
|
@@ -71,18 +117,22 @@ var CopilotChatAnalyzer = class {
|
|
|
71
117
|
const status = this.getDialogStatus(chatData);
|
|
72
118
|
if (!this.hasRequests(chatData)) {
|
|
73
119
|
return {
|
|
74
|
-
status: DialogStatus.
|
|
75
|
-
statusText: "
|
|
120
|
+
status: DialogStatus.PENDING,
|
|
121
|
+
statusText: "Dialog not started",
|
|
76
122
|
hasResult: false,
|
|
77
123
|
hasFollowups: false,
|
|
78
|
-
isCanceled: false
|
|
124
|
+
isCanceled: false,
|
|
125
|
+
isFailed: false
|
|
79
126
|
};
|
|
80
127
|
}
|
|
81
128
|
const lastRequest = this.getLastRequest(chatData);
|
|
129
|
+
const errorDetails = lastRequest?.result?.errorDetails;
|
|
82
130
|
const statusTexts = {
|
|
83
|
-
[DialogStatus.
|
|
84
|
-
[DialogStatus.
|
|
85
|
-
[DialogStatus.
|
|
131
|
+
[DialogStatus.PENDING]: "Dialog not started",
|
|
132
|
+
[DialogStatus.COMPLETED]: "Dialog completed successfully",
|
|
133
|
+
[DialogStatus.CANCELED]: "Dialog was canceled",
|
|
134
|
+
[DialogStatus.IN_PROGRESS]: "Dialog in progress",
|
|
135
|
+
[DialogStatus.FAILED]: "Dialog failed with error"
|
|
86
136
|
};
|
|
87
137
|
return {
|
|
88
138
|
status,
|
|
@@ -90,9 +140,239 @@ var CopilotChatAnalyzer = class {
|
|
|
90
140
|
hasResult: lastRequest && "result" in lastRequest && lastRequest.result !== null,
|
|
91
141
|
hasFollowups: lastRequest && "followups" in lastRequest,
|
|
92
142
|
isCanceled: lastRequest && lastRequest.isCanceled === true,
|
|
93
|
-
|
|
143
|
+
isFailed: !!errorDetails,
|
|
144
|
+
lastRequestId: lastRequest?.requestId,
|
|
145
|
+
errorCode: errorDetails?.code,
|
|
146
|
+
errorMessage: errorDetails?.message
|
|
94
147
|
};
|
|
95
148
|
}
|
|
149
|
+
extractMcpToolCalls(chatData) {
|
|
150
|
+
if (!this.hasRequests(chatData)) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
const toolCalls = [];
|
|
154
|
+
chatData.requests.forEach((request) => {
|
|
155
|
+
if (!request.response || !Array.isArray(request.response)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
request.response.forEach((responseItem) => {
|
|
159
|
+
if (responseItem.kind === "toolInvocationSerialized" && responseItem.source?.type === "mcp") {
|
|
160
|
+
const toolCall = {
|
|
161
|
+
toolId: responseItem.toolId || responseItem.toolName || "unknown",
|
|
162
|
+
toolName: responseItem.toolName || responseItem.toolId || "unknown",
|
|
163
|
+
requestId: request.requestId,
|
|
164
|
+
input: responseItem.resultDetails?.input || responseItem.toolSpecificData?.rawInput || null,
|
|
165
|
+
output: responseItem.resultDetails?.output || null,
|
|
166
|
+
isError: responseItem.resultDetails?.isError || false,
|
|
167
|
+
timestamp: request.timestamp,
|
|
168
|
+
source: responseItem.source
|
|
169
|
+
};
|
|
170
|
+
toolCalls.push(toolCall);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
return toolCalls;
|
|
175
|
+
}
|
|
176
|
+
getMcpToolMonitoring(chatData, toolName) {
|
|
177
|
+
const allToolCalls = this.extractMcpToolCalls(chatData);
|
|
178
|
+
if (toolName) {
|
|
179
|
+
const toolCalls = allToolCalls.filter(
|
|
180
|
+
(call) => call.toolName.includes(toolName) || call.toolId.includes(toolName)
|
|
181
|
+
);
|
|
182
|
+
const successfulCalls = toolCalls.filter((call) => !call.isError).length;
|
|
183
|
+
const errorCalls = toolCalls.filter((call) => call.isError).length;
|
|
184
|
+
const successRate = toolCalls.length > 0 ? successfulCalls / toolCalls.length * 100 : 0;
|
|
185
|
+
return {
|
|
186
|
+
toolName,
|
|
187
|
+
totalCalls: toolCalls.length,
|
|
188
|
+
successfulCalls,
|
|
189
|
+
errorCalls,
|
|
190
|
+
successRate: Math.round(successRate * 100) / 100,
|
|
191
|
+
calls: toolCalls
|
|
192
|
+
};
|
|
193
|
+
} else {
|
|
194
|
+
const toolsMap = /* @__PURE__ */ new Map();
|
|
195
|
+
allToolCalls.forEach((call) => {
|
|
196
|
+
const key = call.toolName || call.toolId;
|
|
197
|
+
if (!toolsMap.has(key)) {
|
|
198
|
+
toolsMap.set(key, []);
|
|
199
|
+
}
|
|
200
|
+
toolsMap.get(key).push(call);
|
|
201
|
+
});
|
|
202
|
+
const tools = Array.from(toolsMap.entries()).map(
|
|
203
|
+
([toolName2, calls]) => {
|
|
204
|
+
const successfulCalls = calls.filter((call) => !call.isError).length;
|
|
205
|
+
const errorCalls = calls.filter((call) => call.isError).length;
|
|
206
|
+
const successRate = calls.length > 0 ? successfulCalls / calls.length * 100 : 0;
|
|
207
|
+
return {
|
|
208
|
+
toolName: toolName2,
|
|
209
|
+
totalCalls: calls.length,
|
|
210
|
+
successfulCalls,
|
|
211
|
+
errorCalls,
|
|
212
|
+
successRate: Math.round(successRate * 100) / 100,
|
|
213
|
+
calls
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
const totalCalls = allToolCalls.length;
|
|
218
|
+
const totalSuccessful = allToolCalls.filter(
|
|
219
|
+
(call) => !call.isError
|
|
220
|
+
).length;
|
|
221
|
+
const overallSuccessRate = totalCalls > 0 ? totalSuccessful / totalCalls * 100 : 0;
|
|
222
|
+
return {
|
|
223
|
+
totalTools: tools.length,
|
|
224
|
+
totalCalls,
|
|
225
|
+
overallSuccessRate: Math.round(overallSuccessRate * 100) / 100,
|
|
226
|
+
tools
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
getMcpToolSuccessfulCalls(chatData, toolName) {
|
|
231
|
+
const monitoring = this.getMcpToolMonitoring(
|
|
232
|
+
chatData,
|
|
233
|
+
toolName
|
|
234
|
+
);
|
|
235
|
+
return monitoring.calls.filter((call) => !call.isError);
|
|
236
|
+
}
|
|
237
|
+
getMcpToolErrorCalls(chatData, toolName) {
|
|
238
|
+
const monitoring = this.getMcpToolMonitoring(
|
|
239
|
+
chatData,
|
|
240
|
+
toolName
|
|
241
|
+
);
|
|
242
|
+
return monitoring.calls.filter((call) => call.isError);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Простой метод для получения всех вызовов конкретного MCP инструмента
|
|
246
|
+
* @param chatData - данные чата
|
|
247
|
+
* @param toolName - название инструмента (например, 'update_entry_fields')
|
|
248
|
+
* @returns массив всех вызовов инструмента
|
|
249
|
+
*/
|
|
250
|
+
getMcpToolCalls(chatData, toolName) {
|
|
251
|
+
const allToolCalls = this.extractMcpToolCalls(chatData);
|
|
252
|
+
return allToolCalls.filter(
|
|
253
|
+
(call) => call.toolName.includes(toolName) || call.toolId.includes(toolName)
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Получить список всех уникальных MCP инструментов, использованных в чате
|
|
258
|
+
* @param chatData - данные чата
|
|
259
|
+
* @returns массив названий инструментов
|
|
260
|
+
*/
|
|
261
|
+
getMcpToolNames(chatData) {
|
|
262
|
+
const allToolCalls = this.extractMcpToolCalls(chatData);
|
|
263
|
+
const uniqueNames = /* @__PURE__ */ new Set();
|
|
264
|
+
allToolCalls.forEach((call) => {
|
|
265
|
+
uniqueNames.add(call.toolName || call.toolId);
|
|
266
|
+
});
|
|
267
|
+
return Array.from(uniqueNames);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Получить историю запросов пользователя из данных чата
|
|
271
|
+
* @param chatData - данные чата Copilot
|
|
272
|
+
* @returns массив запросов пользователя с текстом сообщения
|
|
273
|
+
*/
|
|
274
|
+
getUserRequests(chatData) {
|
|
275
|
+
if (!chatData || !Array.isArray(chatData.requests)) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
const userRequests = [];
|
|
279
|
+
chatData.requests.forEach((request, index) => {
|
|
280
|
+
let messageText;
|
|
281
|
+
if (typeof request.message === "string") {
|
|
282
|
+
messageText = request.message;
|
|
283
|
+
} else if (request.message && typeof request.message.text === "string") {
|
|
284
|
+
messageText = request.message.text;
|
|
285
|
+
}
|
|
286
|
+
if (messageText) {
|
|
287
|
+
userRequests.push({
|
|
288
|
+
id: request.variableData?.requestId || request.requestId || `req-${index}`,
|
|
289
|
+
message: messageText,
|
|
290
|
+
timestamp: request.timestamp,
|
|
291
|
+
index
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
return userRequests;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Extract response text from a request object
|
|
299
|
+
* Aggregates text from response[] and toolCallRounds[].response
|
|
300
|
+
*/
|
|
301
|
+
extractResponseText(request) {
|
|
302
|
+
const parts = [];
|
|
303
|
+
if (Array.isArray(request.response)) {
|
|
304
|
+
for (const item of request.response) {
|
|
305
|
+
if (typeof item === "string") {
|
|
306
|
+
parts.push(item);
|
|
307
|
+
} else if (item.value && typeof item.value === "string") {
|
|
308
|
+
parts.push(item.value);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const toolCallRounds = request.result?.metadata?.toolCallRounds;
|
|
313
|
+
if (Array.isArray(toolCallRounds)) {
|
|
314
|
+
for (const round of toolCallRounds) {
|
|
315
|
+
if (round.response && typeof round.response === "string") {
|
|
316
|
+
if (!parts.includes(round.response)) {
|
|
317
|
+
parts.push(round.response);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return parts.join("\n\n");
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Count total tool calls in a request
|
|
326
|
+
*/
|
|
327
|
+
countToolCalls(request) {
|
|
328
|
+
const toolCallRounds = request.result?.metadata?.toolCallRounds;
|
|
329
|
+
if (!Array.isArray(toolCallRounds)) return 0;
|
|
330
|
+
return toolCallRounds.reduce((count, round) => {
|
|
331
|
+
return count + (round.toolCalls?.length || 0);
|
|
332
|
+
}, 0);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Get AI responses from chat data
|
|
336
|
+
* @param chatData - Copilot chat data
|
|
337
|
+
* @returns array of AI responses
|
|
338
|
+
*/
|
|
339
|
+
getAIResponses(chatData) {
|
|
340
|
+
if (!chatData || !Array.isArray(chatData.requests)) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const aiResponses = [];
|
|
344
|
+
chatData.requests.forEach((request, index) => {
|
|
345
|
+
const message = this.extractResponseText(request);
|
|
346
|
+
const toolCallCount = this.countToolCalls(request);
|
|
347
|
+
aiResponses.push({
|
|
348
|
+
requestId: request.requestId || `req-${index}`,
|
|
349
|
+
responseId: request.responseId,
|
|
350
|
+
message,
|
|
351
|
+
timestamp: request.timestamp,
|
|
352
|
+
index,
|
|
353
|
+
hasToolCalls: toolCallCount > 0,
|
|
354
|
+
toolCallCount
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
return aiResponses;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get full conversation history with paired requests and responses
|
|
361
|
+
* @param chatData - Copilot chat data
|
|
362
|
+
* @returns array of conversation turns (request + response pairs)
|
|
363
|
+
*/
|
|
364
|
+
getConversationHistory(chatData) {
|
|
365
|
+
const userRequests = this.getUserRequests(chatData);
|
|
366
|
+
const aiResponses = this.getAIResponses(chatData);
|
|
367
|
+
return userRequests.map((request, index) => {
|
|
368
|
+
const response = aiResponses.find((r) => r.index === index) || null;
|
|
369
|
+
return {
|
|
370
|
+
index,
|
|
371
|
+
request,
|
|
372
|
+
response
|
|
373
|
+
};
|
|
374
|
+
});
|
|
375
|
+
}
|
|
96
376
|
};
|
|
97
377
|
var index_default = CopilotChatAnalyzer;
|
|
98
378
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -8,20 +8,142 @@ interface DialogStatusDetails {
|
|
|
8
8
|
hasResult: boolean;
|
|
9
9
|
hasFollowups: boolean;
|
|
10
10
|
isCanceled: boolean;
|
|
11
|
+
isFailed: boolean;
|
|
11
12
|
lastRequestId?: string;
|
|
13
|
+
errorCode?: string;
|
|
14
|
+
errorMessage?: string;
|
|
15
|
+
}
|
|
16
|
+
interface McpToolCall {
|
|
17
|
+
toolId: string;
|
|
18
|
+
toolName: string;
|
|
19
|
+
requestId: string;
|
|
20
|
+
input: any;
|
|
21
|
+
output: any;
|
|
22
|
+
isError: boolean;
|
|
23
|
+
timestamp?: number;
|
|
24
|
+
source?: {
|
|
25
|
+
type: string;
|
|
26
|
+
serverLabel: string;
|
|
27
|
+
label: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface McpToolMonitoring {
|
|
31
|
+
toolName: string;
|
|
32
|
+
totalCalls: number;
|
|
33
|
+
successfulCalls: number;
|
|
34
|
+
errorCalls: number;
|
|
35
|
+
successRate: number;
|
|
36
|
+
calls: McpToolCall[];
|
|
37
|
+
}
|
|
38
|
+
interface McpMonitoringSummary {
|
|
39
|
+
totalTools: number;
|
|
40
|
+
totalCalls: number;
|
|
41
|
+
overallSuccessRate: number;
|
|
42
|
+
tools: McpToolMonitoring[];
|
|
43
|
+
}
|
|
44
|
+
interface UserRequest {
|
|
45
|
+
id: string;
|
|
46
|
+
message: string;
|
|
47
|
+
timestamp?: number;
|
|
48
|
+
index: number;
|
|
49
|
+
}
|
|
50
|
+
interface DialogSession {
|
|
51
|
+
sessionId: string;
|
|
52
|
+
agentId?: string;
|
|
53
|
+
modelId?: string;
|
|
54
|
+
}
|
|
55
|
+
interface AIResponse {
|
|
56
|
+
/** Reference to the original request */
|
|
57
|
+
requestId: string;
|
|
58
|
+
/** Response ID if available */
|
|
59
|
+
responseId?: string;
|
|
60
|
+
/** Aggregated response text (all parts combined) */
|
|
61
|
+
message: string;
|
|
62
|
+
/** Timestamp when response was generated */
|
|
63
|
+
timestamp?: number;
|
|
64
|
+
/** Index in the conversation (0-based) */
|
|
65
|
+
index: number;
|
|
66
|
+
/** Whether response includes tool calls */
|
|
67
|
+
hasToolCalls: boolean;
|
|
68
|
+
/** Number of tool calls in this response */
|
|
69
|
+
toolCallCount: number;
|
|
70
|
+
}
|
|
71
|
+
interface ConversationTurn {
|
|
72
|
+
/** Turn index (0-based) */
|
|
73
|
+
index: number;
|
|
74
|
+
/** User's request */
|
|
75
|
+
request: UserRequest;
|
|
76
|
+
/** AI's response (null if no response yet) */
|
|
77
|
+
response: AIResponse | null;
|
|
12
78
|
}
|
|
13
79
|
declare const DialogStatus: {
|
|
80
|
+
readonly PENDING: "pending";
|
|
14
81
|
readonly COMPLETED: "completed";
|
|
15
82
|
readonly CANCELED: "canceled";
|
|
16
83
|
readonly IN_PROGRESS: "in_progress";
|
|
84
|
+
readonly FAILED: "failed";
|
|
17
85
|
};
|
|
18
86
|
type DialogStatusType = (typeof DialogStatus)[keyof typeof DialogStatus];
|
|
19
87
|
declare class CopilotChatAnalyzer {
|
|
20
88
|
getRequestsCount(chatData: CopilotChatData): number;
|
|
89
|
+
/**
|
|
90
|
+
* Extract sessionId from chat data.
|
|
91
|
+
* The sessionId is stored in result.metadata.sessionId of any request.
|
|
92
|
+
* All requests in a dialog share the same sessionId.
|
|
93
|
+
*/
|
|
94
|
+
getSessionId(chatData: CopilotChatData): string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Get full session information including agentId and modelId.
|
|
97
|
+
*/
|
|
98
|
+
getSessionInfo(chatData: CopilotChatData): DialogSession | null;
|
|
21
99
|
private hasRequests;
|
|
22
100
|
private getLastRequest;
|
|
23
101
|
getDialogStatus(chatData: CopilotChatData): DialogStatusType;
|
|
24
102
|
getDialogStatusDetails(chatData: CopilotChatData): DialogStatusDetails;
|
|
103
|
+
private extractMcpToolCalls;
|
|
104
|
+
getMcpToolMonitoring(chatData: CopilotChatData, toolName?: string): McpToolMonitoring | McpMonitoringSummary;
|
|
105
|
+
getMcpToolSuccessfulCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
|
|
106
|
+
getMcpToolErrorCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
|
|
107
|
+
/**
|
|
108
|
+
* Простой метод для получения всех вызовов конкретного MCP инструмента
|
|
109
|
+
* @param chatData - данные чата
|
|
110
|
+
* @param toolName - название инструмента (например, 'update_entry_fields')
|
|
111
|
+
* @returns массив всех вызовов инструмента
|
|
112
|
+
*/
|
|
113
|
+
getMcpToolCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
|
|
114
|
+
/**
|
|
115
|
+
* Получить список всех уникальных MCP инструментов, использованных в чате
|
|
116
|
+
* @param chatData - данные чата
|
|
117
|
+
* @returns массив названий инструментов
|
|
118
|
+
*/
|
|
119
|
+
getMcpToolNames(chatData: CopilotChatData): string[];
|
|
120
|
+
/**
|
|
121
|
+
* Получить историю запросов пользователя из данных чата
|
|
122
|
+
* @param chatData - данные чата Copilot
|
|
123
|
+
* @returns массив запросов пользователя с текстом сообщения
|
|
124
|
+
*/
|
|
125
|
+
getUserRequests(chatData: CopilotChatData): UserRequest[];
|
|
126
|
+
/**
|
|
127
|
+
* Extract response text from a request object
|
|
128
|
+
* Aggregates text from response[] and toolCallRounds[].response
|
|
129
|
+
*/
|
|
130
|
+
private extractResponseText;
|
|
131
|
+
/**
|
|
132
|
+
* Count total tool calls in a request
|
|
133
|
+
*/
|
|
134
|
+
private countToolCalls;
|
|
135
|
+
/**
|
|
136
|
+
* Get AI responses from chat data
|
|
137
|
+
* @param chatData - Copilot chat data
|
|
138
|
+
* @returns array of AI responses
|
|
139
|
+
*/
|
|
140
|
+
getAIResponses(chatData: CopilotChatData): AIResponse[];
|
|
141
|
+
/**
|
|
142
|
+
* Get full conversation history with paired requests and responses
|
|
143
|
+
* @param chatData - Copilot chat data
|
|
144
|
+
* @returns array of conversation turns (request + response pairs)
|
|
145
|
+
*/
|
|
146
|
+
getConversationHistory(chatData: CopilotChatData): ConversationTurn[];
|
|
25
147
|
}
|
|
26
148
|
|
|
27
|
-
export { CopilotChatAnalyzer, DialogStatus, type DialogStatusType, CopilotChatAnalyzer as default };
|
|
149
|
+
export { type AIResponse, type ConversationTurn, CopilotChatAnalyzer, type DialogSession, DialogStatus, type DialogStatusDetails, type DialogStatusType, type McpMonitoringSummary, type McpToolCall, type McpToolMonitoring, type UserRequest, CopilotChatAnalyzer as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,20 +8,142 @@ interface DialogStatusDetails {
|
|
|
8
8
|
hasResult: boolean;
|
|
9
9
|
hasFollowups: boolean;
|
|
10
10
|
isCanceled: boolean;
|
|
11
|
+
isFailed: boolean;
|
|
11
12
|
lastRequestId?: string;
|
|
13
|
+
errorCode?: string;
|
|
14
|
+
errorMessage?: string;
|
|
15
|
+
}
|
|
16
|
+
interface McpToolCall {
|
|
17
|
+
toolId: string;
|
|
18
|
+
toolName: string;
|
|
19
|
+
requestId: string;
|
|
20
|
+
input: any;
|
|
21
|
+
output: any;
|
|
22
|
+
isError: boolean;
|
|
23
|
+
timestamp?: number;
|
|
24
|
+
source?: {
|
|
25
|
+
type: string;
|
|
26
|
+
serverLabel: string;
|
|
27
|
+
label: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface McpToolMonitoring {
|
|
31
|
+
toolName: string;
|
|
32
|
+
totalCalls: number;
|
|
33
|
+
successfulCalls: number;
|
|
34
|
+
errorCalls: number;
|
|
35
|
+
successRate: number;
|
|
36
|
+
calls: McpToolCall[];
|
|
37
|
+
}
|
|
38
|
+
interface McpMonitoringSummary {
|
|
39
|
+
totalTools: number;
|
|
40
|
+
totalCalls: number;
|
|
41
|
+
overallSuccessRate: number;
|
|
42
|
+
tools: McpToolMonitoring[];
|
|
43
|
+
}
|
|
44
|
+
interface UserRequest {
|
|
45
|
+
id: string;
|
|
46
|
+
message: string;
|
|
47
|
+
timestamp?: number;
|
|
48
|
+
index: number;
|
|
49
|
+
}
|
|
50
|
+
interface DialogSession {
|
|
51
|
+
sessionId: string;
|
|
52
|
+
agentId?: string;
|
|
53
|
+
modelId?: string;
|
|
54
|
+
}
|
|
55
|
+
interface AIResponse {
|
|
56
|
+
/** Reference to the original request */
|
|
57
|
+
requestId: string;
|
|
58
|
+
/** Response ID if available */
|
|
59
|
+
responseId?: string;
|
|
60
|
+
/** Aggregated response text (all parts combined) */
|
|
61
|
+
message: string;
|
|
62
|
+
/** Timestamp when response was generated */
|
|
63
|
+
timestamp?: number;
|
|
64
|
+
/** Index in the conversation (0-based) */
|
|
65
|
+
index: number;
|
|
66
|
+
/** Whether response includes tool calls */
|
|
67
|
+
hasToolCalls: boolean;
|
|
68
|
+
/** Number of tool calls in this response */
|
|
69
|
+
toolCallCount: number;
|
|
70
|
+
}
|
|
71
|
+
interface ConversationTurn {
|
|
72
|
+
/** Turn index (0-based) */
|
|
73
|
+
index: number;
|
|
74
|
+
/** User's request */
|
|
75
|
+
request: UserRequest;
|
|
76
|
+
/** AI's response (null if no response yet) */
|
|
77
|
+
response: AIResponse | null;
|
|
12
78
|
}
|
|
13
79
|
declare const DialogStatus: {
|
|
80
|
+
readonly PENDING: "pending";
|
|
14
81
|
readonly COMPLETED: "completed";
|
|
15
82
|
readonly CANCELED: "canceled";
|
|
16
83
|
readonly IN_PROGRESS: "in_progress";
|
|
84
|
+
readonly FAILED: "failed";
|
|
17
85
|
};
|
|
18
86
|
type DialogStatusType = (typeof DialogStatus)[keyof typeof DialogStatus];
|
|
19
87
|
declare class CopilotChatAnalyzer {
|
|
20
88
|
getRequestsCount(chatData: CopilotChatData): number;
|
|
89
|
+
/**
|
|
90
|
+
* Extract sessionId from chat data.
|
|
91
|
+
* The sessionId is stored in result.metadata.sessionId of any request.
|
|
92
|
+
* All requests in a dialog share the same sessionId.
|
|
93
|
+
*/
|
|
94
|
+
getSessionId(chatData: CopilotChatData): string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Get full session information including agentId and modelId.
|
|
97
|
+
*/
|
|
98
|
+
getSessionInfo(chatData: CopilotChatData): DialogSession | null;
|
|
21
99
|
private hasRequests;
|
|
22
100
|
private getLastRequest;
|
|
23
101
|
getDialogStatus(chatData: CopilotChatData): DialogStatusType;
|
|
24
102
|
getDialogStatusDetails(chatData: CopilotChatData): DialogStatusDetails;
|
|
103
|
+
private extractMcpToolCalls;
|
|
104
|
+
getMcpToolMonitoring(chatData: CopilotChatData, toolName?: string): McpToolMonitoring | McpMonitoringSummary;
|
|
105
|
+
getMcpToolSuccessfulCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
|
|
106
|
+
getMcpToolErrorCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
|
|
107
|
+
/**
|
|
108
|
+
* Простой метод для получения всех вызовов конкретного MCP инструмента
|
|
109
|
+
* @param chatData - данные чата
|
|
110
|
+
* @param toolName - название инструмента (например, 'update_entry_fields')
|
|
111
|
+
* @returns массив всех вызовов инструмента
|
|
112
|
+
*/
|
|
113
|
+
getMcpToolCalls(chatData: CopilotChatData, toolName: string): McpToolCall[];
|
|
114
|
+
/**
|
|
115
|
+
* Получить список всех уникальных MCP инструментов, использованных в чате
|
|
116
|
+
* @param chatData - данные чата
|
|
117
|
+
* @returns массив названий инструментов
|
|
118
|
+
*/
|
|
119
|
+
getMcpToolNames(chatData: CopilotChatData): string[];
|
|
120
|
+
/**
|
|
121
|
+
* Получить историю запросов пользователя из данных чата
|
|
122
|
+
* @param chatData - данные чата Copilot
|
|
123
|
+
* @returns массив запросов пользователя с текстом сообщения
|
|
124
|
+
*/
|
|
125
|
+
getUserRequests(chatData: CopilotChatData): UserRequest[];
|
|
126
|
+
/**
|
|
127
|
+
* Extract response text from a request object
|
|
128
|
+
* Aggregates text from response[] and toolCallRounds[].response
|
|
129
|
+
*/
|
|
130
|
+
private extractResponseText;
|
|
131
|
+
/**
|
|
132
|
+
* Count total tool calls in a request
|
|
133
|
+
*/
|
|
134
|
+
private countToolCalls;
|
|
135
|
+
/**
|
|
136
|
+
* Get AI responses from chat data
|
|
137
|
+
* @param chatData - Copilot chat data
|
|
138
|
+
* @returns array of AI responses
|
|
139
|
+
*/
|
|
140
|
+
getAIResponses(chatData: CopilotChatData): AIResponse[];
|
|
141
|
+
/**
|
|
142
|
+
* Get full conversation history with paired requests and responses
|
|
143
|
+
* @param chatData - Copilot chat data
|
|
144
|
+
* @returns array of conversation turns (request + response pairs)
|
|
145
|
+
*/
|
|
146
|
+
getConversationHistory(chatData: CopilotChatData): ConversationTurn[];
|
|
25
147
|
}
|
|
26
148
|
|
|
27
|
-
export { CopilotChatAnalyzer, DialogStatus, type DialogStatusType, CopilotChatAnalyzer as default };
|
|
149
|
+
export { type AIResponse, type ConversationTurn, CopilotChatAnalyzer, type DialogSession, DialogStatus, type DialogStatusDetails, type DialogStatusType, type McpMonitoringSummary, type McpToolCall, type McpToolMonitoring, type UserRequest, CopilotChatAnalyzer as default };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
var DialogStatus = {
|
|
3
|
+
PENDING: "pending",
|
|
3
4
|
COMPLETED: "completed",
|
|
4
5
|
CANCELED: "canceled",
|
|
5
|
-
IN_PROGRESS: "in_progress"
|
|
6
|
+
IN_PROGRESS: "in_progress",
|
|
7
|
+
FAILED: "failed"
|
|
6
8
|
};
|
|
7
9
|
var CopilotChatAnalyzer = class {
|
|
8
10
|
getRequestsCount(chatData) {
|
|
@@ -11,6 +13,43 @@ var CopilotChatAnalyzer = class {
|
|
|
11
13
|
}
|
|
12
14
|
return chatData.requests.length;
|
|
13
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Extract sessionId from chat data.
|
|
18
|
+
* The sessionId is stored in result.metadata.sessionId of any request.
|
|
19
|
+
* All requests in a dialog share the same sessionId.
|
|
20
|
+
*/
|
|
21
|
+
getSessionId(chatData) {
|
|
22
|
+
if (!chatData?.requests?.length) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
for (const request of chatData.requests) {
|
|
26
|
+
const sessionId = request?.result?.metadata?.sessionId;
|
|
27
|
+
if (sessionId && typeof sessionId === "string") {
|
|
28
|
+
return sessionId;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get full session information including agentId and modelId.
|
|
35
|
+
*/
|
|
36
|
+
getSessionInfo(chatData) {
|
|
37
|
+
const sessionId = this.getSessionId(chatData);
|
|
38
|
+
if (!sessionId) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
for (const request of chatData.requests) {
|
|
42
|
+
const metadata = request?.result?.metadata;
|
|
43
|
+
if (metadata?.sessionId === sessionId) {
|
|
44
|
+
return {
|
|
45
|
+
sessionId,
|
|
46
|
+
agentId: metadata?.agentId,
|
|
47
|
+
modelId: metadata?.modelId
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { sessionId };
|
|
52
|
+
}
|
|
14
53
|
hasRequests(chatData) {
|
|
15
54
|
return Array.isArray(chatData.requests) && chatData.requests.length > 0;
|
|
16
55
|
}
|
|
@@ -22,15 +61,22 @@ var CopilotChatAnalyzer = class {
|
|
|
22
61
|
}
|
|
23
62
|
getDialogStatus(chatData) {
|
|
24
63
|
if (!this.hasRequests(chatData)) {
|
|
25
|
-
return DialogStatus.
|
|
64
|
+
return DialogStatus.PENDING;
|
|
26
65
|
}
|
|
27
66
|
const lastRequest = this.getLastRequest(chatData);
|
|
28
67
|
if (!lastRequest) {
|
|
29
|
-
return DialogStatus.
|
|
68
|
+
return DialogStatus.PENDING;
|
|
30
69
|
}
|
|
31
70
|
if (lastRequest.isCanceled === true) {
|
|
32
71
|
return DialogStatus.CANCELED;
|
|
33
72
|
}
|
|
73
|
+
const errorDetails = lastRequest?.result?.errorDetails;
|
|
74
|
+
if (errorDetails) {
|
|
75
|
+
if (errorDetails.code === "canceled") {
|
|
76
|
+
return DialogStatus.CANCELED;
|
|
77
|
+
}
|
|
78
|
+
return DialogStatus.FAILED;
|
|
79
|
+
}
|
|
34
80
|
if ("followups" in lastRequest && Array.isArray(lastRequest.followups)) {
|
|
35
81
|
if (lastRequest.followups.length === 0) {
|
|
36
82
|
return DialogStatus.COMPLETED;
|
|
@@ -45,18 +91,22 @@ var CopilotChatAnalyzer = class {
|
|
|
45
91
|
const status = this.getDialogStatus(chatData);
|
|
46
92
|
if (!this.hasRequests(chatData)) {
|
|
47
93
|
return {
|
|
48
|
-
status: DialogStatus.
|
|
49
|
-
statusText: "
|
|
94
|
+
status: DialogStatus.PENDING,
|
|
95
|
+
statusText: "Dialog not started",
|
|
50
96
|
hasResult: false,
|
|
51
97
|
hasFollowups: false,
|
|
52
|
-
isCanceled: false
|
|
98
|
+
isCanceled: false,
|
|
99
|
+
isFailed: false
|
|
53
100
|
};
|
|
54
101
|
}
|
|
55
102
|
const lastRequest = this.getLastRequest(chatData);
|
|
103
|
+
const errorDetails = lastRequest?.result?.errorDetails;
|
|
56
104
|
const statusTexts = {
|
|
57
|
-
[DialogStatus.
|
|
58
|
-
[DialogStatus.
|
|
59
|
-
[DialogStatus.
|
|
105
|
+
[DialogStatus.PENDING]: "Dialog not started",
|
|
106
|
+
[DialogStatus.COMPLETED]: "Dialog completed successfully",
|
|
107
|
+
[DialogStatus.CANCELED]: "Dialog was canceled",
|
|
108
|
+
[DialogStatus.IN_PROGRESS]: "Dialog in progress",
|
|
109
|
+
[DialogStatus.FAILED]: "Dialog failed with error"
|
|
60
110
|
};
|
|
61
111
|
return {
|
|
62
112
|
status,
|
|
@@ -64,9 +114,239 @@ var CopilotChatAnalyzer = class {
|
|
|
64
114
|
hasResult: lastRequest && "result" in lastRequest && lastRequest.result !== null,
|
|
65
115
|
hasFollowups: lastRequest && "followups" in lastRequest,
|
|
66
116
|
isCanceled: lastRequest && lastRequest.isCanceled === true,
|
|
67
|
-
|
|
117
|
+
isFailed: !!errorDetails,
|
|
118
|
+
lastRequestId: lastRequest?.requestId,
|
|
119
|
+
errorCode: errorDetails?.code,
|
|
120
|
+
errorMessage: errorDetails?.message
|
|
68
121
|
};
|
|
69
122
|
}
|
|
123
|
+
extractMcpToolCalls(chatData) {
|
|
124
|
+
if (!this.hasRequests(chatData)) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
const toolCalls = [];
|
|
128
|
+
chatData.requests.forEach((request) => {
|
|
129
|
+
if (!request.response || !Array.isArray(request.response)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
request.response.forEach((responseItem) => {
|
|
133
|
+
if (responseItem.kind === "toolInvocationSerialized" && responseItem.source?.type === "mcp") {
|
|
134
|
+
const toolCall = {
|
|
135
|
+
toolId: responseItem.toolId || responseItem.toolName || "unknown",
|
|
136
|
+
toolName: responseItem.toolName || responseItem.toolId || "unknown",
|
|
137
|
+
requestId: request.requestId,
|
|
138
|
+
input: responseItem.resultDetails?.input || responseItem.toolSpecificData?.rawInput || null,
|
|
139
|
+
output: responseItem.resultDetails?.output || null,
|
|
140
|
+
isError: responseItem.resultDetails?.isError || false,
|
|
141
|
+
timestamp: request.timestamp,
|
|
142
|
+
source: responseItem.source
|
|
143
|
+
};
|
|
144
|
+
toolCalls.push(toolCall);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
return toolCalls;
|
|
149
|
+
}
|
|
150
|
+
getMcpToolMonitoring(chatData, toolName) {
|
|
151
|
+
const allToolCalls = this.extractMcpToolCalls(chatData);
|
|
152
|
+
if (toolName) {
|
|
153
|
+
const toolCalls = allToolCalls.filter(
|
|
154
|
+
(call) => call.toolName.includes(toolName) || call.toolId.includes(toolName)
|
|
155
|
+
);
|
|
156
|
+
const successfulCalls = toolCalls.filter((call) => !call.isError).length;
|
|
157
|
+
const errorCalls = toolCalls.filter((call) => call.isError).length;
|
|
158
|
+
const successRate = toolCalls.length > 0 ? successfulCalls / toolCalls.length * 100 : 0;
|
|
159
|
+
return {
|
|
160
|
+
toolName,
|
|
161
|
+
totalCalls: toolCalls.length,
|
|
162
|
+
successfulCalls,
|
|
163
|
+
errorCalls,
|
|
164
|
+
successRate: Math.round(successRate * 100) / 100,
|
|
165
|
+
calls: toolCalls
|
|
166
|
+
};
|
|
167
|
+
} else {
|
|
168
|
+
const toolsMap = /* @__PURE__ */ new Map();
|
|
169
|
+
allToolCalls.forEach((call) => {
|
|
170
|
+
const key = call.toolName || call.toolId;
|
|
171
|
+
if (!toolsMap.has(key)) {
|
|
172
|
+
toolsMap.set(key, []);
|
|
173
|
+
}
|
|
174
|
+
toolsMap.get(key).push(call);
|
|
175
|
+
});
|
|
176
|
+
const tools = Array.from(toolsMap.entries()).map(
|
|
177
|
+
([toolName2, calls]) => {
|
|
178
|
+
const successfulCalls = calls.filter((call) => !call.isError).length;
|
|
179
|
+
const errorCalls = calls.filter((call) => call.isError).length;
|
|
180
|
+
const successRate = calls.length > 0 ? successfulCalls / calls.length * 100 : 0;
|
|
181
|
+
return {
|
|
182
|
+
toolName: toolName2,
|
|
183
|
+
totalCalls: calls.length,
|
|
184
|
+
successfulCalls,
|
|
185
|
+
errorCalls,
|
|
186
|
+
successRate: Math.round(successRate * 100) / 100,
|
|
187
|
+
calls
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
const totalCalls = allToolCalls.length;
|
|
192
|
+
const totalSuccessful = allToolCalls.filter(
|
|
193
|
+
(call) => !call.isError
|
|
194
|
+
).length;
|
|
195
|
+
const overallSuccessRate = totalCalls > 0 ? totalSuccessful / totalCalls * 100 : 0;
|
|
196
|
+
return {
|
|
197
|
+
totalTools: tools.length,
|
|
198
|
+
totalCalls,
|
|
199
|
+
overallSuccessRate: Math.round(overallSuccessRate * 100) / 100,
|
|
200
|
+
tools
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
getMcpToolSuccessfulCalls(chatData, toolName) {
|
|
205
|
+
const monitoring = this.getMcpToolMonitoring(
|
|
206
|
+
chatData,
|
|
207
|
+
toolName
|
|
208
|
+
);
|
|
209
|
+
return monitoring.calls.filter((call) => !call.isError);
|
|
210
|
+
}
|
|
211
|
+
getMcpToolErrorCalls(chatData, toolName) {
|
|
212
|
+
const monitoring = this.getMcpToolMonitoring(
|
|
213
|
+
chatData,
|
|
214
|
+
toolName
|
|
215
|
+
);
|
|
216
|
+
return monitoring.calls.filter((call) => call.isError);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Простой метод для получения всех вызовов конкретного MCP инструмента
|
|
220
|
+
* @param chatData - данные чата
|
|
221
|
+
* @param toolName - название инструмента (например, 'update_entry_fields')
|
|
222
|
+
* @returns массив всех вызовов инструмента
|
|
223
|
+
*/
|
|
224
|
+
getMcpToolCalls(chatData, toolName) {
|
|
225
|
+
const allToolCalls = this.extractMcpToolCalls(chatData);
|
|
226
|
+
return allToolCalls.filter(
|
|
227
|
+
(call) => call.toolName.includes(toolName) || call.toolId.includes(toolName)
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Получить список всех уникальных MCP инструментов, использованных в чате
|
|
232
|
+
* @param chatData - данные чата
|
|
233
|
+
* @returns массив названий инструментов
|
|
234
|
+
*/
|
|
235
|
+
getMcpToolNames(chatData) {
|
|
236
|
+
const allToolCalls = this.extractMcpToolCalls(chatData);
|
|
237
|
+
const uniqueNames = /* @__PURE__ */ new Set();
|
|
238
|
+
allToolCalls.forEach((call) => {
|
|
239
|
+
uniqueNames.add(call.toolName || call.toolId);
|
|
240
|
+
});
|
|
241
|
+
return Array.from(uniqueNames);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Получить историю запросов пользователя из данных чата
|
|
245
|
+
* @param chatData - данные чата Copilot
|
|
246
|
+
* @returns массив запросов пользователя с текстом сообщения
|
|
247
|
+
*/
|
|
248
|
+
getUserRequests(chatData) {
|
|
249
|
+
if (!chatData || !Array.isArray(chatData.requests)) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
const userRequests = [];
|
|
253
|
+
chatData.requests.forEach((request, index) => {
|
|
254
|
+
let messageText;
|
|
255
|
+
if (typeof request.message === "string") {
|
|
256
|
+
messageText = request.message;
|
|
257
|
+
} else if (request.message && typeof request.message.text === "string") {
|
|
258
|
+
messageText = request.message.text;
|
|
259
|
+
}
|
|
260
|
+
if (messageText) {
|
|
261
|
+
userRequests.push({
|
|
262
|
+
id: request.variableData?.requestId || request.requestId || `req-${index}`,
|
|
263
|
+
message: messageText,
|
|
264
|
+
timestamp: request.timestamp,
|
|
265
|
+
index
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
return userRequests;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Extract response text from a request object
|
|
273
|
+
* Aggregates text from response[] and toolCallRounds[].response
|
|
274
|
+
*/
|
|
275
|
+
extractResponseText(request) {
|
|
276
|
+
const parts = [];
|
|
277
|
+
if (Array.isArray(request.response)) {
|
|
278
|
+
for (const item of request.response) {
|
|
279
|
+
if (typeof item === "string") {
|
|
280
|
+
parts.push(item);
|
|
281
|
+
} else if (item.value && typeof item.value === "string") {
|
|
282
|
+
parts.push(item.value);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const toolCallRounds = request.result?.metadata?.toolCallRounds;
|
|
287
|
+
if (Array.isArray(toolCallRounds)) {
|
|
288
|
+
for (const round of toolCallRounds) {
|
|
289
|
+
if (round.response && typeof round.response === "string") {
|
|
290
|
+
if (!parts.includes(round.response)) {
|
|
291
|
+
parts.push(round.response);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return parts.join("\n\n");
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Count total tool calls in a request
|
|
300
|
+
*/
|
|
301
|
+
countToolCalls(request) {
|
|
302
|
+
const toolCallRounds = request.result?.metadata?.toolCallRounds;
|
|
303
|
+
if (!Array.isArray(toolCallRounds)) return 0;
|
|
304
|
+
return toolCallRounds.reduce((count, round) => {
|
|
305
|
+
return count + (round.toolCalls?.length || 0);
|
|
306
|
+
}, 0);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get AI responses from chat data
|
|
310
|
+
* @param chatData - Copilot chat data
|
|
311
|
+
* @returns array of AI responses
|
|
312
|
+
*/
|
|
313
|
+
getAIResponses(chatData) {
|
|
314
|
+
if (!chatData || !Array.isArray(chatData.requests)) {
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
const aiResponses = [];
|
|
318
|
+
chatData.requests.forEach((request, index) => {
|
|
319
|
+
const message = this.extractResponseText(request);
|
|
320
|
+
const toolCallCount = this.countToolCalls(request);
|
|
321
|
+
aiResponses.push({
|
|
322
|
+
requestId: request.requestId || `req-${index}`,
|
|
323
|
+
responseId: request.responseId,
|
|
324
|
+
message,
|
|
325
|
+
timestamp: request.timestamp,
|
|
326
|
+
index,
|
|
327
|
+
hasToolCalls: toolCallCount > 0,
|
|
328
|
+
toolCallCount
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
return aiResponses;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Get full conversation history with paired requests and responses
|
|
335
|
+
* @param chatData - Copilot chat data
|
|
336
|
+
* @returns array of conversation turns (request + response pairs)
|
|
337
|
+
*/
|
|
338
|
+
getConversationHistory(chatData) {
|
|
339
|
+
const userRequests = this.getUserRequests(chatData);
|
|
340
|
+
const aiResponses = this.getAIResponses(chatData);
|
|
341
|
+
return userRequests.map((request, index) => {
|
|
342
|
+
const response = aiResponses.find((r) => r.index === index) || null;
|
|
343
|
+
return {
|
|
344
|
+
index,
|
|
345
|
+
request,
|
|
346
|
+
response
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
}
|
|
70
350
|
};
|
|
71
351
|
var index_default = CopilotChatAnalyzer;
|
|
72
352
|
export {
|
package/package.json
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "copilot-chat-analyzer",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "0.0.4",
|
|
5
|
+
"description": "TypeScript library for analyzing and monitoring GitHub Copilot chat exports with status detection, request counting, and MCP tool tracking",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"copilot",
|
|
8
8
|
"chat",
|
|
9
9
|
"analyzer",
|
|
10
10
|
"github",
|
|
11
|
-
"typescript"
|
|
11
|
+
"typescript",
|
|
12
|
+
"mcp",
|
|
13
|
+
"model-context-protocol",
|
|
14
|
+
"monitoring",
|
|
15
|
+
"status-detection",
|
|
16
|
+
"dialog-analysis",
|
|
17
|
+
"ai-tools",
|
|
18
|
+
"export-analysis"
|
|
12
19
|
],
|
|
13
20
|
"author": "dealenx",
|
|
14
21
|
"license": "MIT",
|
|
@@ -20,6 +27,35 @@
|
|
|
20
27
|
"url": "https://github.com/dealenx/copilot-chat-analyzer/issues"
|
|
21
28
|
},
|
|
22
29
|
"homepage": "https://github.com/dealenx/copilot-chat-analyzer#readme",
|
|
30
|
+
"funding": {
|
|
31
|
+
"type": "github",
|
|
32
|
+
"url": "https://github.com/sponsors/dealenx"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=16.0.0"
|
|
36
|
+
},
|
|
37
|
+
"ai-usage": {
|
|
38
|
+
"purpose": "Analyze GitHub Copilot chat exports for dialog status, request counting, and MCP tool monitoring",
|
|
39
|
+
"quick-start": "import CopilotChatAnalyzer, { DialogStatus } from 'copilot-chat-analyzer'; const analyzer = new CopilotChatAnalyzer(); const status = analyzer.getDialogStatus(chatData);",
|
|
40
|
+
"main-features": [
|
|
41
|
+
"Dialog status detection (pending, in_progress, completed, canceled)",
|
|
42
|
+
"Request counting and analysis",
|
|
43
|
+
"MCP (Model Context Protocol) tool monitoring",
|
|
44
|
+
"Chat export validation and parsing"
|
|
45
|
+
],
|
|
46
|
+
"common-methods": {
|
|
47
|
+
"getDialogStatus": "Returns dialog status: 'pending' | 'completed' | 'canceled' | 'in_progress'",
|
|
48
|
+
"getRequestsCount": "Returns number of requests in chat",
|
|
49
|
+
"getDialogStatusDetails": "Returns detailed status information with flags",
|
|
50
|
+
"getMcpToolMonitoring": "Returns MCP tool usage statistics and monitoring data"
|
|
51
|
+
},
|
|
52
|
+
"status-meanings": {
|
|
53
|
+
"pending": "Chat created but no requests made yet (requests: [])",
|
|
54
|
+
"in_progress": "Chat has requests but not completed",
|
|
55
|
+
"completed": "Chat finished successfully (has followups: [])",
|
|
56
|
+
"canceled": "Chat was canceled (isCanceled: true)"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
23
59
|
"type": "module",
|
|
24
60
|
"main": "dist/index.cjs",
|
|
25
61
|
"module": "dist/index.mjs",
|
|
@@ -33,7 +69,10 @@
|
|
|
33
69
|
}
|
|
34
70
|
},
|
|
35
71
|
"files": [
|
|
36
|
-
"dist"
|
|
72
|
+
"dist",
|
|
73
|
+
"README.md",
|
|
74
|
+
"AI_USAGE.md",
|
|
75
|
+
"LICENSE"
|
|
37
76
|
],
|
|
38
77
|
"devDependencies": {
|
|
39
78
|
"@types/jest": "^30.0.0",
|