-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.ts
More file actions
108 lines (94 loc) · 3.32 KB
/
server.ts
File metadata and controls
108 lines (94 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* Tracing example — server.
*
* Wires `@arcp/middleware-otel` into the runtime side so every inbound
* frame extracts the W3C trace context from `extensions["x.otel"]` and
* every outbound frame injects one. Spans are exported to the console
* via `ConsoleSpanExporter` — no collector required.
*/
import {
ConsoleSpanExporter,
SimpleSpanProcessor,
} from "@opentelemetry/sdk-trace-base";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { trace } from "@opentelemetry/api";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import {
ARCPServer,
StaticBearerVerifier,
startWebSocketServer,
} from "@arcp/sdk";
import { withTracing } from "@arcp/middleware-otel";
const PORT = Number(process.env.ARCP_DEMO_PORT ?? 7895);
const TOKEN = process.env.ARCP_DEMO_TOKEN ?? "demo-token";
async function main(): Promise<void> {
const provider = new NodeTracerProvider({
resource: resourceFromAttributes({
[SEMRESATTRS_SERVICE_NAME]: "arcp-tracing-demo-server",
}),
spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())],
});
provider.register();
const tracer = trace.getTracer("arcp-tracing-demo-server");
const server = new ARCPServer({
runtime: { name: "tracing-demo", version: "1.0.0" },
capabilities: { encodings: ["json"], agents: ["parent", "child"] },
bearer: new StaticBearerVerifier(new Map([[TOKEN, { principal: "demo" }]])),
});
// Parent agent: emits a tool_call/tool_result pair then delegates to child.
server.registerAgent("parent", async (input, ctx) => {
const { item } = input as { item: string };
await ctx.status("calling tool");
await ctx.toolCall({
call_id: "call_lookup",
tool: "catalog.lookup",
args: { item },
});
await sleep(20);
await ctx.toolResult({
call_id: "call_lookup",
result: { price_usd: 42 },
});
await ctx.status("delegating child");
await ctx.delegate({
delegate_id: "del_child",
agent: "child",
input: { item, price_usd: 42 },
});
return { item, ok: true };
});
// Child agent: trivial work — exists so we can show trace_id inheritance.
server.registerAgent("child", async (input, ctx) => {
await ctx.log("info", `child processing ${JSON.stringify(input)}`);
await sleep(15);
return { received: input };
});
const wss = await startWebSocketServer({
host: "127.0.0.1",
port: PORT,
onTransport: (raw) => {
// Wrap the raw transport BEFORE handing it to the runtime so every
// envelope on this connection produces a span.
server.accept(withTracing(raw, { tracer }));
},
});
console.log(`ARCP runtime listening on ${wss.url}`);
console.log("OTel spans → ConsoleSpanExporter (this stdout).");
console.log("Press Ctrl+C to stop.");
const shutdown = async (): Promise<void> => {
console.log("\nshutting down...");
await wss.close();
await server.close();
await provider.shutdown();
process.exit(0);
};
process.on("SIGINT", () => void shutdown());
process.on("SIGTERM", () => void shutdown());
}
const sleep = (ms: number): Promise<void> =>
new Promise((resolve) => setTimeout(resolve, ms));
void main().catch((err) => {
console.error(err);
process.exit(1);
});