-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathruntime-run.ts
More file actions
119 lines (110 loc) · 3.21 KB
/
Copy pathruntime-run.ts
File metadata and controls
119 lines (110 loc) · 3.21 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
109
110
111
112
113
114
115
116
117
118
119
/**
* Production-run lifecycle: drive a streaming task through `runAgentTaskStream`
* and record a canonical `RuntimeRunRow` for cost / audit dashboards.
*
* Wire into your own DB by implementing the `RuntimeRunPersistenceAdapter`
* interface (one `upsert(row)` method).
*
* Run with:
* pnpm tsx examples/runtime-run/runtime-run.ts
*/
import {
type AgentBackendInput,
type AgentTaskSpec,
createIterableBackend,
type RuntimeRunPersistenceAdapter,
type RuntimeRunRow,
runAgentTaskStream,
startRuntimeRun,
} from '@tangle-network/agent-runtime'
const readyTask: AgentTaskSpec = {
id: 'legal-chat:thread-42',
intent: 'Run a legal advisory chat turn with workspace context.',
domain: 'legal',
metadata: { workspaceId: 'ws-1', threadId: 'thread-42' },
}
// Toy backend that yields a couple of llm_call events so the cost ledger has
// real input. Real consumers plug in `createOpenAICompatibleBackend`,
// `createSandboxPromptBackend`, or any `AgentExecutionBackend`.
const backend = createIterableBackend<AgentBackendInput>({
kind: 'demo',
async *stream(_input, ctx) {
yield {
type: 'llm_call',
task: ctx.task,
session: ctx.session,
model: 'claude-sonnet-4-6',
tokensIn: 1_200,
tokensOut: 280,
costUsd: 0.0042,
latencyMs: 510,
timestamp: new Date().toISOString(),
}
yield {
type: 'text_delta',
task: ctx.task,
session: ctx.session,
text: 'Reviewed the matter. No blocking issues found.',
timestamp: new Date().toISOString(),
}
yield {
type: 'llm_call',
task: ctx.task,
session: ctx.session,
model: 'claude-sonnet-4-6',
tokensIn: 600,
tokensOut: 110,
costUsd: 0.0019,
latencyMs: 220,
timestamp: new Date().toISOString(),
}
},
})
// In-memory adapter for demonstration. Real adapters write to D1 / postgres /
// the agent's `agentRuns` table — same `upsert(row)` shape.
const persisted: RuntimeRunRow[] = []
const adapter: RuntimeRunPersistenceAdapter = {
upsert(row) {
persisted.push(row)
},
}
async function main(): Promise<void> {
const run = startRuntimeRun({
workspaceId: 'ws-1',
sessionId: 'thread-42',
agentId: 'legal-chat-runtime',
taskSpec: readyTask,
scenarioId: 'legal-chat:thread-42',
adapter,
})
try {
for await (const event of runAgentTaskStream({
task: readyTask,
backend,
input: { message: 'Please review the latest filing.' },
})) {
run.observe(event)
if (event.type === 'final') {
const status = event.status === 'completed' ? 'completed' : 'failed'
run.complete({
status,
resultSummary: status === 'completed' ? 'Reviewed' : 'Stream did not complete cleanly',
error: status === 'failed' ? event.reason : undefined,
})
}
}
} catch (err) {
run.complete({
status: 'failed',
resultSummary: 'Stream threw before final event',
error: err instanceof Error ? err.message : String(err),
})
}
await run.persist({ note: 'demo persistence metadata' })
console.log('Cost ledger:', run.cost())
console.log('Persisted row:', persisted[0])
}
main().catch((err) => {
console.error(err)
process.exit(1)
})