-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinline-executor.ts
More file actions
95 lines (90 loc) · 3.44 KB
/
Copy pathinline-executor.ts
File metadata and controls
95 lines (90 loc) · 3.44 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
/**
* The offline plumbing for recursive-supervisor — a scripted leaf `Executor` and the two ways
* this example resolves children to one.
*
* It lives in this sibling so `recursive-supervisor.ts` leads with its LESSON (`scope.spawn` on a
* conserved budget pool, then the `fanout` combinator) instead of registry plumbing and as-casts.
* A real leaf streams `UsageEvent`s as it burns budget and exposes its terminal artifact via
* `resultArtifact()`; the scripted one derives its artifact from the task it was handed, with fixed
* usage numbers. Swap any of this for `createExecutor({ backend })` (router / router-tools /
* sandbox / cli) or any object implementing `Executor`.
*/
import type {
Agent,
AgentProfile,
AgentSpec,
DefaultVerdict,
Executor,
ExecutorResult,
UsageEvent,
} from '@tangle-network/agent-runtime/loops'
import { createExecutorRegistry } from '@tangle-network/agent-runtime/loops'
export interface Script {
out: string
score: number
tokens: { input: number; output: number }
}
export function scriptedExecutor(scriptFor: (task: unknown) => Script): Executor<unknown> {
let artifact: ExecutorResult<unknown> | undefined
return {
runtime: 'router',
execute(task: unknown): AsyncIterable<UsageEvent> {
const script = scriptFor(task)
return (async function* () {
const verdict: DefaultVerdict = { valid: true, score: script.score }
artifact = {
outRef: `mock:${script.out}`,
out: script.out,
verdict,
spent: { iterations: 1, tokens: script.tokens, usd: 0, ms: 0 },
}
yield { kind: 'iteration' }
yield { kind: 'tokens', input: script.tokens.input, output: script.tokens.output }
})()
},
teardown: () => Promise.resolve({ destroyed: true }),
resultArtifact(): ExecutorResult<unknown> {
if (!artifact) throw new Error('mock executor: resultArtifact before stream drained')
return artifact
},
}
}
/** A leaf agent carrying its executor as the BYO `executorSpec.executor` —
* the default registry resolves it verbatim, so no built-in runtime fires. */
export function leaf(name: string, script: Script): Agent<unknown, unknown> {
const spec: AgentSpec = {
profile: { name } as AgentProfile,
harness: null,
executor: scriptedExecutor(() => script),
}
return { name, act: async () => script.out, executorSpec: spec } as Agent<unknown, unknown> & {
executorSpec: AgentSpec
}
}
/** The Part-2 registry: mints a fresh scripted leaf per spawn, scored by the angle index the
* fanout combinator put on the child task. `definePersona` binds it as the persona's resolver.
* Returned with its structural type inferred — the persona's `executors.registry` field accepts
* any object with this `register` + `resolve` shape. */
export function scriptedPersonaRegistry() {
const base = createExecutorRegistry()
return {
register: base.register.bind(base),
resolve<Out>(_spec: AgentSpec) {
return {
succeeded: true as const,
value: (): Executor<Out> =>
scriptedExecutor((task) => {
const index =
task && typeof task === 'object' && 'index' in task
? Number((task as { index: unknown }).index)
: 0
return {
out: `thesis-${index}`,
score: 0.3 + index * 0.3,
tokens: { input: 20, output: 20 },
}
}) as Executor<Out>,
}
},
}
}