@antseed/node 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/seller-provider.d.ts +13 -1
- package/dist/interfaces/seller-provider.d.ts.map +1 -1
- package/dist/node.d.ts +13 -3
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +123 -15
- package/dist/node.js.map +1 -1
- package/dist/proxy/proxy-mux.d.ts +3 -1
- package/dist/proxy/proxy-mux.d.ts.map +1 -1
- package/dist/proxy/proxy-mux.js +9 -5
- package/dist/proxy/proxy-mux.js.map +1 -1
- package/dist/types/http.d.ts +1 -0
- package/dist/types/http.d.ts.map +1 -1
- package/dist/types/http.js +1 -1
- package/dist/types/http.js.map +1 -1
- package/package.json +14 -10
- package/contracts/AntseedEscrow.sol +0 -310
- package/contracts/MockUSDC.sol +0 -64
- package/contracts/README.md +0 -102
- package/src/config/encryption.test.ts +0 -49
- package/src/config/encryption.ts +0 -53
- package/src/config/plugin-config-manager.test.ts +0 -92
- package/src/config/plugin-config-manager.ts +0 -153
- package/src/config/plugin-loader.ts +0 -90
- package/src/discovery/announcer.ts +0 -169
- package/src/discovery/bootstrap.ts +0 -57
- package/src/discovery/default-metadata-resolver.ts +0 -18
- package/src/discovery/dht-health.ts +0 -136
- package/src/discovery/dht-node.ts +0 -191
- package/src/discovery/http-metadata-resolver.ts +0 -47
- package/src/discovery/index.ts +0 -15
- package/src/discovery/metadata-codec.ts +0 -453
- package/src/discovery/metadata-resolver.ts +0 -7
- package/src/discovery/metadata-server.ts +0 -73
- package/src/discovery/metadata-validator.ts +0 -172
- package/src/discovery/peer-lookup.ts +0 -122
- package/src/discovery/peer-metadata.ts +0 -34
- package/src/discovery/peer-selector.ts +0 -134
- package/src/discovery/profile-manager.ts +0 -131
- package/src/discovery/profile-search.ts +0 -100
- package/src/discovery/reputation-verifier.ts +0 -54
- package/src/index.ts +0 -61
- package/src/interfaces/buyer-router.ts +0 -21
- package/src/interfaces/plugin.ts +0 -36
- package/src/interfaces/seller-provider.ts +0 -81
- package/src/metering/index.ts +0 -6
- package/src/metering/receipt-generator.ts +0 -105
- package/src/metering/receipt-verifier.ts +0 -102
- package/src/metering/session-tracker.ts +0 -145
- package/src/metering/storage.ts +0 -600
- package/src/metering/token-counter.ts +0 -127
- package/src/metering/usage-aggregator.ts +0 -236
- package/src/node.ts +0 -1698
- package/src/p2p/connection-auth.ts +0 -152
- package/src/p2p/connection-manager.ts +0 -916
- package/src/p2p/handshake.ts +0 -162
- package/src/p2p/ice-config.ts +0 -59
- package/src/p2p/identity.ts +0 -110
- package/src/p2p/index.ts +0 -11
- package/src/p2p/keepalive.ts +0 -118
- package/src/p2p/message-protocol.ts +0 -171
- package/src/p2p/nat-traversal.ts +0 -169
- package/src/p2p/payment-codec.ts +0 -165
- package/src/p2p/payment-mux.ts +0 -153
- package/src/p2p/reconnect.ts +0 -117
- package/src/payments/balance-manager.ts +0 -77
- package/src/payments/buyer-payment-manager.ts +0 -414
- package/src/payments/disputes.ts +0 -72
- package/src/payments/evm/escrow-client.ts +0 -263
- package/src/payments/evm/keypair.ts +0 -31
- package/src/payments/evm/signatures.ts +0 -103
- package/src/payments/evm/wallet.ts +0 -42
- package/src/payments/index.ts +0 -50
- package/src/payments/settlement.ts +0 -40
- package/src/payments/types.ts +0 -79
- package/src/proxy/index.ts +0 -3
- package/src/proxy/provider-detection.ts +0 -78
- package/src/proxy/proxy-mux.ts +0 -173
- package/src/proxy/request-codec.ts +0 -294
- package/src/reputation/index.ts +0 -6
- package/src/reputation/rating-manager.ts +0 -118
- package/src/reputation/report-manager.ts +0 -91
- package/src/reputation/trust-engine.ts +0 -120
- package/src/reputation/trust-score.ts +0 -74
- package/src/reputation/uptime-tracker.ts +0 -155
- package/src/routing/default-router.ts +0 -75
- package/src/types/bittorrent-dht.d.ts +0 -19
- package/src/types/buyer.ts +0 -37
- package/src/types/capability.ts +0 -34
- package/src/types/connection.ts +0 -29
- package/src/types/http.ts +0 -20
- package/src/types/index.ts +0 -14
- package/src/types/metering.ts +0 -175
- package/src/types/nat-api.d.ts +0 -29
- package/src/types/peer-profile.ts +0 -25
- package/src/types/peer.ts +0 -62
- package/src/types/plugin-config.ts +0 -31
- package/src/types/protocol.ts +0 -162
- package/src/types/provider.ts +0 -40
- package/src/types/rating.ts +0 -23
- package/src/types/report.ts +0 -30
- package/src/types/seller.ts +0 -38
- package/src/types/staking.ts +0 -23
- package/src/utils/debug.ts +0 -30
- package/src/utils/hex.ts +0 -14
- package/tests/balance-manager.test.ts +0 -156
- package/tests/bootstrap.test.ts +0 -108
- package/tests/buyer-payment-manager.test.ts +0 -358
- package/tests/connection-auth.test.ts +0 -87
- package/tests/default-router.test.ts +0 -148
- package/tests/evm-keypair.test.ts +0 -173
- package/tests/identity.test.ts +0 -133
- package/tests/message-protocol.test.ts +0 -212
- package/tests/metadata-codec.test.ts +0 -165
- package/tests/metadata-validator.test.ts +0 -261
- package/tests/metering-storage.test.ts +0 -244
- package/tests/payment-codec.test.ts +0 -95
- package/tests/payment-mux.test.ts +0 -191
- package/tests/peer-selector.test.ts +0 -184
- package/tests/provider-detection.test.ts +0 -107
- package/tests/proxy-mux-security.test.ts +0 -38
- package/tests/receipt.test.ts +0 -215
- package/tests/reputation-integration.test.ts +0 -195
- package/tests/request-codec.test.ts +0 -144
- package/tests/token-counter.test.ts +0 -122
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -7
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import type { ProviderType, TokenCount } from '../types/metering.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Provider-specific bytes-to-token ratios.
|
|
5
|
-
*
|
|
6
|
-
* These are empirical averages. The actual ratio depends on the
|
|
7
|
-
* tokenizer and content, but for billing estimates they are
|
|
8
|
-
* sufficiently accurate (typically within 10-15%).
|
|
9
|
-
*
|
|
10
|
-
* Rationale:
|
|
11
|
-
* - English text averages ~4 bytes per token across most tokenizers
|
|
12
|
-
* - JSON overhead (keys, brackets, quotes) increases the ratio
|
|
13
|
-
* - Request bodies include system prompts, tool definitions, etc.
|
|
14
|
-
* - Response bodies include JSON wrapping around the actual content
|
|
15
|
-
*/
|
|
16
|
-
export const BYTES_PER_TOKEN: Record<string, number> = {
|
|
17
|
-
anthropic: 4.2, // Claude tokenizer, JSON Messages API
|
|
18
|
-
openai: 4.0, // tiktoken cl100k_base, JSON Chat API
|
|
19
|
-
google: 4.1, // Gemini tokenizer, JSON generateContent API
|
|
20
|
-
moonshot: 4.0, // Similar to OpenAI tokenizer
|
|
21
|
-
default: 4.0, // Fallback for unknown providers
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Minimum token estimate when Content-Length is missing or zero.
|
|
26
|
-
* LLM API requests always have some tokens (at least a system prompt).
|
|
27
|
-
*/
|
|
28
|
-
export const MIN_REQUEST_TOKENS = 100;
|
|
29
|
-
export const MIN_RESPONSE_TOKENS = 10;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Estimate token count from HTTP Content-Length header value.
|
|
33
|
-
*
|
|
34
|
-
* @param contentLength - Value of Content-Length header (bytes), or null if absent
|
|
35
|
-
* @param provider - Provider type for selecting bytes-per-token ratio
|
|
36
|
-
* @param direction - Whether this is a request (input) or response (output)
|
|
37
|
-
* @returns Estimated token count
|
|
38
|
-
*/
|
|
39
|
-
export function estimateTokensFromContentLength(
|
|
40
|
-
contentLength: number | null,
|
|
41
|
-
provider: ProviderType,
|
|
42
|
-
direction: 'request' | 'response'
|
|
43
|
-
): number {
|
|
44
|
-
if (contentLength === null || contentLength === 0) {
|
|
45
|
-
return direction === 'request' ? MIN_REQUEST_TOKENS : MIN_RESPONSE_TOKENS;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const ratio = BYTES_PER_TOKEN[provider] ?? BYTES_PER_TOKEN['default']!;
|
|
49
|
-
return Math.max(
|
|
50
|
-
Math.ceil(contentLength / ratio),
|
|
51
|
-
direction === 'request' ? MIN_REQUEST_TOKENS : MIN_RESPONSE_TOKENS
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Estimate token count from accumulated SSE stream chunk sizes.
|
|
57
|
-
* Used when Content-Length is not available (streaming responses).
|
|
58
|
-
*
|
|
59
|
-
* @param totalBytes - Total bytes received across all SSE chunks
|
|
60
|
-
* @param provider - Provider type
|
|
61
|
-
* @returns Estimated token count
|
|
62
|
-
*/
|
|
63
|
-
export function estimateTokensFromStreamBytes(
|
|
64
|
-
totalBytes: number,
|
|
65
|
-
provider: ProviderType
|
|
66
|
-
): number {
|
|
67
|
-
if (totalBytes === 0) return MIN_RESPONSE_TOKENS;
|
|
68
|
-
|
|
69
|
-
const ratio = BYTES_PER_TOKEN[provider] ?? BYTES_PER_TOKEN['default']!;
|
|
70
|
-
// SSE streams have additional overhead: "data: " prefix, newlines, event framing
|
|
71
|
-
// Approximately 15-20% of stream bytes are SSE overhead, not content
|
|
72
|
-
const contentBytes = totalBytes * 0.82;
|
|
73
|
-
return Math.max(Math.ceil(contentBytes / ratio), MIN_RESPONSE_TOKENS);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Build a complete TokenCount from request and response metadata.
|
|
78
|
-
*
|
|
79
|
-
* @param requestContentLength - Request Content-Length header value (bytes), or null
|
|
80
|
-
* @param responseContentLength - Response Content-Length header value (bytes), or null
|
|
81
|
-
* @param provider - Provider type
|
|
82
|
-
* @param isStreaming - Whether the response was an SSE stream
|
|
83
|
-
* @param streamTotalBytes - Total bytes from stream chunks (only if isStreaming)
|
|
84
|
-
*/
|
|
85
|
-
export function estimateTokens(
|
|
86
|
-
requestContentLength: number | null,
|
|
87
|
-
responseContentLength: number | null,
|
|
88
|
-
provider: ProviderType,
|
|
89
|
-
isStreaming: boolean,
|
|
90
|
-
streamTotalBytes?: number
|
|
91
|
-
): TokenCount {
|
|
92
|
-
const inputTokens = estimateTokensFromContentLength(
|
|
93
|
-
requestContentLength,
|
|
94
|
-
provider,
|
|
95
|
-
'request'
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
let outputTokens: number;
|
|
99
|
-
let method: TokenCount['method'];
|
|
100
|
-
let confidence: TokenCount['confidence'];
|
|
101
|
-
|
|
102
|
-
if (isStreaming && streamTotalBytes !== undefined) {
|
|
103
|
-
outputTokens = estimateTokensFromStreamBytes(streamTotalBytes, provider);
|
|
104
|
-
method = 'chunk-accumulation';
|
|
105
|
-
confidence = 'medium';
|
|
106
|
-
} else if (responseContentLength !== null && responseContentLength > 0) {
|
|
107
|
-
outputTokens = estimateTokensFromContentLength(
|
|
108
|
-
responseContentLength,
|
|
109
|
-
provider,
|
|
110
|
-
'response'
|
|
111
|
-
);
|
|
112
|
-
method = 'content-length';
|
|
113
|
-
confidence = 'high';
|
|
114
|
-
} else {
|
|
115
|
-
outputTokens = MIN_RESPONSE_TOKENS;
|
|
116
|
-
method = 'fallback';
|
|
117
|
-
confidence = 'low';
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
inputTokens,
|
|
122
|
-
outputTokens,
|
|
123
|
-
totalTokens: inputTokens + outputTokens,
|
|
124
|
-
method,
|
|
125
|
-
confidence,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import type { UsageAggregate, SessionMetrics, ProviderType } from '../types/metering.js';
|
|
2
|
-
|
|
3
|
-
export type AggregationGranularity = 'daily' | 'weekly' | 'monthly';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Time period boundaries for aggregation.
|
|
7
|
-
*/
|
|
8
|
-
export interface TimePeriod {
|
|
9
|
-
start: number; // ms since epoch
|
|
10
|
-
end: number; // ms since epoch
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Aggregates session metrics into time-period summaries.
|
|
15
|
-
*/
|
|
16
|
-
export class UsageAggregator {
|
|
17
|
-
/**
|
|
18
|
-
* Aggregate a list of session metrics into time-period buckets.
|
|
19
|
-
*
|
|
20
|
-
* @param sessions - Session metrics to aggregate
|
|
21
|
-
* @param granularity - Time period granularity
|
|
22
|
-
* @param topPeerCount - Number of top peers to include (default: 5)
|
|
23
|
-
* @returns Array of UsageAggregate, one per period that has data
|
|
24
|
-
*/
|
|
25
|
-
aggregate(
|
|
26
|
-
sessions: SessionMetrics[],
|
|
27
|
-
granularity: AggregationGranularity,
|
|
28
|
-
topPeerCount: number = 5
|
|
29
|
-
): UsageAggregate[] {
|
|
30
|
-
if (sessions.length === 0) return [];
|
|
31
|
-
|
|
32
|
-
// Group sessions by period
|
|
33
|
-
const buckets = new Map<string, SessionMetrics[]>();
|
|
34
|
-
|
|
35
|
-
for (const session of sessions) {
|
|
36
|
-
const period = this.getPeriod(session.startedAt, granularity);
|
|
37
|
-
const key = `${period.start}`;
|
|
38
|
-
const bucket = buckets.get(key);
|
|
39
|
-
if (bucket) {
|
|
40
|
-
bucket.push(session);
|
|
41
|
-
} else {
|
|
42
|
-
buckets.set(key, [session]);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Build aggregates
|
|
47
|
-
const aggregates: UsageAggregate[] = [];
|
|
48
|
-
|
|
49
|
-
for (const [_key, bucketSessions] of buckets) {
|
|
50
|
-
const period = this.getPeriod(bucketSessions[0]!.startedAt, granularity);
|
|
51
|
-
aggregates.push(this.buildAggregate(bucketSessions, period, granularity, topPeerCount));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Sort by period start
|
|
55
|
-
aggregates.sort((a, b) => a.periodStart - b.periodStart);
|
|
56
|
-
|
|
57
|
-
return aggregates;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Get the time period that a timestamp falls into.
|
|
62
|
-
*/
|
|
63
|
-
getPeriod(timestamp: number, granularity: AggregationGranularity): TimePeriod {
|
|
64
|
-
const date = new Date(timestamp);
|
|
65
|
-
|
|
66
|
-
switch (granularity) {
|
|
67
|
-
case 'daily': {
|
|
68
|
-
const start = new Date(Date.UTC(
|
|
69
|
-
date.getUTCFullYear(),
|
|
70
|
-
date.getUTCMonth(),
|
|
71
|
-
date.getUTCDate()
|
|
72
|
-
));
|
|
73
|
-
const end = new Date(Date.UTC(
|
|
74
|
-
date.getUTCFullYear(),
|
|
75
|
-
date.getUTCMonth(),
|
|
76
|
-
date.getUTCDate() + 1
|
|
77
|
-
));
|
|
78
|
-
return { start: start.getTime(), end: end.getTime() };
|
|
79
|
-
}
|
|
80
|
-
case 'weekly': {
|
|
81
|
-
// Monday 00:00 UTC to next Monday 00:00 UTC
|
|
82
|
-
const dayOfWeek = date.getUTCDay();
|
|
83
|
-
// getUTCDay() returns 0 for Sunday, 1 for Monday, etc.
|
|
84
|
-
// We want Monday as the start, so offset: Monday=0, Tue=1, ..., Sun=6
|
|
85
|
-
const daysFromMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
86
|
-
const monday = new Date(Date.UTC(
|
|
87
|
-
date.getUTCFullYear(),
|
|
88
|
-
date.getUTCMonth(),
|
|
89
|
-
date.getUTCDate() - daysFromMonday
|
|
90
|
-
));
|
|
91
|
-
const nextMonday = new Date(monday.getTime() + 7 * 24 * 60 * 60 * 1000);
|
|
92
|
-
return { start: monday.getTime(), end: nextMonday.getTime() };
|
|
93
|
-
}
|
|
94
|
-
case 'monthly': {
|
|
95
|
-
const start = new Date(Date.UTC(
|
|
96
|
-
date.getUTCFullYear(),
|
|
97
|
-
date.getUTCMonth(),
|
|
98
|
-
1
|
|
99
|
-
));
|
|
100
|
-
const end = new Date(Date.UTC(
|
|
101
|
-
date.getUTCFullYear(),
|
|
102
|
-
date.getUTCMonth() + 1,
|
|
103
|
-
1
|
|
104
|
-
));
|
|
105
|
-
return { start: start.getTime(), end: end.getTime() };
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get aggregates for a specific date range.
|
|
112
|
-
*
|
|
113
|
-
* @param sessions - All available session metrics
|
|
114
|
-
* @param startDate - Start of range (ms since epoch)
|
|
115
|
-
* @param endDate - End of range (ms since epoch)
|
|
116
|
-
* @param granularity - Bucket granularity
|
|
117
|
-
* @param topPeerCount - Number of top peers to include (default: 5)
|
|
118
|
-
*/
|
|
119
|
-
aggregateRange(
|
|
120
|
-
sessions: SessionMetrics[],
|
|
121
|
-
startDate: number,
|
|
122
|
-
endDate: number,
|
|
123
|
-
granularity: AggregationGranularity,
|
|
124
|
-
topPeerCount: number = 5
|
|
125
|
-
): UsageAggregate[] {
|
|
126
|
-
const filtered = sessions.filter(
|
|
127
|
-
(s) => s.startedAt >= startDate && s.startedAt < endDate
|
|
128
|
-
);
|
|
129
|
-
return this.aggregate(filtered, granularity, topPeerCount);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Get a single aggregate for all-time usage.
|
|
134
|
-
*/
|
|
135
|
-
aggregateAll(sessions: SessionMetrics[]): UsageAggregate {
|
|
136
|
-
if (sessions.length === 0) {
|
|
137
|
-
return {
|
|
138
|
-
periodStart: 0,
|
|
139
|
-
periodEnd: 0,
|
|
140
|
-
granularity: 'monthly',
|
|
141
|
-
totalSessions: 0,
|
|
142
|
-
totalRequests: 0,
|
|
143
|
-
totalTokens: 0,
|
|
144
|
-
totalCostCents: 0,
|
|
145
|
-
byProvider: {},
|
|
146
|
-
topPeers: [],
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
let minStart = Infinity;
|
|
151
|
-
let maxEnd = -Infinity;
|
|
152
|
-
|
|
153
|
-
for (const s of sessions) {
|
|
154
|
-
if (s.startedAt < minStart) minStart = s.startedAt;
|
|
155
|
-
const end = s.endedAt ?? s.startedAt;
|
|
156
|
-
if (end > maxEnd) maxEnd = end;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return this.buildAggregate(
|
|
160
|
-
sessions,
|
|
161
|
-
{ start: minStart, end: maxEnd },
|
|
162
|
-
'monthly',
|
|
163
|
-
sessions.length // Include all peers in all-time view
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Build a single UsageAggregate from a list of sessions.
|
|
169
|
-
*/
|
|
170
|
-
private buildAggregate(
|
|
171
|
-
sessions: SessionMetrics[],
|
|
172
|
-
period: TimePeriod,
|
|
173
|
-
granularity: AggregationGranularity,
|
|
174
|
-
topPeerCount: number
|
|
175
|
-
): UsageAggregate {
|
|
176
|
-
const byProvider: Record<ProviderType, { requests: number; tokens: number; costCents: number }> = {};
|
|
177
|
-
const peerMap = new Map<string, { requests: number; tokens: number; costCents: number }>();
|
|
178
|
-
|
|
179
|
-
let totalRequests = 0;
|
|
180
|
-
let totalTokens = 0;
|
|
181
|
-
let totalCostCents = 0;
|
|
182
|
-
|
|
183
|
-
for (const session of sessions) {
|
|
184
|
-
totalRequests += session.totalRequests;
|
|
185
|
-
totalTokens += session.totalTokens;
|
|
186
|
-
totalCostCents += session.totalCostCents;
|
|
187
|
-
|
|
188
|
-
// Provider breakdown
|
|
189
|
-
const providerEntry = byProvider[session.provider];
|
|
190
|
-
if (providerEntry) {
|
|
191
|
-
providerEntry.requests += session.totalRequests;
|
|
192
|
-
providerEntry.tokens += session.totalTokens;
|
|
193
|
-
providerEntry.costCents += session.totalCostCents;
|
|
194
|
-
} else {
|
|
195
|
-
byProvider[session.provider] = {
|
|
196
|
-
requests: session.totalRequests,
|
|
197
|
-
tokens: session.totalTokens,
|
|
198
|
-
costCents: session.totalCostCents,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Peer breakdown (use sellerPeerId as the peer identifier)
|
|
203
|
-
const peerId = session.sellerPeerId;
|
|
204
|
-
const peerEntry = peerMap.get(peerId);
|
|
205
|
-
if (peerEntry) {
|
|
206
|
-
peerEntry.requests += session.totalRequests;
|
|
207
|
-
peerEntry.tokens += session.totalTokens;
|
|
208
|
-
peerEntry.costCents += session.totalCostCents;
|
|
209
|
-
} else {
|
|
210
|
-
peerMap.set(peerId, {
|
|
211
|
-
requests: session.totalRequests,
|
|
212
|
-
tokens: session.totalTokens,
|
|
213
|
-
costCents: session.totalCostCents,
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Sort peers by costCents descending, take top N
|
|
219
|
-
const topPeers = Array.from(peerMap.entries())
|
|
220
|
-
.map(([peerId, data]) => ({ peerId, ...data }))
|
|
221
|
-
.sort((a, b) => b.costCents - a.costCents)
|
|
222
|
-
.slice(0, topPeerCount);
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
periodStart: period.start,
|
|
226
|
-
periodEnd: period.end,
|
|
227
|
-
granularity,
|
|
228
|
-
totalSessions: sessions.length,
|
|
229
|
-
totalRequests,
|
|
230
|
-
totalTokens,
|
|
231
|
-
totalCostCents,
|
|
232
|
-
byProvider,
|
|
233
|
-
topPeers,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
}
|