@loopstack/observability-examples 0.1.0 → 0.1.2
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/README.md +60 -6
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/observability-examples.module.d.ts +6 -1
- package/dist/observability-examples.module.d.ts.map +1 -1
- package/dist/observability-examples.module.js +36 -4
- package/dist/observability-examples.module.js.map +1 -1
- package/dist/workflows/audit-log/audit-log-example.workflow.d.ts +10 -0
- package/dist/workflows/audit-log/audit-log-example.workflow.d.ts.map +1 -0
- package/dist/workflows/audit-log/audit-log-example.workflow.js +66 -0
- package/dist/workflows/audit-log/audit-log-example.workflow.js.map +1 -0
- package/dist/workflows/audit-log/listeners/audit.listener.d.ts +9 -0
- package/dist/workflows/audit-log/listeners/audit.listener.d.ts.map +1 -0
- package/dist/workflows/audit-log/listeners/audit.listener.js +44 -0
- package/dist/workflows/audit-log/listeners/audit.listener.js.map +1 -0
- package/dist/workflows/audit-log/services/audit-log.service.d.ts +11 -0
- package/dist/workflows/audit-log/services/audit-log.service.d.ts.map +1 -0
- package/dist/workflows/audit-log/services/audit-log.service.js +28 -0
- package/dist/workflows/audit-log/services/audit-log.service.js.map +1 -0
- package/dist/workflows/custom-calculator/calculators/words-processed.calculator.d.ts +7 -0
- package/dist/workflows/custom-calculator/calculators/words-processed.calculator.d.ts.map +1 -0
- package/dist/workflows/custom-calculator/calculators/words-processed.calculator.js +15 -0
- package/dist/workflows/custom-calculator/calculators/words-processed.calculator.js.map +1 -0
- package/dist/workflows/custom-calculator/custom-calculator-example.workflow.d.ts +17 -0
- package/dist/workflows/custom-calculator/custom-calculator-example.workflow.d.ts.map +1 -0
- package/dist/workflows/custom-calculator/custom-calculator-example.workflow.js +66 -0
- package/dist/workflows/custom-calculator/custom-calculator-example.workflow.js.map +1 -0
- package/dist/workflows/custom-calculator/tools/analyze-text.tool.d.ts +19 -0
- package/dist/workflows/custom-calculator/tools/analyze-text.tool.d.ts.map +1 -0
- package/dist/workflows/custom-calculator/tools/analyze-text.tool.js +35 -0
- package/dist/workflows/custom-calculator/tools/analyze-text.tool.js.map +1 -0
- package/dist/workflows/quota/quota-example.workflow.js +1 -1
- package/dist/workflows/quota/quota-example.workflow.js.map +1 -1
- package/dist/workflows/tracing/interceptors/tracing.interceptor.d.ts +10 -0
- package/dist/workflows/tracing/interceptors/tracing.interceptor.d.ts.map +1 -0
- package/dist/workflows/tracing/interceptors/tracing.interceptor.js +48 -0
- package/dist/workflows/tracing/interceptors/tracing.interceptor.js.map +1 -0
- package/dist/workflows/tracing/services/tool-trace.service.d.ts +12 -0
- package/dist/workflows/tracing/services/tool-trace.service.d.ts.map +1 -0
- package/dist/workflows/tracing/services/tool-trace.service.js +28 -0
- package/dist/workflows/tracing/services/tool-trace.service.js.map +1 -0
- package/dist/workflows/tracing/tools/simulate-work.tool.d.ts +20 -0
- package/dist/workflows/tracing/tools/simulate-work.tool.d.ts.map +1 -0
- package/dist/workflows/tracing/tools/simulate-work.tool.js +36 -0
- package/dist/workflows/tracing/tools/simulate-work.tool.js.map +1 -0
- package/dist/workflows/tracing/tracing-example.workflow.d.ts +17 -0
- package/dist/workflows/tracing/tracing-example.workflow.d.ts.map +1 -0
- package/dist/workflows/tracing/tracing-example.workflow.js +62 -0
- package/dist/workflows/tracing/tracing-example.workflow.js.map +1 -0
- package/package.json +4 -2
- package/src/index.ts +10 -0
- package/src/observability-examples.module.ts +33 -6
- package/src/workflows/audit-log/audit-log-example.workflow.ts +43 -0
- package/src/workflows/audit-log/listeners/audit.listener.ts +22 -0
- package/src/workflows/audit-log/services/audit-log.service.ts +25 -0
- package/src/workflows/custom-calculator/__tests__/custom-calculator-example.spec.ts +27 -0
- package/src/workflows/custom-calculator/calculators/words-processed.calculator.ts +15 -0
- package/src/workflows/custom-calculator/custom-calculator-example.workflow.ts +52 -0
- package/src/workflows/custom-calculator/tools/analyze-text.tool.ts +32 -0
- package/src/workflows/quota/quota-example.workflow.ts +1 -1
- package/src/workflows/tracing/interceptors/tracing.interceptor.ts +31 -0
- package/src/workflows/tracing/services/tool-trace.service.ts +26 -0
- package/src/workflows/tracing/tools/simulate-work.tool.ts +33 -0
- package/src/workflows/tracing/tracing-example.workflow.ts +47 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
2
|
+
import { ToolEnvelope, ToolExecutionContext, ToolInterceptor, UseToolInterceptor } from '@loopstack/common';
|
|
3
|
+
import { ToolTraceService } from '../services/tool-trace.service';
|
|
4
|
+
|
|
5
|
+
@UseToolInterceptor({ priority: 10 })
|
|
6
|
+
export class TracingInterceptor implements ToolInterceptor {
|
|
7
|
+
private readonly logger = new Logger(TracingInterceptor.name);
|
|
8
|
+
|
|
9
|
+
constructor(private readonly traceStore: ToolTraceService) {}
|
|
10
|
+
|
|
11
|
+
async intercept(context: ToolExecutionContext, next: () => Promise<ToolEnvelope>): Promise<ToolEnvelope> {
|
|
12
|
+
const toolName = context.tool.constructor.name;
|
|
13
|
+
const workflowId = context.runContext.workflowId;
|
|
14
|
+
const startTime = performance.now();
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const result = await next();
|
|
18
|
+
this.trace(toolName, workflowId, startTime, true);
|
|
19
|
+
return result;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
this.trace(toolName, workflowId, startTime, false);
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private trace(toolName: string, workflowId: string, startTime: number, success: boolean): void {
|
|
27
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
28
|
+
this.traceStore.record({ toolName, workflowId, durationMs, success });
|
|
29
|
+
this.logger.log(`${toolName} — ${success ? 'ok' : 'failed'} in ${durationMs}ms (workflow ${workflowId})`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
export interface ToolTraceEntry {
|
|
4
|
+
toolName: string;
|
|
5
|
+
workflowId: string;
|
|
6
|
+
durationMs: number;
|
|
7
|
+
success: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const MAX_ENTRIES = 100;
|
|
11
|
+
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class ToolTraceService {
|
|
14
|
+
private readonly entries: ToolTraceEntry[] = [];
|
|
15
|
+
|
|
16
|
+
record(entry: ToolTraceEntry): void {
|
|
17
|
+
this.entries.push(entry);
|
|
18
|
+
if (this.entries.length > MAX_ENTRIES) {
|
|
19
|
+
this.entries.shift();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
forWorkflow(workflowId: string): ToolTraceEntry[] {
|
|
24
|
+
return this.entries.filter((entry) => entry.workflowId === workflowId);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseTool, Tool, ToolEnvelope } from '@loopstack/common';
|
|
3
|
+
|
|
4
|
+
const SimulateWorkSchema = z.object({
|
|
5
|
+
label: z.string(),
|
|
6
|
+
delayMs: z.number().min(0).max(5000),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
type SimulateWorkArgs = z.infer<typeof SimulateWorkSchema>;
|
|
10
|
+
|
|
11
|
+
export interface SimulateWorkToolResult {
|
|
12
|
+
label: string;
|
|
13
|
+
delayMs: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const SimulateWorkToolResultSchema = z.strictObject({
|
|
17
|
+
label: z.string(),
|
|
18
|
+
delayMs: z.number(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
@Tool({
|
|
22
|
+
name: 'simulate_work',
|
|
23
|
+
description: 'Sleeps for the given duration so interceptors have something to measure.',
|
|
24
|
+
schema: SimulateWorkSchema,
|
|
25
|
+
resultSchema: SimulateWorkToolResultSchema,
|
|
26
|
+
effects: 'none',
|
|
27
|
+
})
|
|
28
|
+
export class SimulateWorkTool extends BaseTool<SimulateWorkArgs, object, SimulateWorkToolResult> {
|
|
29
|
+
protected async handle(args: SimulateWorkArgs): Promise<ToolEnvelope<SimulateWorkToolResult>> {
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, args.delayMs));
|
|
31
|
+
return { data: { label: args.label, delayMs: args.delayMs } };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseWorkflow, MarkdownDocument, Transition, Workflow } from '@loopstack/common';
|
|
3
|
+
import type { RunContext } from '@loopstack/common';
|
|
4
|
+
import { ToolTraceService } from './services/tool-trace.service';
|
|
5
|
+
import { SimulateWorkTool } from './tools/simulate-work.tool';
|
|
6
|
+
|
|
7
|
+
const TracingExampleArgsSchema = z.object({
|
|
8
|
+
steps: z.number().min(1).max(10).default(3),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
type TracingExampleArgs = z.infer<typeof TracingExampleArgsSchema>;
|
|
12
|
+
|
|
13
|
+
@Workflow({
|
|
14
|
+
title: 'Observability - Tracing Interceptor Example',
|
|
15
|
+
description:
|
|
16
|
+
'Demonstrates a custom ToolInterceptor — @UseToolInterceptor() registers it app-wide, it measures every tool execution and stores trace entries in an injectable service. The workflow runs a few tool calls and renders the collected trace.',
|
|
17
|
+
schema: TracingExampleArgsSchema,
|
|
18
|
+
})
|
|
19
|
+
export class TracingExampleWorkflow extends BaseWorkflow<TracingExampleArgs> {
|
|
20
|
+
constructor(
|
|
21
|
+
private readonly simulateWorkTool: SimulateWorkTool,
|
|
22
|
+
private readonly traceStore: ToolTraceService,
|
|
23
|
+
) {
|
|
24
|
+
super();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Transition({ to: 'end' })
|
|
28
|
+
async runAndTrace(_state: object, ctx: RunContext<TracingExampleArgs>) {
|
|
29
|
+
for (let i = 1; i <= ctx.args.steps; i++) {
|
|
30
|
+
await this.simulateWorkTool.call({ label: `step-${i}`, delayMs: i * 100 });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const entries = this.traceStore.forWorkflow(ctx.workflowId);
|
|
34
|
+
|
|
35
|
+
await this.documentStore.save(MarkdownDocument, {
|
|
36
|
+
markdown: [
|
|
37
|
+
'## Tool Trace',
|
|
38
|
+
'',
|
|
39
|
+
'Collected by `TracingInterceptor` — it wraps every tool call in this app.',
|
|
40
|
+
'',
|
|
41
|
+
'| Tool | Duration | Status |',
|
|
42
|
+
'| ---- | -------- | ------ |',
|
|
43
|
+
...entries.map((e) => `| \`${e.toolName}\` | ${e.durationMs}ms | ${e.success ? '✅ ok' : '❌ failed'} |`),
|
|
44
|
+
].join('\n'),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|