@zhijianma/studio 1.0.10
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 +202 -0
- package/README.md +158 -0
- package/bin/cli.js +7 -0
- package/dist/app/friday/args.py +65 -0
- package/dist/app/friday/hook.py +52 -0
- package/dist/app/friday/main.py +164 -0
- package/dist/app/friday/model.py +124 -0
- package/dist/app/friday/tool/agentscope_tools.py +159 -0
- package/dist/app/friday/tool/utils.py +161 -0
- package/dist/app/friday/utils/common.py +28 -0
- package/dist/app/friday/utils/connect.py +63 -0
- package/dist/app/friday/utils/constants.py +8 -0
- package/dist/app/requirements.txt +1 -0
- package/dist/public/assets/001-aaron-Bb4ZO8rt.js +1 -0
- package/dist/public/assets/001-little girl-CnyQBZ0k.js +1 -0
- package/dist/public/assets/001-man-16-BO6NLPD_.js +1 -0
- package/dist/public/assets/002-avatar-C3FPnFjo.js +1 -0
- package/dist/public/assets/002-man-15-DfiiX2xR.js +1 -0
- package/dist/public/assets/002-old man-D4L-hA2l.js +1 -0
- package/dist/public/assets/003-avatar-UMXD80xE.js +1 -0
- package/dist/public/assets/003-cat-Cgyj1qJU.js +1 -0
- package/dist/public/assets/003-woman-20-Dy0WdjIG.js +1 -0
- package/dist/public/assets/004-avatar-D6Aw4tpO.js +1 -0
- package/dist/public/assets/004-dog-DUajs1ZO.js +1 -0
- package/dist/public/assets/004-woman-19-D5go5okE.js +1 -0
- package/dist/public/assets/005-avatar-NNQ3sWMe.js +1 -0
- package/dist/public/assets/005-child-SN-FoL0R.js +1 -0
- package/dist/public/assets/005-woman-18-AedSRg8a.js +1 -0
- package/dist/public/assets/006-avatar-FD8IUNL_.js +1 -0
- package/dist/public/assets/006-grandpa-T90IdpYr.js +1 -0
- package/dist/public/assets/006-man-14-DuMA-IZ-.js +1 -0
- package/dist/public/assets/007-avatar-eSQZtpkg.js +1 -0
- package/dist/public/assets/007-grandson-DV7UWhrR.js +1 -0
- package/dist/public/assets/007-man-13-owFTee2M.js +1 -0
- package/dist/public/assets/008-avatar-B9Rcm8D2.js +1 -0
- package/dist/public/assets/008-grandmother-1-BrQNVu9I.js +1 -0
- package/dist/public/assets/008-teenager-ClYxB7sG.js +1 -0
- package/dist/public/assets/009-avatar-DNSTEyOZ.js +1 -0
- package/dist/public/assets/009-grandfather-1-CPOn7LN0.js +1 -0
- package/dist/public/assets/009-grandmother-BAW_dEQC.js +1 -0
- package/dist/public/assets/010-avatar-CRXWH-A0.js +1 -0
- package/dist/public/assets/010-boy-1-C21LhAEr.js +1 -0
- package/dist/public/assets/011-woman-17-CI0UoPMR.js +1 -0
- package/dist/public/assets/012-avatar-BO5j-gGw.js +1 -0
- package/dist/public/assets/012-boy-T-hL8z9d.js +1 -0
- package/dist/public/assets/012-niece-M-tDMWMW.js +1 -0
- package/dist/public/assets/013-avatar-BTyOVKVo.js +1 -0
- package/dist/public/assets/013-granddaughter-D4VonRL-.js +1 -0
- package/dist/public/assets/013-woman-16-CuR7_ppw.js +1 -0
- package/dist/public/assets/014-avatar-DAW_Hvos.js +1 -0
- package/dist/public/assets/014-man-k8AXOF1Q.js +1 -0
- package/dist/public/assets/014-nurse-DnVigtmM.js +1 -0
- package/dist/public/assets/015-avatar-AxHR3j7h.js +1 -0
- package/dist/public/assets/015-woman-15-rDPwBxxc.js +1 -0
- package/dist/public/assets/015-woman-B71QRPrr.js +1 -0
- package/dist/public/assets/016-avatar-BVbMUisT.js +1 -0
- package/dist/public/assets/016-boy-BV_jzAu3.js +1 -0
- package/dist/public/assets/016-woman-14-D08zoPt9.js +1 -0
- package/dist/public/assets/017-gaming-BEpEpa1H.js +1 -0
- package/dist/public/assets/017-superpower-9xQAqiKN.js +1 -0
- package/dist/public/assets/017-uncle-EB1lmqAo.js +1 -0
- package/dist/public/assets/017-woman-13-BFsTAnGR.js +1 -0
- package/dist/public/assets/018-avatar-CnCLJXqW.js +1 -0
- package/dist/public/assets/018-young-QpIX6g3z.js +1 -0
- package/dist/public/assets/019-gaming-Dz5tR0Te.js +1 -0
- package/dist/public/assets/019-man-12-DiRb1dri.js +1 -0
- package/dist/public/assets/019-nephew-tXAgA9cr.js +1 -0
- package/dist/public/assets/020-avatar-Djtnu7jj.js +1 -0
- package/dist/public/assets/020-man-11-BNIZQyX4.js +1 -0
- package/dist/public/assets/020-young woman-BzklF8tq.js +1 -0
- package/dist/public/assets/021-aunt-CX9ikaTy.js +1 -0
- package/dist/public/assets/021-avatar-DP2sODK2.js +1 -0
- package/dist/public/assets/021-superhero-CIWREXp0.js +1 -0
- package/dist/public/assets/021-wizard-Dc9X50m_.js +1 -0
- package/dist/public/assets/021-woman-12-n9ipJK5z.js +1 -0
- package/dist/public/assets/022-father-B1_-ZTn1.js +1 -0
- package/dist/public/assets/022-woman-11-BUCsm06E.js +1 -0
- package/dist/public/assets/023-avatar-TeJ6vGqs.js +1 -0
- package/dist/public/assets/023-ghost-BYvGsa7H.js +1 -0
- package/dist/public/assets/023-man-10-Dfii-GE1.js +1 -0
- package/dist/public/assets/023-uncle-DoAMe1Iy.js +1 -0
- package/dist/public/assets/024-gaming-5ovd4aeT.js +1 -0
- package/dist/public/assets/024-man-9-CEenp_2W.js +1 -0
- package/dist/public/assets/024-prince-Fx3eCiNX.js +1 -0
- package/dist/public/assets/024-uncle-B6tqhqxU.js +1 -0
- package/dist/public/assets/025-gaming-CdlTp3Ub.js +1 -0
- package/dist/public/assets/025-man-8-Cfp6jNt4.js +1 -0
- package/dist/public/assets/025-man-BxhR-XTO.js +1 -0
- package/dist/public/assets/025-princess-Di5TdSnG.js +1 -0
- package/dist/public/assets/026-avatar-DojOPRVd.js +1 -0
- package/dist/public/assets/026-dad-CHaoMWM5.js +1 -0
- package/dist/public/assets/026-owl-Dl-UtZM8.js +1 -0
- package/dist/public/assets/026-woman-10-CywXJiaj.js +1 -0
- package/dist/public/assets/027-avatar-CrEyLmOb.js +1 -0
- package/dist/public/assets/027-girl-Box_GaMO.js +1 -0
- package/dist/public/assets/028-gaming-CmOZ2uGp.js +1 -0
- package/dist/public/assets/028-mom-tEacX7K4.js +1 -0
- package/dist/public/assets/028-woman-9-C1oq4sFk.js +1 -0
- package/dist/public/assets/029-boy-CVg6oMuZ.js +1 -0
- package/dist/public/assets/029-man-7-C4eeEeTQ.js +1 -0
- package/dist/public/assets/030-elf-C8vikAvx.js +1 -0
- package/dist/public/assets/030-gaming-ufCRMN7S.js +1 -0
- package/dist/public/assets/030-granddaughter-DVCp3D8k.js +1 -0
- package/dist/public/assets/030-woman-8-Bv_tRQci.js +1 -0
- package/dist/public/assets/031-avatar-D0u5HVaC.js +1 -0
- package/dist/public/assets/031-grandpa-jKj5oB8I.js +1 -0
- package/dist/public/assets/031-superhero-DfSkK-j2.js +1 -0
- package/dist/public/assets/031-woman-7-DJHfCbTS.js +1 -0
- package/dist/public/assets/032-avatar-BvY6LKIZ.js +1 -0
- package/dist/public/assets/032-grandson-DIjRxDr-.js +1 -0
- package/dist/public/assets/032-man-6-CU7pJmmt.js +1 -0
- package/dist/public/assets/033-avatar-Dviobgw8.js +1 -0
- package/dist/public/assets/033-elf-CTAEJgfN.js +1 -0
- package/dist/public/assets/033-grandfather-BCXPgoJj.js +1 -0
- package/dist/public/assets/033-man-5-wgMylvNO.js +1 -0
- package/dist/public/assets/034-avatar-BqtUHT3_.js +1 -0
- package/dist/public/assets/034-little girl-CrgnV25d.js +1 -0
- package/dist/public/assets/034-man-4-DJdzfcSi.js +1 -0
- package/dist/public/assets/035-gaming-Bp8xnJp_.js +1 -0
- package/dist/public/assets/035-mother-Ccg5FDO8.js +1 -0
- package/dist/public/assets/036-avatar-q6pZKQVf.js +1 -0
- package/dist/public/assets/036-father-DzpzeKJJ.js +1 -0
- package/dist/public/assets/036-goblin-vDsj4aP0.js +1 -0
- package/dist/public/assets/036-grandmother-BI_Fl_aR.js +1 -0
- package/dist/public/assets/037-avatar-CmfvVc7v.js +1 -0
- package/dist/public/assets/037-grandfather-Dtp4XH-s.js +1 -0
- package/dist/public/assets/037-sister-BQv83Uhf.js +1 -0
- package/dist/public/assets/038-gaming-UtXFtv5k.js +1 -0
- package/dist/public/assets/038-girl-Cwg5Rv36.js +1 -0
- package/dist/public/assets/038-gnome-HbogTmVa.js +1 -0
- package/dist/public/assets/038-woman-6-B7jMwsP0.js +1 -0
- package/dist/public/assets/039-avatar-Bb3kVg4l.js +1 -0
- package/dist/public/assets/039-man-3-BSjLnoLV.js +1 -0
- package/dist/public/assets/039-student-CIi32WG9.js +1 -0
- package/dist/public/assets/040-aunt-B5XQH_sJ.js +1 -0
- package/dist/public/assets/040-gaming-CzTjib4v.js +1 -0
- package/dist/public/assets/040-vampire-UFzaHu5c.js +1 -0
- package/dist/public/assets/040-woman-5-Byal-gfX.js +1 -0
- package/dist/public/assets/041-avatar-CPQN2PvT.js +1 -0
- package/dist/public/assets/041-little boy-DUZFMrmJ.js +1 -0
- package/dist/public/assets/041-queen-CqFzW3uO.js +1 -0
- package/dist/public/assets/041-woman-4-CKi_Q-ns.js +1 -0
- package/dist/public/assets/042-avatar-BatY-WQG.js +1 -0
- package/dist/public/assets/042-cyclops-DAxxBVqG.js +1 -0
- package/dist/public/assets/042-man-omzAnpu1.js +1 -0
- package/dist/public/assets/042-woman-3-2Ki1Qz3j.js +1 -0
- package/dist/public/assets/043-avatar-DN026abg.js +1 -0
- package/dist/public/assets/043-child-D8_xXkcm.js +1 -0
- package/dist/public/assets/043-genie-DK78Ykl6.js +1 -0
- package/dist/public/assets/043-woman-2-DMnB0A-0.js +1 -0
- package/dist/public/assets/044-avatar-Bz2FAXUw.js +1 -0
- package/dist/public/assets/044-avatar-DzDIyuj0.js +1 -0
- package/dist/public/assets/044-fairy-BT73odE0.js +1 -0
- package/dist/public/assets/044-sister-CtNPPHh-.js +1 -0
- package/dist/public/assets/045-avatar-DgiFAlxj.js +1 -0
- package/dist/public/assets/045-man-2-BZPavI2b.js +1 -0
- package/dist/public/assets/045-young-B39gytpm.js +1 -0
- package/dist/public/assets/046-avatar-ClHeZPOV.js +1 -0
- package/dist/public/assets/046-brother-BSvJBKnA.js +1 -0
- package/dist/public/assets/046-witch-CVjM2-iJ.js +1 -0
- package/dist/public/assets/046-woman-1-CHWuzLfU.js +1 -0
- package/dist/public/assets/047-avatar-DM01i1ZC.js +1 -0
- package/dist/public/assets/047-king-knA4oWNa.js +1 -0
- package/dist/public/assets/047-man-1-DRWdGzL5.js +1 -0
- package/dist/public/assets/048-father-B0wiM2h6.js +1 -0
- package/dist/public/assets/048-gaming-D7s83-h3.js +1 -0
- package/dist/public/assets/048-girl-CxGcfLgU.js +1 -0
- package/dist/public/assets/049-avatar-BfGDVwlO.js +1 -0
- package/dist/public/assets/049-little boy-Bu5mmsUf.js +1 -0
- package/dist/public/assets/049-man-B3xcAso3.js +1 -0
- package/dist/public/assets/050-avatar-itJOA0dd.js +1 -0
- package/dist/public/assets/050-boy-DEIcVyYT.js +1 -0
- package/dist/public/assets/050-knight-KRUkfXDS.js +1 -0
- package/dist/public/assets/051-avatar-fISL-mLU.js +1 -0
- package/dist/public/assets/052-avatar-BYrM0aPD.js +1 -0
- package/dist/public/assets/053-avatar-B-rnufvm.js +1 -0
- package/dist/public/assets/054-avatar-B7rwS7Qv.js +1 -0
- package/dist/public/assets/055-gaming-DqUOmMvT.js +1 -0
- package/dist/public/assets/056-avatar-D1lb7KL9.js +1 -0
- package/dist/public/assets/057-avatar-Df7NRaJW.js +1 -0
- package/dist/public/assets/058-avatar-C8zso2zk.js +1 -0
- package/dist/public/assets/059-avatar-zEhHpOcr.js +1 -0
- package/dist/public/assets/060-avatar-DkNabW8M.js +1 -0
- package/dist/public/assets/index-CWvY7i3l.css +1 -0
- package/dist/public/assets/index-Zxr-JuKD.js +820 -0
- package/dist/public/index.html +14 -0
- package/dist/public/logo.svg +42 -0
- package/dist/server/src/dao/FridayAppMessage.js +103 -0
- package/dist/server/src/dao/InputRequest.js +99 -0
- package/dist/server/src/dao/Message.js +42 -0
- package/dist/server/src/dao/Reply.js +96 -0
- package/dist/server/src/dao/Run.js +395 -0
- package/dist/server/src/dao/Trace.js +643 -0
- package/dist/server/src/database.js +54 -0
- package/dist/server/src/index.js +211 -0
- package/dist/server/src/migrations/1730000000000-AddMessageReplyForeignKey.js +216 -0
- package/dist/server/src/migrations/1740000000000-MigrateSpanTable.js +638 -0
- package/dist/server/src/migrations/index.js +19 -0
- package/dist/server/src/models/FridayApp.js +69 -0
- package/dist/server/src/models/FridayAppView.js +94 -0
- package/dist/server/src/models/InputRequest.js +41 -0
- package/dist/server/src/models/Message.js +39 -0
- package/dist/server/src/models/ModelInvocationView.js +127 -0
- package/dist/server/src/models/Reply.js +50 -0
- package/dist/server/src/models/Run.js +63 -0
- package/dist/server/src/models/RunView.js +111 -0
- package/dist/server/src/models/Trace.js +154 -0
- package/dist/server/src/otel/grpc-server.js +154 -0
- package/dist/server/src/otel/opentelemetry/proto/collector/trace/v1/trace_service.js +309 -0
- package/dist/server/src/otel/opentelemetry/proto/common/v1/common.js +747 -0
- package/dist/server/src/otel/opentelemetry/proto/resource/v1/resource.js +170 -0
- package/dist/server/src/otel/opentelemetry/proto/trace/v1/trace.js +1134 -0
- package/dist/server/src/otel/processor.js +395 -0
- package/dist/server/src/otel/router.js +128 -0
- package/dist/server/src/services/ReplyingStateManager.js +21 -0
- package/dist/server/src/trpc/router.js +385 -0
- package/dist/server/src/trpc/socket.js +589 -0
- package/dist/server/src/utils/index.js +17 -0
- package/dist/server/src/utils/processUtils.js +27 -0
- package/dist/shared/src/config/client.js +6 -0
- package/dist/shared/src/config/common.js +32 -0
- package/dist/shared/src/config/friday.js +168 -0
- package/dist/shared/src/config/index.js +19 -0
- package/dist/shared/src/config/server.js +104 -0
- package/dist/shared/src/index.js +18 -0
- package/dist/shared/src/types/index.js +20 -0
- package/dist/shared/src/types/messageForm.js +25 -0
- package/dist/shared/src/types/trace.js +20 -0
- package/dist/shared/src/types/trpc.js +98 -0
- package/dist/shared/src/types/usage.js +7 -0
- package/dist/shared/src/utils/banner.js +74 -0
- package/dist/shared/src/utils/objectUtils.js +74 -0
- package/dist/shared/src/utils/terminal.js +30 -0
- package/dist/shared/src/utils/timeUtils.js +86 -0
- package/package.json +89 -0
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SpanDao = void 0;
|
|
13
|
+
const objectUtils_1 = require("../../../shared/src/utils/objectUtils");
|
|
14
|
+
const ModelInvocationView_1 = require("../models/ModelInvocationView");
|
|
15
|
+
const Trace_1 = require("../models/Trace");
|
|
16
|
+
class SpanDao {
|
|
17
|
+
static saveSpans(dataArray) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
try {
|
|
20
|
+
// Create SpanTable instances with embedded resource and scope data
|
|
21
|
+
const spans = dataArray.map((data) => {
|
|
22
|
+
// Extract key fields for indexing
|
|
23
|
+
const serviceName = this.extractServiceName(data.resource);
|
|
24
|
+
const operationName = this.extractOperationName(data.attributes);
|
|
25
|
+
const instrumentationName = this.extractInstrumentationName(data.scope);
|
|
26
|
+
const instrumentationVersion = this.extractInstrumentationVersion(data.scope);
|
|
27
|
+
const model = this.extractModel(data.attributes);
|
|
28
|
+
const inputTokens = this.extractInputTokens(data.attributes);
|
|
29
|
+
const outputTokens = this.extractOutputTokens(data.attributes);
|
|
30
|
+
const totalTokens = this.calculateTotalTokens(inputTokens, outputTokens);
|
|
31
|
+
const statusCode = data.status.code || 0;
|
|
32
|
+
const span = new Trace_1.SpanTable();
|
|
33
|
+
Object.assign(span, {
|
|
34
|
+
id: data.spanId, // Use spanId as the primary key
|
|
35
|
+
traceId: data.traceId,
|
|
36
|
+
spanId: data.spanId,
|
|
37
|
+
traceState: data.traceState,
|
|
38
|
+
parentSpanId: data.parentSpanId,
|
|
39
|
+
flags: data.flags,
|
|
40
|
+
name: data.name,
|
|
41
|
+
kind: data.kind, // Now it's a number (OpenTelemetry API enum)
|
|
42
|
+
startTimeUnixNano: data.startTimeUnixNano,
|
|
43
|
+
endTimeUnixNano: data.endTimeUnixNano,
|
|
44
|
+
attributes: data.attributes,
|
|
45
|
+
droppedAttributesCount: data.droppedAttributesCount,
|
|
46
|
+
events: data.events,
|
|
47
|
+
droppedEventsCount: data.droppedEventsCount,
|
|
48
|
+
links: data.links,
|
|
49
|
+
droppedLinksCount: data.droppedLinksCount,
|
|
50
|
+
status: data.status,
|
|
51
|
+
resource: data.resource,
|
|
52
|
+
scope: data.scope,
|
|
53
|
+
// Additional fields for our application
|
|
54
|
+
statusCode: statusCode,
|
|
55
|
+
serviceName: serviceName,
|
|
56
|
+
operationName: operationName,
|
|
57
|
+
instrumentationName: instrumentationName,
|
|
58
|
+
instrumentationVersion: instrumentationVersion,
|
|
59
|
+
model: model,
|
|
60
|
+
inputTokens: inputTokens,
|
|
61
|
+
outputTokens: outputTokens,
|
|
62
|
+
totalTokens: totalTokens,
|
|
63
|
+
conversationId: data.conversationId,
|
|
64
|
+
latencyNs: data.latencyNs,
|
|
65
|
+
});
|
|
66
|
+
return span;
|
|
67
|
+
});
|
|
68
|
+
// Save all spans in a single transaction
|
|
69
|
+
return yield Trace_1.SpanTable.save(spans);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error('Error saving spans:', error);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
static getSpansByConversationId(conversationId) {
|
|
78
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
try {
|
|
80
|
+
const spans = yield Trace_1.SpanTable.find({
|
|
81
|
+
where: { conversationId },
|
|
82
|
+
order: { startTimeUnixNano: 'ASC' },
|
|
83
|
+
});
|
|
84
|
+
return spans.map((span) => ({
|
|
85
|
+
traceId: span.traceId,
|
|
86
|
+
spanId: span.spanId,
|
|
87
|
+
traceState: span.traceState,
|
|
88
|
+
parentSpanId: span.parentSpanId,
|
|
89
|
+
flags: span.flags,
|
|
90
|
+
name: span.name,
|
|
91
|
+
kind: span.kind,
|
|
92
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
93
|
+
endTimeUnixNano: span.endTimeUnixNano,
|
|
94
|
+
attributes: span.attributes,
|
|
95
|
+
droppedAttributesCount: span.droppedAttributesCount || 0,
|
|
96
|
+
events: (span.events || []),
|
|
97
|
+
droppedEventsCount: span.droppedEventsCount || 0,
|
|
98
|
+
links: (span.links || []),
|
|
99
|
+
droppedLinksCount: span.droppedLinksCount || 0,
|
|
100
|
+
status: span.status,
|
|
101
|
+
resource: span.resource,
|
|
102
|
+
scope: span.scope,
|
|
103
|
+
conversationId: span.conversationId,
|
|
104
|
+
latencyNs: span.latencyNs,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error(`Error fetching spans for conversationId ${conversationId}:`, error);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Helper methods to extract key fields from nested data
|
|
114
|
+
static calculateTotalTokens(inputTokens, outputTokens) {
|
|
115
|
+
if (typeof inputTokens === 'number' &&
|
|
116
|
+
typeof outputTokens === 'number') {
|
|
117
|
+
return inputTokens + outputTokens;
|
|
118
|
+
}
|
|
119
|
+
if (typeof inputTokens === 'number') {
|
|
120
|
+
return inputTokens;
|
|
121
|
+
}
|
|
122
|
+
if (typeof outputTokens === 'number') {
|
|
123
|
+
return outputTokens;
|
|
124
|
+
}
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
static extractServiceName(resource) {
|
|
128
|
+
const value = (0, objectUtils_1.getNestedValue)(resource.attributes, 'service.name');
|
|
129
|
+
return typeof value === 'string' ? value : undefined;
|
|
130
|
+
}
|
|
131
|
+
static extractOperationName(attributes) {
|
|
132
|
+
const value = (0, objectUtils_1.getNestedValue)(attributes, 'gen_ai.operation.name');
|
|
133
|
+
return typeof value === 'string' ? value : undefined;
|
|
134
|
+
}
|
|
135
|
+
static extractInstrumentationName(scope) {
|
|
136
|
+
const value = (0, objectUtils_1.getNestedValue)(scope.attributes, 'server.name');
|
|
137
|
+
return typeof value === 'string' ? value : undefined;
|
|
138
|
+
}
|
|
139
|
+
static extractInstrumentationVersion(scope) {
|
|
140
|
+
const value = (0, objectUtils_1.getNestedValue)(scope.attributes, 'server.version');
|
|
141
|
+
return typeof value === 'string' ? value : undefined;
|
|
142
|
+
}
|
|
143
|
+
static extractModel(attributes) {
|
|
144
|
+
const value = (0, objectUtils_1.getNestedValue)(attributes, 'gen_ai.request.model');
|
|
145
|
+
return typeof value === 'string' ? value : undefined;
|
|
146
|
+
}
|
|
147
|
+
static extractInputTokens(attributes) {
|
|
148
|
+
const value = (0, objectUtils_1.getNestedValue)(attributes, 'gen_ai.usage.input_tokens');
|
|
149
|
+
return typeof value === 'number' ? value : undefined;
|
|
150
|
+
}
|
|
151
|
+
static extractOutputTokens(attributes) {
|
|
152
|
+
const value = (0, objectUtils_1.getNestedValue)(attributes, 'gen_ai.usage.output_tokens');
|
|
153
|
+
return typeof value === 'number' ? value : undefined;
|
|
154
|
+
}
|
|
155
|
+
// Trace listing and filtering methods
|
|
156
|
+
static getLatestTraces() {
|
|
157
|
+
return __awaiter(this, arguments, void 0, function* (limit = 10) {
|
|
158
|
+
return yield Trace_1.SpanTable.find({
|
|
159
|
+
order: { startTimeUnixNano: 'DESC' },
|
|
160
|
+
take: limit,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
static getTracesByTraceId(traceId) {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
+
return yield Trace_1.SpanTable.find({
|
|
167
|
+
where: { traceId },
|
|
168
|
+
order: { startTimeUnixNano: 'ASC' },
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
static getSpanById(spanId) {
|
|
173
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
174
|
+
return yield Trace_1.SpanTable.findOne({
|
|
175
|
+
where: { spanId },
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
static searchTraces(filters) {
|
|
180
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
181
|
+
const queryBuilder = Trace_1.SpanTable.createQueryBuilder('span');
|
|
182
|
+
if (filters.serviceName) {
|
|
183
|
+
queryBuilder.andWhere('span.serviceName = :serviceName', {
|
|
184
|
+
serviceName: filters.serviceName,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (filters.operationName) {
|
|
188
|
+
queryBuilder.andWhere('span.operationName = :operationName', {
|
|
189
|
+
operationName: filters.operationName,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (filters.instrumentationName) {
|
|
193
|
+
queryBuilder.andWhere('span.instrumentationName = :instrumentationName', { instrumentationName: filters.instrumentationName });
|
|
194
|
+
}
|
|
195
|
+
if (filters.model) {
|
|
196
|
+
queryBuilder.andWhere('span.model = :model', {
|
|
197
|
+
model: filters.model,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (filters.status !== undefined) {
|
|
201
|
+
queryBuilder.andWhere('span.statusCode = :statusCode', {
|
|
202
|
+
statusCode: filters.status,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (filters.startTime) {
|
|
206
|
+
queryBuilder.andWhere('span.startTimeUnixNano >= :startTime', {
|
|
207
|
+
startTime: filters.startTime,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (filters.endTime) {
|
|
211
|
+
queryBuilder.andWhere('span.startTimeUnixNano <= :endTime', {
|
|
212
|
+
endTime: filters.endTime,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
queryBuilder.orderBy('span.startTimeUnixNano', 'DESC');
|
|
216
|
+
if (filters.limit) {
|
|
217
|
+
queryBuilder.limit(filters.limit);
|
|
218
|
+
}
|
|
219
|
+
return yield queryBuilder.getMany();
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
static getModelInvocationViewData() {
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
const res = yield ModelInvocationView_1.ModelInvocationView.find();
|
|
225
|
+
if (res.length > 0) {
|
|
226
|
+
return res[0];
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
throw new Error('ModelInvocationView data not found');
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
static getModelInvocationData(conversationId) {
|
|
234
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
235
|
+
// 1. Basic statistics
|
|
236
|
+
const basicStats = yield Trace_1.SpanTable.createQueryBuilder('span')
|
|
237
|
+
.select(`COUNT(CASE
|
|
238
|
+
WHEN (span.operationName = 'chat'
|
|
239
|
+
OR span.operationName = 'chat_model')
|
|
240
|
+
THEN 1
|
|
241
|
+
END)`, 'totalInvocations')
|
|
242
|
+
.addSelect(`COUNT(CASE
|
|
243
|
+
WHEN (span.operationName = 'chat'
|
|
244
|
+
OR span.operationName = 'chat_model')
|
|
245
|
+
AND span.totalTokens IS NOT NULL
|
|
246
|
+
THEN 1
|
|
247
|
+
END)`, 'chatInvocations')
|
|
248
|
+
.where('span.conversationId = :conversationId', { conversationId })
|
|
249
|
+
.getRawOne();
|
|
250
|
+
// 2. Chat token statistics (total and average)
|
|
251
|
+
const chatTokenStats = yield Trace_1.SpanTable.createQueryBuilder('span')
|
|
252
|
+
.select([
|
|
253
|
+
// Total - input tokens
|
|
254
|
+
`COALESCE(SUM(
|
|
255
|
+
CASE WHEN (span.operationName = 'chat'
|
|
256
|
+
OR span.operationName = 'chat_model')
|
|
257
|
+
AND span.totalTokens IS NOT NULL
|
|
258
|
+
THEN CAST(COALESCE(span.inputTokens, 0) AS INTEGER)
|
|
259
|
+
ELSE 0 END
|
|
260
|
+
), 0) as totalPromptTokens`,
|
|
261
|
+
// Total - output tokens
|
|
262
|
+
`COALESCE(SUM(
|
|
263
|
+
CASE WHEN (span.operationName = 'chat'
|
|
264
|
+
OR span.operationName = 'chat_model')
|
|
265
|
+
AND span.totalTokens IS NOT NULL
|
|
266
|
+
THEN CAST(COALESCE(span.outputTokens, 0) AS INTEGER)
|
|
267
|
+
ELSE 0 END
|
|
268
|
+
), 0) as totalCompletionTokens`,
|
|
269
|
+
// Total - total tokens
|
|
270
|
+
`COALESCE(SUM(
|
|
271
|
+
CASE WHEN (span.operationName = 'chat'
|
|
272
|
+
OR span.operationName = 'chat_model')
|
|
273
|
+
AND span.totalTokens IS NOT NULL
|
|
274
|
+
THEN CAST(COALESCE(span.totalTokens, 0) AS INTEGER)
|
|
275
|
+
ELSE 0 END
|
|
276
|
+
), 0) as totalTokens`,
|
|
277
|
+
// Average - input tokens
|
|
278
|
+
`COALESCE(
|
|
279
|
+
CAST(SUM(
|
|
280
|
+
CASE WHEN (span.operationName = 'chat'
|
|
281
|
+
OR span.operationName = 'chat_model')
|
|
282
|
+
AND span.totalTokens IS NOT NULL
|
|
283
|
+
THEN CAST(COALESCE(span.inputTokens, 0) AS INTEGER)
|
|
284
|
+
ELSE 0 END
|
|
285
|
+
) AS FLOAT) /
|
|
286
|
+
NULLIF(COUNT(CASE WHEN (span.operationName = 'chat'
|
|
287
|
+
OR span.operationName = 'chat_model')
|
|
288
|
+
AND span.totalTokens IS NOT NULL THEN 1 END), 0)
|
|
289
|
+
, 0) as avgPromptTokens`,
|
|
290
|
+
// Average - output tokens
|
|
291
|
+
`COALESCE(
|
|
292
|
+
CAST(SUM(
|
|
293
|
+
CASE WHEN (span.operationName = 'chat'
|
|
294
|
+
OR span.operationName = 'chat_model')
|
|
295
|
+
AND span.totalTokens IS NOT NULL
|
|
296
|
+
THEN CAST(COALESCE(span.outputTokens, 0) AS INTEGER)
|
|
297
|
+
ELSE 0 END
|
|
298
|
+
) AS FLOAT) /
|
|
299
|
+
NULLIF(COUNT(CASE WHEN (span.operationName = 'chat'
|
|
300
|
+
OR span.operationName = 'chat_model')
|
|
301
|
+
AND span.totalTokens IS NOT NULL THEN 1 END), 0)
|
|
302
|
+
, 0) as avgCompletionTokens`,
|
|
303
|
+
// Average - total tokens
|
|
304
|
+
`COALESCE(
|
|
305
|
+
CAST(SUM(
|
|
306
|
+
CASE WHEN (span.operationName = 'chat'
|
|
307
|
+
OR span.operationName = 'chat_model')
|
|
308
|
+
AND span.totalTokens IS NOT NULL
|
|
309
|
+
THEN CAST(COALESCE(span.totalTokens, 0) AS INTEGER)
|
|
310
|
+
ELSE 0 END
|
|
311
|
+
) AS FLOAT) /
|
|
312
|
+
NULLIF(COUNT(CASE WHEN (span.operationName = 'chat'
|
|
313
|
+
OR span.operationName = 'chat_model')
|
|
314
|
+
AND span.totalTokens IS NOT NULL THEN 1 END), 0)
|
|
315
|
+
, 0) as avgTotalTokens`,
|
|
316
|
+
])
|
|
317
|
+
.where('span.conversationId = :conversationId', { conversationId })
|
|
318
|
+
.getRawOne();
|
|
319
|
+
// 3. Model invocation statistics (grouped by model)
|
|
320
|
+
const modelInvocations = yield Trace_1.SpanTable.createQueryBuilder('span')
|
|
321
|
+
.select(['span.model as modelName', 'COUNT(*) as invocations'])
|
|
322
|
+
.where('span.conversationId = :conversationId', { conversationId })
|
|
323
|
+
.andWhere("(span.operationName = 'chat' OR span.operationName = 'chat_model')")
|
|
324
|
+
.andWhere('span.totalTokens IS NOT NULL')
|
|
325
|
+
.groupBy('modelName')
|
|
326
|
+
.getRawMany();
|
|
327
|
+
// 4. Model token statistics (grouped by model)
|
|
328
|
+
const modelTokenStats = yield Trace_1.SpanTable.createQueryBuilder('span')
|
|
329
|
+
.select([
|
|
330
|
+
'span.model as modelName',
|
|
331
|
+
// Total
|
|
332
|
+
`SUM(CAST(COALESCE(span.inputTokens, 0) AS INTEGER)) as totalPromptTokens`,
|
|
333
|
+
`SUM(CAST(COALESCE(span.outputTokens, 0) AS INTEGER)) as totalCompletionTokens`,
|
|
334
|
+
`SUM(CAST(COALESCE(span.totalTokens, 0) AS INTEGER)) as totalTokens`,
|
|
335
|
+
// Average
|
|
336
|
+
`CAST(SUM(CAST(COALESCE(span.inputTokens, 0) AS INTEGER)) AS FLOAT) / COUNT(*) as avgPromptTokens`,
|
|
337
|
+
`CAST(SUM(CAST(COALESCE(span.outputTokens, 0) AS INTEGER)) AS FLOAT) / COUNT(*) as avgCompletionTokens`,
|
|
338
|
+
`CAST(SUM(CAST(COALESCE(span.totalTokens, 0) AS INTEGER)) AS FLOAT) / COUNT(*) as avgTotalTokens`,
|
|
339
|
+
])
|
|
340
|
+
.where('span.conversationId = :conversationId', { conversationId })
|
|
341
|
+
.andWhere("(span.operationName = 'chat' OR span.operationName = 'chat_model')")
|
|
342
|
+
.andWhere('span.totalTokens IS NOT NULL')
|
|
343
|
+
.groupBy('modelName')
|
|
344
|
+
.getRawMany();
|
|
345
|
+
// 5. Build return structure
|
|
346
|
+
return {
|
|
347
|
+
modelInvocations: Number(basicStats.totalInvocations),
|
|
348
|
+
chat: {
|
|
349
|
+
modelInvocations: Number(basicStats.chatInvocations),
|
|
350
|
+
totalTokens: {
|
|
351
|
+
promptTokens: Number(chatTokenStats.totalPromptTokens),
|
|
352
|
+
completionTokens: Number(chatTokenStats.totalCompletionTokens),
|
|
353
|
+
totalTokens: Number(chatTokenStats.totalTokens),
|
|
354
|
+
},
|
|
355
|
+
avgTokens: {
|
|
356
|
+
promptTokens: Number(chatTokenStats.avgPromptTokens),
|
|
357
|
+
completionTokens: Number(chatTokenStats.avgCompletionTokens),
|
|
358
|
+
totalTokens: Number(chatTokenStats.avgTotalTokens),
|
|
359
|
+
},
|
|
360
|
+
modelInvocationsByModel: modelInvocations.map((stat) => ({
|
|
361
|
+
modelName: stat.modelName || 'unknown',
|
|
362
|
+
invocations: Number(stat.invocations),
|
|
363
|
+
})),
|
|
364
|
+
totalTokensByModel: modelTokenStats.map((stat) => ({
|
|
365
|
+
modelName: stat.modelName || 'unknown',
|
|
366
|
+
promptTokens: Number(stat.totalPromptTokens),
|
|
367
|
+
completionTokens: Number(stat.totalCompletionTokens),
|
|
368
|
+
totalTokens: Number(stat.totalTokens),
|
|
369
|
+
})),
|
|
370
|
+
avgTokensByModel: modelTokenStats.map((stat) => ({
|
|
371
|
+
modelName: stat.modelName || 'unknown',
|
|
372
|
+
promptTokens: Number(stat.avgPromptTokens),
|
|
373
|
+
completionTokens: Number(stat.avgCompletionTokens),
|
|
374
|
+
totalTokens: Number(stat.avgTotalTokens),
|
|
375
|
+
})),
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
static deleteSpansByConversationIds(conversationIds) {
|
|
381
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
382
|
+
try {
|
|
383
|
+
if (conversationIds.length === 0) {
|
|
384
|
+
return 0;
|
|
385
|
+
}
|
|
386
|
+
const result = yield Trace_1.SpanTable.createQueryBuilder()
|
|
387
|
+
.delete()
|
|
388
|
+
.where('conversationId IN (:...conversationIds)', {
|
|
389
|
+
conversationIds,
|
|
390
|
+
})
|
|
391
|
+
.execute();
|
|
392
|
+
return result.affected || 0;
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
console.error('Error deleting spans by conversationIds:', error);
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
// Get unique trace IDs with aggregated information
|
|
401
|
+
static getTraceList(filters) {
|
|
402
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
403
|
+
try {
|
|
404
|
+
const queryBuilder = Trace_1.SpanTable.createQueryBuilder('span')
|
|
405
|
+
.select('span.traceId', 'traceId')
|
|
406
|
+
.addSelect('MIN(span.startTimeUnixNano)', 'startTime')
|
|
407
|
+
.addSelect('MAX(span.endTimeUnixNano)', 'endTime')
|
|
408
|
+
.addSelect('MIN(span.name)', 'name')
|
|
409
|
+
.addSelect('MAX(span.statusCode)', 'status')
|
|
410
|
+
.addSelect('COUNT(span.id)', 'spanCount')
|
|
411
|
+
.addSelect('SUM(COALESCE(span.totalTokens, 0))', 'totalTokens')
|
|
412
|
+
.groupBy('span.traceId');
|
|
413
|
+
if (filters.serviceName) {
|
|
414
|
+
queryBuilder.andWhere('span.serviceName = :serviceName', {
|
|
415
|
+
serviceName: filters.serviceName,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
if (filters.operationName) {
|
|
419
|
+
queryBuilder.andWhere('span.operationName = :operationName', {
|
|
420
|
+
operationName: filters.operationName,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (filters.status !== undefined) {
|
|
424
|
+
queryBuilder.andWhere('span.statusCode = :statusCode', {
|
|
425
|
+
statusCode: filters.status,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
if (filters.startTime) {
|
|
429
|
+
queryBuilder.andWhere('span.startTimeUnixNano >= :startTime', {
|
|
430
|
+
startTime: filters.startTime,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
if (filters.endTime) {
|
|
434
|
+
queryBuilder.andWhere('span.startTimeUnixNano <= :endTime', {
|
|
435
|
+
endTime: filters.endTime,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
// Get total count (before pagination)
|
|
439
|
+
const countQuery = queryBuilder.clone();
|
|
440
|
+
const totalResult = yield countQuery.getRawMany();
|
|
441
|
+
const total = totalResult.length;
|
|
442
|
+
// Apply ordering and pagination
|
|
443
|
+
// Use the aggregate expression directly for ordering
|
|
444
|
+
queryBuilder.orderBy('MIN(span.startTimeUnixNano)', 'DESC');
|
|
445
|
+
if (filters.limit) {
|
|
446
|
+
queryBuilder.limit(filters.limit);
|
|
447
|
+
}
|
|
448
|
+
if (filters.offset) {
|
|
449
|
+
queryBuilder.offset(filters.offset);
|
|
450
|
+
}
|
|
451
|
+
const results = yield queryBuilder.getRawMany();
|
|
452
|
+
console.debug(`[TraceDao] getTraceList: found ${total} traces, returning ${results.length} with limit=${filters.limit}, offset=${filters.offset}`);
|
|
453
|
+
const traceIds = results.map((row) => row.traceId);
|
|
454
|
+
// Get root spans (spans with no parent or empty parent) for the traces in results
|
|
455
|
+
const rootSpans = traceIds.length > 0
|
|
456
|
+
? yield Trace_1.SpanTable.createQueryBuilder('span')
|
|
457
|
+
.where('span.traceId IN (:...traceIds)', { traceIds })
|
|
458
|
+
.andWhere('(span.parentSpanId IS NULL OR span.parentSpanId = :emptyString)', { emptyString: '' })
|
|
459
|
+
.select(['span.traceId', 'span.name'])
|
|
460
|
+
.getMany()
|
|
461
|
+
: [];
|
|
462
|
+
const rootSpanMap = new Map(rootSpans.map((span) => [span.traceId, span]));
|
|
463
|
+
const traces = results.map((row) => {
|
|
464
|
+
try {
|
|
465
|
+
const startTimeNs = BigInt(row.startTime || '0');
|
|
466
|
+
const endTimeNs = BigInt(row.endTime || '0');
|
|
467
|
+
const duration = Number(endTimeNs - startTimeNs) / 1e9; // Convert nanoseconds to seconds
|
|
468
|
+
const rootSpan = rootSpanMap.get(row.traceId);
|
|
469
|
+
return {
|
|
470
|
+
traceId: row.traceId || '',
|
|
471
|
+
name: (rootSpan === null || rootSpan === void 0 ? void 0 : rootSpan.name) || row.name || 'Unknown',
|
|
472
|
+
startTime: row.startTime || '0',
|
|
473
|
+
endTime: row.endTime || '0',
|
|
474
|
+
duration,
|
|
475
|
+
status: Number(row.status) || 0,
|
|
476
|
+
spanCount: Number(row.spanCount) || 0,
|
|
477
|
+
totalTokens: row.totalTokens !== null &&
|
|
478
|
+
row.totalTokens !== undefined
|
|
479
|
+
? Number(row.totalTokens)
|
|
480
|
+
: undefined,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
console.error('Error processing trace row:', row, err);
|
|
485
|
+
throw err;
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
console.debug(`[TraceDao] getTraceList: returning ${traces.length} traces`);
|
|
489
|
+
return { traces, total };
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
console.error('Error getting trace list:', error);
|
|
493
|
+
throw error;
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
// Get a single trace with all spans
|
|
498
|
+
static getTrace(traceId) {
|
|
499
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
500
|
+
try {
|
|
501
|
+
const spans = yield this.getTracesByTraceId(traceId);
|
|
502
|
+
if (spans.length === 0) {
|
|
503
|
+
throw new Error(`Trace with id ${traceId} not found`);
|
|
504
|
+
}
|
|
505
|
+
// Calculate trace-level statistics
|
|
506
|
+
const startTimes = spans.map((s) => BigInt(s.startTimeUnixNano));
|
|
507
|
+
const endTimes = spans.map((s) => BigInt(s.endTimeUnixNano));
|
|
508
|
+
const minStartTime = startTimes.reduce((a, b) => (a < b ? a : b));
|
|
509
|
+
const maxEndTime = endTimes.reduce((a, b) => (a > b ? a : b));
|
|
510
|
+
const duration = Number(maxEndTime - minStartTime) / 1e9;
|
|
511
|
+
// Get status (ERROR if any span has error status)
|
|
512
|
+
const status = spans.some((s) => s.statusCode === 2) ? 2 : 1;
|
|
513
|
+
// Calculate total tokens
|
|
514
|
+
const totalTokens = spans.reduce((sum, s) => sum + (s.totalTokens || 0), 0);
|
|
515
|
+
const spanDataArray = spans.map((span) => ({
|
|
516
|
+
traceId: span.traceId,
|
|
517
|
+
spanId: span.spanId,
|
|
518
|
+
traceState: span.traceState,
|
|
519
|
+
parentSpanId: span.parentSpanId,
|
|
520
|
+
flags: span.flags,
|
|
521
|
+
name: span.name,
|
|
522
|
+
kind: span.kind,
|
|
523
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
524
|
+
endTimeUnixNano: span.endTimeUnixNano,
|
|
525
|
+
attributes: span.attributes,
|
|
526
|
+
droppedAttributesCount: span.droppedAttributesCount || 0,
|
|
527
|
+
events: (span.events || []),
|
|
528
|
+
droppedEventsCount: span.droppedEventsCount || 0,
|
|
529
|
+
links: (span.links || []),
|
|
530
|
+
droppedLinksCount: span.droppedLinksCount || 0,
|
|
531
|
+
status: span.status,
|
|
532
|
+
resource: span.resource,
|
|
533
|
+
scope: span.scope,
|
|
534
|
+
conversationId: span.conversationId,
|
|
535
|
+
latencyNs: span.latencyNs,
|
|
536
|
+
}));
|
|
537
|
+
return {
|
|
538
|
+
traceId,
|
|
539
|
+
spans: spanDataArray,
|
|
540
|
+
startTime: minStartTime.toString(),
|
|
541
|
+
endTime: maxEndTime.toString(),
|
|
542
|
+
duration,
|
|
543
|
+
status,
|
|
544
|
+
totalTokens: totalTokens > 0 ? totalTokens : undefined,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
console.error(`Error getting trace ${traceId}:`, error);
|
|
549
|
+
throw error;
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
// Get trace statistics
|
|
554
|
+
static getTraceStatistic(filters) {
|
|
555
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
556
|
+
try {
|
|
557
|
+
const queryBuilder = Trace_1.SpanTable.createQueryBuilder('span');
|
|
558
|
+
if (filters === null || filters === void 0 ? void 0 : filters.serviceName) {
|
|
559
|
+
queryBuilder.andWhere('span.serviceName = :serviceName', {
|
|
560
|
+
serviceName: filters.serviceName,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
if (filters === null || filters === void 0 ? void 0 : filters.operationName) {
|
|
564
|
+
queryBuilder.andWhere('span.operationName = :operationName', {
|
|
565
|
+
operationName: filters.operationName,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
if (filters === null || filters === void 0 ? void 0 : filters.startTime) {
|
|
569
|
+
queryBuilder.andWhere('span.startTimeUnixNano >= :startTime', {
|
|
570
|
+
startTime: filters.startTime,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
if (filters === null || filters === void 0 ? void 0 : filters.endTime) {
|
|
574
|
+
queryBuilder.andWhere('span.startTimeUnixNano <= :endTime', {
|
|
575
|
+
endTime: filters.endTime,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
// Get total spans
|
|
579
|
+
const totalSpans = yield queryBuilder.getCount();
|
|
580
|
+
// Get unique trace count
|
|
581
|
+
const uniqueTracesQuery = queryBuilder
|
|
582
|
+
.clone()
|
|
583
|
+
.select('COUNT(DISTINCT span.traceId)', 'count')
|
|
584
|
+
.getRawOne();
|
|
585
|
+
const totalTraces = Number((yield uniqueTracesQuery).count || 0);
|
|
586
|
+
// Get error traces count
|
|
587
|
+
const errorTracesQuery = queryBuilder
|
|
588
|
+
.clone()
|
|
589
|
+
.select('COUNT(DISTINCT span.traceId)', 'count')
|
|
590
|
+
.andWhere('span.statusCode = :statusCode', { statusCode: 2 })
|
|
591
|
+
.getRawOne();
|
|
592
|
+
const errorTraces = Number((yield errorTracesQuery).count || 0);
|
|
593
|
+
// Get average duration
|
|
594
|
+
const durationQuery = yield queryBuilder
|
|
595
|
+
.clone()
|
|
596
|
+
.select('span.traceId', 'traceId')
|
|
597
|
+
.addSelect('MIN(span.startTimeUnixNano)', 'startTime')
|
|
598
|
+
.addSelect('MAX(span.endTimeUnixNano)', 'endTime')
|
|
599
|
+
.groupBy('span.traceId')
|
|
600
|
+
.getRawMany();
|
|
601
|
+
const durations = durationQuery.map((row) => {
|
|
602
|
+
const startTimeNs = BigInt(row.startTime);
|
|
603
|
+
const endTimeNs = BigInt(row.endTime);
|
|
604
|
+
return Number(endTimeNs - startTimeNs) / 1e9;
|
|
605
|
+
});
|
|
606
|
+
const avgDuration = durations.length > 0
|
|
607
|
+
? durations.reduce((a, b) => a + b, 0) /
|
|
608
|
+
durations.length
|
|
609
|
+
: 0;
|
|
610
|
+
// Get total tokens
|
|
611
|
+
const tokensQuery = queryBuilder
|
|
612
|
+
.clone()
|
|
613
|
+
.select('SUM(COALESCE(span.totalTokens, 0))', 'total')
|
|
614
|
+
.getRawOne();
|
|
615
|
+
const totalTokens = Number((yield tokensQuery).total || 0);
|
|
616
|
+
// Get traces by status
|
|
617
|
+
const statusQuery = yield queryBuilder
|
|
618
|
+
.clone()
|
|
619
|
+
.select('span.statusCode', 'status')
|
|
620
|
+
.addSelect('COUNT(DISTINCT span.traceId)', 'count')
|
|
621
|
+
.groupBy('span.statusCode')
|
|
622
|
+
.getRawMany();
|
|
623
|
+
const tracesByStatus = statusQuery.map((row) => ({
|
|
624
|
+
status: Number(row.status) || 0,
|
|
625
|
+
count: Number(row.count) || 0,
|
|
626
|
+
}));
|
|
627
|
+
return {
|
|
628
|
+
totalTraces,
|
|
629
|
+
totalSpans,
|
|
630
|
+
errorTraces,
|
|
631
|
+
avgDuration,
|
|
632
|
+
totalTokens,
|
|
633
|
+
tracesByStatus,
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
catch (error) {
|
|
637
|
+
console.error('Error getting trace statistics:', error);
|
|
638
|
+
throw error;
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
exports.SpanDao = SpanDao;
|