@louloulinx/metagpt 0.1.3
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/.eslintrc.json +23 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README-CN.md +754 -0
- package/README.md +238 -0
- package/bun.lock +1023 -0
- package/doc/TutorialAssistant.md +114 -0
- package/doc/VercelLLMProvider.md +164 -0
- package/eslint.config.js +55 -0
- package/examples/data-interpreter-example.ts +173 -0
- package/examples/qwen-direct-example.ts +60 -0
- package/examples/qwen-example.ts +62 -0
- package/examples/tutorial-assistant-example.ts +97 -0
- package/jest.config.ts +22 -0
- package/output/tutorials/Go/350/257/255/350/250/200/347/274/226/347/250/213/346/225/231/347/250/213_2025-02-25T09-35-15-436Z.md +2208 -0
- package/output/tutorials/Rust/346/225/231/347/250/213_2025-02-25T08-27-27-632Z.md +1967 -0
- package/output/tutorials//345/246/202/344/275/225/344/275/277/347/224/250TypeScript/345/274/200/345/217/221Node.js/345/272/224/347/224/250_2025-02-25T08-14-39-605Z.md +1721 -0
- package/output/tutorials//346/225/260/345/255/227/347/273/217/346/265/216/345/255/246/346/225/231/347/250/213_2025-02-25T10-45-03-605Z.md +902 -0
- package/output/tutorials//346/232/250/345/215/227/345/244/247/345/255/246/346/225/260/345/255/227/347/273/217/346/265/216/345/255/246/345/244/215/350/257/225/350/265/204/346/226/231_2025-02-25T11-16-59-133Z.md +719 -0
- package/package.json +58 -0
- package/plan-cn.md +321 -0
- package/plan.md +154 -0
- package/src/actions/analyze-task.ts +65 -0
- package/src/actions/base-action.ts +103 -0
- package/src/actions/di/execute-nb-code.ts +247 -0
- package/src/actions/di/write-analysis-code.ts +234 -0
- package/src/actions/write-tutorial.ts +232 -0
- package/src/config/browser.ts +33 -0
- package/src/config/config.ts +345 -0
- package/src/config/embedding.ts +26 -0
- package/src/config/llm.ts +36 -0
- package/src/config/mermaid.ts +37 -0
- package/src/config/omniparse.ts +25 -0
- package/src/config/redis.ts +34 -0
- package/src/config/s3.ts +33 -0
- package/src/config/search.ts +30 -0
- package/src/config/workspace.ts +20 -0
- package/src/index.ts +40 -0
- package/src/management/team.ts +168 -0
- package/src/memory/longterm.ts +218 -0
- package/src/memory/manager.ts +160 -0
- package/src/memory/types.ts +100 -0
- package/src/memory/working.ts +154 -0
- package/src/monitoring/system.ts +413 -0
- package/src/monitoring/types.ts +230 -0
- package/src/plugin/manager.ts +79 -0
- package/src/plugin/types.ts +114 -0
- package/src/provider/vercel-llm.ts +314 -0
- package/src/rag/base-rag.ts +194 -0
- package/src/rag/document-qa.ts +102 -0
- package/src/roles/base-role.ts +155 -0
- package/src/roles/data-interpreter.ts +360 -0
- package/src/roles/engineer.ts +1 -0
- package/src/roles/tutorial-assistant.ts +217 -0
- package/src/skills/base-skill.ts +144 -0
- package/src/skills/code-review.ts +120 -0
- package/src/tools/base-tool.ts +155 -0
- package/src/tools/file-system.ts +204 -0
- package/src/tools/tool-recommend.d.ts +14 -0
- package/src/tools/tool-recommend.ts +31 -0
- package/src/types/action.ts +38 -0
- package/src/types/config.ts +129 -0
- package/src/types/document.ts +354 -0
- package/src/types/llm.ts +64 -0
- package/src/types/memory.ts +36 -0
- package/src/types/message.ts +193 -0
- package/src/types/rag.ts +86 -0
- package/src/types/role.ts +67 -0
- package/src/types/skill.ts +71 -0
- package/src/types/task.ts +32 -0
- package/src/types/team.ts +55 -0
- package/src/types/tool.ts +77 -0
- package/src/types/workflow.ts +133 -0
- package/src/utils/common.ts +73 -0
- package/src/utils/yaml.ts +67 -0
- package/src/websocket/browser-client.ts +187 -0
- package/src/websocket/client.ts +186 -0
- package/src/websocket/server.ts +169 -0
- package/src/websocket/types.ts +125 -0
- package/src/workflow/executor.ts +193 -0
- package/src/workflow/executors/action-executor.ts +72 -0
- package/src/workflow/executors/condition-executor.ts +118 -0
- package/src/workflow/executors/parallel-executor.ts +201 -0
- package/src/workflow/executors/role-executor.ts +76 -0
- package/src/workflow/executors/sequence-executor.ts +196 -0
- package/tests/actions.test.ts +105 -0
- package/tests/benchmark/performance.test.ts +147 -0
- package/tests/config/config.test.ts +115 -0
- package/tests/config.test.ts +106 -0
- package/tests/e2e/setup.ts +74 -0
- package/tests/e2e/workflow.test.ts +88 -0
- package/tests/llm.test.ts +84 -0
- package/tests/memory/memory.test.ts +164 -0
- package/tests/memory.test.ts +63 -0
- package/tests/monitoring/monitoring.test.ts +225 -0
- package/tests/plugin/plugin.test.ts +183 -0
- package/tests/provider/bailian-llm.test.ts +98 -0
- package/tests/rag.test.ts +162 -0
- package/tests/roles.test.ts +88 -0
- package/tests/skills.test.ts +166 -0
- package/tests/team.test.ts +143 -0
- package/tests/tools.test.ts +170 -0
- package/tests/types/document.test.ts +181 -0
- package/tests/types/message.test.ts +122 -0
- package/tests/utils/yaml.test.ts +110 -0
- package/tests/utils.test.ts +74 -0
- package/tests/websocket/browser-client.test.ts +1 -0
- package/tests/websocket/websocket.test.ts +42 -0
- package/tests/workflow/parallel-executor.test.ts +224 -0
- package/tests/workflow/sequence-executor.test.ts +207 -0
- package/tests/workflow.test.ts +290 -0
- package/tsconfig.json +27 -0
- package/typedoc.json +25 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
import type { WorkingMemory } from './types';
|
2
|
+
import type { MemoryEntrySchema, MemoryQueryOptions } from './types';
|
3
|
+
import type { z } from 'zod';
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* In-memory implementation of working memory
|
8
|
+
*/
|
9
|
+
export class WorkingMemoryImpl implements WorkingMemory {
|
10
|
+
private memories: Map<string, z.infer<typeof MemoryEntrySchema>> = new Map();
|
11
|
+
private focusId: string | null = null;
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Add a new memory
|
15
|
+
*/
|
16
|
+
public async add(
|
17
|
+
content: string,
|
18
|
+
type: string,
|
19
|
+
metadata: Record<string, any> = {}
|
20
|
+
): Promise<z.infer<typeof MemoryEntrySchema>> {
|
21
|
+
const memory = {
|
22
|
+
id: uuidv4(),
|
23
|
+
content,
|
24
|
+
type,
|
25
|
+
timestamp: Date.now(),
|
26
|
+
metadata,
|
27
|
+
importance: metadata.importance ?? 0.5,
|
28
|
+
embedding: metadata.embedding ?? [],
|
29
|
+
};
|
30
|
+
|
31
|
+
this.memories.set(memory.id, memory);
|
32
|
+
return memory;
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Get a memory by ID
|
37
|
+
*/
|
38
|
+
public async get(id: string): Promise<z.infer<typeof MemoryEntrySchema> | null> {
|
39
|
+
return this.memories.get(id) || null;
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Search memories by query options
|
44
|
+
*/
|
45
|
+
public async search(options: MemoryQueryOptions): Promise<z.infer<typeof MemoryEntrySchema>[]> {
|
46
|
+
let results = Array.from(this.memories.values());
|
47
|
+
|
48
|
+
// Apply filters
|
49
|
+
if (options.type) {
|
50
|
+
results = results.filter(m => m.type === options.type);
|
51
|
+
}
|
52
|
+
|
53
|
+
if (options.startTime) {
|
54
|
+
results = results.filter(m => m.timestamp >= options.startTime!);
|
55
|
+
}
|
56
|
+
|
57
|
+
if (options.endTime) {
|
58
|
+
results = results.filter(m => m.timestamp <= options.endTime!);
|
59
|
+
}
|
60
|
+
|
61
|
+
if (typeof options.minImportance === 'number') {
|
62
|
+
results = results.filter(m => m.importance >= options.minImportance!);
|
63
|
+
}
|
64
|
+
|
65
|
+
if (options.metadata) {
|
66
|
+
results = results.filter(m => {
|
67
|
+
return Object.entries(options.metadata!).every(([key, value]) => {
|
68
|
+
if (typeof value === 'function') {
|
69
|
+
return value(m.metadata[key]);
|
70
|
+
}
|
71
|
+
return m.metadata[key] === value;
|
72
|
+
});
|
73
|
+
});
|
74
|
+
}
|
75
|
+
|
76
|
+
if (options.content) {
|
77
|
+
const searchContent = options.content.toLowerCase();
|
78
|
+
results = results.filter(m =>
|
79
|
+
m.content.toLowerCase().includes(searchContent)
|
80
|
+
);
|
81
|
+
}
|
82
|
+
|
83
|
+
// Sort by timestamp and importance
|
84
|
+
results.sort((a, b) => {
|
85
|
+
const importanceDiff = b.importance - a.importance;
|
86
|
+
return importanceDiff !== 0 ? importanceDiff : b.timestamp - a.timestamp;
|
87
|
+
});
|
88
|
+
|
89
|
+
// Apply limit
|
90
|
+
if (options.limit) {
|
91
|
+
results = results.slice(0, options.limit);
|
92
|
+
}
|
93
|
+
|
94
|
+
return results;
|
95
|
+
}
|
96
|
+
|
97
|
+
/**
|
98
|
+
* Update a memory
|
99
|
+
*/
|
100
|
+
public async update(
|
101
|
+
id: string,
|
102
|
+
updates: Partial<z.infer<typeof MemoryEntrySchema>>
|
103
|
+
): Promise<void> {
|
104
|
+
const memory = this.memories.get(id);
|
105
|
+
if (!memory) {
|
106
|
+
throw new Error(`Memory ${id} not found`);
|
107
|
+
}
|
108
|
+
|
109
|
+
Object.assign(memory, updates);
|
110
|
+
this.memories.set(id, memory);
|
111
|
+
}
|
112
|
+
|
113
|
+
/**
|
114
|
+
* Delete a memory
|
115
|
+
*/
|
116
|
+
public async delete(id: string): Promise<void> {
|
117
|
+
this.memories.delete(id);
|
118
|
+
if (this.focusId === id) {
|
119
|
+
this.focusId = null;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Clear all memories
|
125
|
+
*/
|
126
|
+
public async clear(): Promise<void> {
|
127
|
+
this.memories.clear();
|
128
|
+
this.focusId = null;
|
129
|
+
}
|
130
|
+
|
131
|
+
/**
|
132
|
+
* Get current focus of attention
|
133
|
+
*/
|
134
|
+
public async getFocus(): Promise<z.infer<typeof MemoryEntrySchema> | null> {
|
135
|
+
return this.focusId ? this.memories.get(this.focusId) || null : null;
|
136
|
+
}
|
137
|
+
|
138
|
+
/**
|
139
|
+
* Set focus of attention
|
140
|
+
*/
|
141
|
+
public async setFocus(id: string): Promise<void> {
|
142
|
+
if (!this.memories.has(id)) {
|
143
|
+
throw new Error(`Memory ${id} not found`);
|
144
|
+
}
|
145
|
+
this.focusId = id;
|
146
|
+
}
|
147
|
+
|
148
|
+
/**
|
149
|
+
* Clear focus of attention
|
150
|
+
*/
|
151
|
+
public async clearFocus(): Promise<void> {
|
152
|
+
this.focusId = null;
|
153
|
+
}
|
154
|
+
}
|
@@ -0,0 +1,413 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
import type {
|
3
|
+
Counter,
|
4
|
+
Gauge,
|
5
|
+
Histogram,
|
6
|
+
Summary,
|
7
|
+
MetricCollector,
|
8
|
+
Tracer,
|
9
|
+
Logger,
|
10
|
+
MonitoringSystem,
|
11
|
+
} from './types';
|
12
|
+
import {
|
13
|
+
MetricType,
|
14
|
+
MetricSchema,
|
15
|
+
TraceSpanSchema,
|
16
|
+
LogLevel,
|
17
|
+
} from './types';
|
18
|
+
|
19
|
+
/**
|
20
|
+
* In-memory counter metric implementation
|
21
|
+
*/
|
22
|
+
class CounterImpl implements Counter {
|
23
|
+
private values: Map<string, number> = new Map();
|
24
|
+
|
25
|
+
constructor(private options: z.infer<typeof MetricSchema>) {}
|
26
|
+
|
27
|
+
public inc(value = 1, labels: Record<string, string> = {}): void {
|
28
|
+
const key = this.getKey(labels);
|
29
|
+
const current = this.values.get(key) || 0;
|
30
|
+
this.values.set(key, current + value);
|
31
|
+
}
|
32
|
+
|
33
|
+
public getValue(labels: Record<string, string> = {}): number {
|
34
|
+
return this.values.get(this.getKey(labels)) || 0;
|
35
|
+
}
|
36
|
+
|
37
|
+
private getKey(labels: Record<string, string>): string {
|
38
|
+
return JSON.stringify(labels);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* In-memory gauge metric implementation
|
44
|
+
*/
|
45
|
+
class GaugeImpl implements Gauge {
|
46
|
+
private values: Map<string, number> = new Map();
|
47
|
+
|
48
|
+
constructor(private options: z.infer<typeof MetricSchema>) {}
|
49
|
+
|
50
|
+
public set(value: number, labels: Record<string, string> = {}): void {
|
51
|
+
this.values.set(this.getKey(labels), value);
|
52
|
+
}
|
53
|
+
|
54
|
+
public inc(value = 1, labels: Record<string, string> = {}): void {
|
55
|
+
const key = this.getKey(labels);
|
56
|
+
const current = this.values.get(key) || 0;
|
57
|
+
this.values.set(key, current + value);
|
58
|
+
}
|
59
|
+
|
60
|
+
public dec(value = 1, labels: Record<string, string> = {}): void {
|
61
|
+
this.inc(-value, labels);
|
62
|
+
}
|
63
|
+
|
64
|
+
public getValue(labels: Record<string, string> = {}): number {
|
65
|
+
return this.values.get(this.getKey(labels)) || 0;
|
66
|
+
}
|
67
|
+
|
68
|
+
private getKey(labels: Record<string, string>): string {
|
69
|
+
return JSON.stringify(labels);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* In-memory histogram metric implementation
|
75
|
+
*/
|
76
|
+
class HistogramImpl implements Histogram {
|
77
|
+
private buckets: Map<string, Record<number, number>> = new Map();
|
78
|
+
private defaultBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];
|
79
|
+
|
80
|
+
constructor(private options: z.infer<typeof MetricSchema>) {}
|
81
|
+
|
82
|
+
public observe(value: number, labels: Record<string, string> = {}): void {
|
83
|
+
const key = this.getKey(labels);
|
84
|
+
let bucketValues = this.buckets.get(key);
|
85
|
+
if (!bucketValues) {
|
86
|
+
bucketValues = this.defaultBuckets.reduce((acc, bound) => {
|
87
|
+
acc[bound] = 0;
|
88
|
+
return acc;
|
89
|
+
}, {} as Record<number, number>);
|
90
|
+
this.buckets.set(key, bucketValues);
|
91
|
+
}
|
92
|
+
|
93
|
+
for (const bound of this.defaultBuckets) {
|
94
|
+
if (value <= bound) {
|
95
|
+
bucketValues[bound]++;
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
public getBuckets(labels: Record<string, string> = {}): Record<number, number> {
|
101
|
+
return this.buckets.get(this.getKey(labels)) || {};
|
102
|
+
}
|
103
|
+
|
104
|
+
private getKey(labels: Record<string, string>): string {
|
105
|
+
return JSON.stringify(labels);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* In-memory summary metric implementation
|
111
|
+
*/
|
112
|
+
class SummaryImpl implements Summary {
|
113
|
+
private values: Map<string, number[]> = new Map();
|
114
|
+
private defaultQuantiles = [0.5, 0.9, 0.95, 0.99];
|
115
|
+
|
116
|
+
constructor(private options: z.infer<typeof MetricSchema>) {}
|
117
|
+
|
118
|
+
public observe(value: number, labels: Record<string, string> = {}): void {
|
119
|
+
const key = this.getKey(labels);
|
120
|
+
let values = this.values.get(key);
|
121
|
+
if (!values) {
|
122
|
+
values = [];
|
123
|
+
this.values.set(key, values);
|
124
|
+
}
|
125
|
+
values.push(value);
|
126
|
+
}
|
127
|
+
|
128
|
+
public getQuantiles(labels: Record<string, string> = {}): Record<number, number> {
|
129
|
+
const values = this.values.get(this.getKey(labels)) || [];
|
130
|
+
if (values.length === 0) return {};
|
131
|
+
|
132
|
+
values.sort((a, b) => a - b);
|
133
|
+
return this.defaultQuantiles.reduce((acc, q) => {
|
134
|
+
const index = Math.ceil(q * values.length) - 1;
|
135
|
+
acc[q] = values[index];
|
136
|
+
return acc;
|
137
|
+
}, {} as Record<number, number>);
|
138
|
+
}
|
139
|
+
|
140
|
+
private getKey(labels: Record<string, string>): string {
|
141
|
+
return JSON.stringify(labels);
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
/**
|
146
|
+
* In-memory metric collector implementation with Prometheus export support
|
147
|
+
*/
|
148
|
+
class MetricCollectorImpl implements MetricCollector {
|
149
|
+
private metrics: Map<string, Counter | Gauge | Histogram | Summary> = new Map();
|
150
|
+
|
151
|
+
public counter(options: z.infer<typeof MetricSchema>): Counter {
|
152
|
+
const key = this.getKey(options.name, MetricType.COUNTER);
|
153
|
+
let metric = this.metrics.get(key) as Counter;
|
154
|
+
if (!metric) {
|
155
|
+
metric = new CounterImpl(options);
|
156
|
+
this.metrics.set(key, metric);
|
157
|
+
}
|
158
|
+
return metric;
|
159
|
+
}
|
160
|
+
|
161
|
+
public gauge(options: z.infer<typeof MetricSchema>): Gauge {
|
162
|
+
const key = this.getKey(options.name, MetricType.GAUGE);
|
163
|
+
let metric = this.metrics.get(key) as Gauge;
|
164
|
+
if (!metric) {
|
165
|
+
metric = new GaugeImpl(options);
|
166
|
+
this.metrics.set(key, metric);
|
167
|
+
}
|
168
|
+
return metric;
|
169
|
+
}
|
170
|
+
|
171
|
+
public histogram(options: z.infer<typeof MetricSchema>): Histogram {
|
172
|
+
const key = this.getKey(options.name, MetricType.HISTOGRAM);
|
173
|
+
let metric = this.metrics.get(key) as Histogram;
|
174
|
+
if (!metric) {
|
175
|
+
metric = new HistogramImpl(options);
|
176
|
+
this.metrics.set(key, metric);
|
177
|
+
}
|
178
|
+
return metric;
|
179
|
+
}
|
180
|
+
|
181
|
+
public summary(options: z.infer<typeof MetricSchema>): Summary {
|
182
|
+
const key = this.getKey(options.name, MetricType.SUMMARY);
|
183
|
+
let metric = this.metrics.get(key) as Summary;
|
184
|
+
if (!metric) {
|
185
|
+
metric = new SummaryImpl(options);
|
186
|
+
this.metrics.set(key, metric);
|
187
|
+
}
|
188
|
+
return metric;
|
189
|
+
}
|
190
|
+
|
191
|
+
private getKey(name: string, type: MetricType): string {
|
192
|
+
return `${type}:${name}`;
|
193
|
+
}
|
194
|
+
|
195
|
+
/**
|
196
|
+
* Export metrics in Prometheus format
|
197
|
+
* @returns Prometheus formatted metrics string
|
198
|
+
*/
|
199
|
+
public exportPrometheus(): string {
|
200
|
+
const lines: string[] = [];
|
201
|
+
|
202
|
+
for (const [key, metric] of this.metrics.entries()) {
|
203
|
+
const [type, name] = key.split(':');
|
204
|
+
const help = `# HELP ${name} ${(metric as any).options.description}`;
|
205
|
+
const typeHelp = `# TYPE ${name} ${type.toLowerCase()}`;
|
206
|
+
lines.push(help, typeHelp);
|
207
|
+
|
208
|
+
if (metric instanceof CounterImpl || metric instanceof GaugeImpl) {
|
209
|
+
lines.push(`${name} ${metric.getValue()}`);
|
210
|
+
} else if (metric instanceof HistogramImpl) {
|
211
|
+
const buckets = metric.getBuckets();
|
212
|
+
Object.entries(buckets).forEach(([bound, count]) => {
|
213
|
+
lines.push(`${name}_bucket{le="${bound}"} ${count}`);
|
214
|
+
});
|
215
|
+
} else if (metric instanceof SummaryImpl) {
|
216
|
+
const quantiles = metric.getQuantiles();
|
217
|
+
Object.entries(quantiles).forEach(([q, value]) => {
|
218
|
+
lines.push(`${name}{quantile="${q}"} ${value}`);
|
219
|
+
});
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
return lines.join('\n');
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
/**
|
228
|
+
* In-memory tracer implementation with context propagation
|
229
|
+
*/
|
230
|
+
class TracerImpl implements Tracer {
|
231
|
+
private spans: Map<string, z.infer<typeof TraceSpanSchema>> = new Map();
|
232
|
+
private activeSpans: Map<string, z.infer<typeof TraceSpanSchema>> = new Map();
|
233
|
+
|
234
|
+
public startSpan(name: string, options: {
|
235
|
+
parent?: z.infer<typeof TraceSpanSchema>;
|
236
|
+
kind?: z.infer<typeof TraceSpanSchema>['kind'];
|
237
|
+
attributes?: Record<string, any>;
|
238
|
+
} = {}): z.infer<typeof TraceSpanSchema> {
|
239
|
+
// Use active span as parent if not specified
|
240
|
+
const parent = options.parent || this.getActiveSpan();
|
241
|
+
const span = TraceSpanSchema.parse({
|
242
|
+
id: crypto.randomUUID(),
|
243
|
+
traceId: parent?.traceId || crypto.randomUUID(),
|
244
|
+
parentId: parent?.id,
|
245
|
+
name,
|
246
|
+
startTime: Date.now(),
|
247
|
+
kind: options.kind,
|
248
|
+
attributes: options.attributes,
|
249
|
+
});
|
250
|
+
this.spans.set(span.id, span);
|
251
|
+
this.setActiveSpan(span);
|
252
|
+
return span;
|
253
|
+
}
|
254
|
+
|
255
|
+
public endSpan(span: z.infer<typeof TraceSpanSchema>): void {
|
256
|
+
const storedSpan = this.spans.get(span.id);
|
257
|
+
if (storedSpan) {
|
258
|
+
storedSpan.endTime = Date.now();
|
259
|
+
this.clearActiveSpan(span);
|
260
|
+
}
|
261
|
+
}
|
262
|
+
|
263
|
+
public addEvent(span: z.infer<typeof TraceSpanSchema>, name: string, attributes: Record<string, any> = {}): void {
|
264
|
+
const storedSpan = this.spans.get(span.id);
|
265
|
+
if (storedSpan) {
|
266
|
+
storedSpan.events.push({
|
267
|
+
name,
|
268
|
+
timestamp: Date.now(),
|
269
|
+
attributes,
|
270
|
+
});
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
public setStatus(span: z.infer<typeof TraceSpanSchema>, status: z.infer<typeof TraceSpanSchema>['status'], error?: any): void {
|
275
|
+
const storedSpan = this.spans.get(span.id);
|
276
|
+
if (storedSpan) {
|
277
|
+
storedSpan.status = status;
|
278
|
+
if (error) {
|
279
|
+
storedSpan.error = error;
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
/**
|
285
|
+
* Get active span for the current context
|
286
|
+
* @returns Currently active span or undefined
|
287
|
+
*/
|
288
|
+
public getActiveSpan(): z.infer<typeof TraceSpanSchema> | undefined {
|
289
|
+
return Array.from(this.activeSpans.values())[0];
|
290
|
+
}
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Set active span for the current context
|
294
|
+
* @param span - Span to set as active
|
295
|
+
*/
|
296
|
+
public setActiveSpan(span: z.infer<typeof TraceSpanSchema>): void {
|
297
|
+
this.activeSpans.set(span.id, span);
|
298
|
+
}
|
299
|
+
|
300
|
+
/**
|
301
|
+
* Clear active span for the current context
|
302
|
+
* @param span - Span to clear
|
303
|
+
*/
|
304
|
+
public clearActiveSpan(span: z.infer<typeof TraceSpanSchema>): void {
|
305
|
+
this.activeSpans.delete(span.id);
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
/**
|
310
|
+
* In-memory logger implementation with trace context correlation
|
311
|
+
*/
|
312
|
+
class LoggerImpl implements Logger {
|
313
|
+
private logs: any[] = [];
|
314
|
+
private tracer?: TracerImpl;
|
315
|
+
|
316
|
+
constructor(tracer?: TracerImpl) {
|
317
|
+
this.tracer = tracer;
|
318
|
+
}
|
319
|
+
|
320
|
+
public debug(message: string, context: Record<string, any> = {}): void {
|
321
|
+
this.log(LogLevel.DEBUG, message, undefined, context);
|
322
|
+
}
|
323
|
+
|
324
|
+
public info(message: string, context: Record<string, any> = {}): void {
|
325
|
+
this.log(LogLevel.INFO, message, undefined, context);
|
326
|
+
}
|
327
|
+
|
328
|
+
public warn(message: string, context: Record<string, any> = {}): void {
|
329
|
+
this.log(LogLevel.WARN, message, undefined, context);
|
330
|
+
}
|
331
|
+
|
332
|
+
public error(message: string, error?: Error, context: Record<string, any> = {}): void {
|
333
|
+
this.log(LogLevel.ERROR, message, error, context);
|
334
|
+
}
|
335
|
+
|
336
|
+
private log(level: LogLevel, message: string, error?: Error, context: Record<string, any> = {}): void {
|
337
|
+
// Get current trace context
|
338
|
+
const activeSpan = this.tracer?.getActiveSpan();
|
339
|
+
const entry = {
|
340
|
+
timestamp: Date.now(),
|
341
|
+
level,
|
342
|
+
message,
|
343
|
+
context: {
|
344
|
+
...context,
|
345
|
+
error: error?.message,
|
346
|
+
stack: error?.stack,
|
347
|
+
traceId: activeSpan?.traceId,
|
348
|
+
spanId: activeSpan?.id,
|
349
|
+
},
|
350
|
+
};
|
351
|
+
this.logs.push(entry);
|
352
|
+
console[level](message, entry.context);
|
353
|
+
}
|
354
|
+
|
355
|
+
/**
|
356
|
+
* Get all logs with optional filtering
|
357
|
+
* @param filter - Filter criteria
|
358
|
+
* @returns Filtered logs
|
359
|
+
*/
|
360
|
+
public getLogs(filter?: {
|
361
|
+
level?: LogLevel;
|
362
|
+
traceId?: string;
|
363
|
+
spanId?: string;
|
364
|
+
startTime?: number;
|
365
|
+
endTime?: number;
|
366
|
+
}): any[] {
|
367
|
+
let filtered = this.logs;
|
368
|
+
|
369
|
+
if (filter) {
|
370
|
+
filtered = filtered.filter(log => {
|
371
|
+
if (filter.level && log.level !== filter.level) return false;
|
372
|
+
if (filter.traceId && log.context.traceId !== filter.traceId) return false;
|
373
|
+
if (filter.spanId && log.context.spanId !== filter.spanId) return false;
|
374
|
+
if (filter.startTime && log.timestamp < filter.startTime) return false;
|
375
|
+
if (filter.endTime && log.timestamp > filter.endTime) return false;
|
376
|
+
return true;
|
377
|
+
});
|
378
|
+
}
|
379
|
+
|
380
|
+
return filtered;
|
381
|
+
}
|
382
|
+
}
|
383
|
+
|
384
|
+
/**
|
385
|
+
* Monitoring system implementation
|
386
|
+
*
|
387
|
+
* Features:
|
388
|
+
* - Metric collection with support for counters, gauges, histograms, and summaries
|
389
|
+
* - Prometheus format metric export
|
390
|
+
* - Distributed tracing with context propagation
|
391
|
+
* - Structured logging with trace correlation
|
392
|
+
* - In-memory storage with filtering capabilities
|
393
|
+
*/
|
394
|
+
export class MonitoringSystemImpl implements MonitoringSystem {
|
395
|
+
public readonly metrics: MetricCollector;
|
396
|
+
public readonly tracer: Tracer;
|
397
|
+
public readonly logger: Logger;
|
398
|
+
|
399
|
+
constructor() {
|
400
|
+
const tracer = new TracerImpl();
|
401
|
+
this.metrics = new MetricCollectorImpl();
|
402
|
+
this.tracer = tracer;
|
403
|
+
this.logger = new LoggerImpl(tracer);
|
404
|
+
}
|
405
|
+
|
406
|
+
public async init(): Promise<void> {
|
407
|
+
this.logger.info('Monitoring system initialized');
|
408
|
+
}
|
409
|
+
|
410
|
+
public async shutdown(): Promise<void> {
|
411
|
+
this.logger.info('Monitoring system shutdown');
|
412
|
+
}
|
413
|
+
}
|