@llm4ts/flow 2.35.2 → 2.35.4

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/src/Roster.ts CHANGED
@@ -407,6 +407,8 @@ export interface LeaseOptions {
407
407
  readonly prefer?: string
408
408
  /** Who is asking, for the events (a story's worktree). */
409
409
  readonly label?: string
410
+ /** What the call is for beyond its role (a review lens), for the events. */
411
+ readonly purpose?: string
410
412
  /**
411
413
  * The executor holding this context's coder, taken when independence
412
414
  * cannot be had now: no executor outside `avoid` can ever take the role,
@@ -516,6 +518,9 @@ export const makeRoster = Effect.fn("@llm4ts/flow/Roster.make")(function* (
516
518
  }
517
519
  }
518
520
 
521
+ const purposeOf = (who: LeaseOptions): { readonly purpose?: string } =>
522
+ who.purpose === undefined ? {} : { purpose: who.purpose }
523
+
519
524
  const say = (message: string): Effect.Effect<void> =>
520
525
  events.publish(Info.make({ message: `roster: ${message}` }))
521
526
 
@@ -599,13 +604,15 @@ export const makeRoster = Effect.fn("@llm4ts/flow/Roster.make")(function* (
599
604
  const leaseOf = (
600
605
  spec: ExecutorSpec,
601
606
  role: Role,
602
- label: string | undefined,
607
+ who: LeaseOptions,
603
608
  because?: "nobody" | "held" | "out"
604
609
  ): Effect.Effect<Lease, never, Scope.Scope> =>
605
610
  Effect.gen(function* () {
606
- const labelled = label === undefined ? {} : { label }
611
+ const labelled = who.label === undefined ? {} : { label: who.label }
607
612
  const own = because === undefined ? {} : { because }
608
- yield* events.publish(ExecutorLeased.make({ executor: spec.id, role, ...labelled, ...own }))
613
+ yield* events.publish(
614
+ ExecutorLeased.make({ executor: spec.id, role, ...labelled, ...purposeOf(who), ...own })
615
+ )
609
616
  const done = yield* Ref.make(false)
610
617
  const free = Effect.flatMap(Ref.getAndSet(done, true), (was) =>
611
618
  was
@@ -623,13 +630,20 @@ export const makeRoster = Effect.fn("@llm4ts/flow/Roster.make")(function* (
623
630
  const borrowedLease = (
624
631
  spec: ExecutorSpec,
625
632
  role: Role,
626
- label: string | undefined,
633
+ who: LeaseOptions,
627
634
  because: "nobody" | "held" | "out"
628
635
  ): Effect.Effect<Lease, never, Scope.Scope> =>
629
636
  Effect.gen(function* () {
630
- const labelled = label === undefined ? {} : { label }
637
+ const labelled = who.label === undefined ? {} : { label: who.label }
631
638
  yield* events.publish(
632
- ExecutorLeased.make({ executor: spec.id, role, ...labelled, borrowed: true, because })
639
+ ExecutorLeased.make({
640
+ executor: spec.id,
641
+ role,
642
+ ...labelled,
643
+ ...purposeOf(who),
644
+ borrowed: true,
645
+ because
646
+ })
633
647
  )
634
648
  const done = yield* Ref.make(false)
635
649
  const free = Effect.flatMap(Ref.getAndSet(done, true), (was) =>
@@ -842,11 +856,11 @@ export const makeRoster = Effect.fn("@llm4ts/flow/Roster.make")(function* (
842
856
  })
843
857
  yield* waited
844
858
  return own !== undefined && own.id === borrow.id
845
- ? yield* leaseOf(own, role, leaseOptions.label, because)
859
+ ? yield* leaseOf(own, role, leaseOptions, because)
846
860
  : own !== undefined
847
861
  ? // Someone came back between the check and the pick: independent after all.
848
- yield* leaseOf(own, role, leaseOptions.label)
849
- : yield* borrowedLease(borrow, role, leaseOptions.label, because)
862
+ yield* leaseOf(own, role, leaseOptions)
863
+ : yield* borrowedLease(borrow, role, leaseOptions, because)
850
864
  })
851
865
  let announced = false
852
866
  const asked = yield* Clock.currentTimeMillis
@@ -867,7 +881,7 @@ export const makeRoster = Effect.fn("@llm4ts/flow/Roster.make")(function* (
867
881
  const spec = yield* attempt(role, leaseOptions)
868
882
  if (spec !== undefined) {
869
883
  yield* waited
870
- return yield* leaseOf(spec, role, leaseOptions.label)
884
+ return yield* leaseOf(spec, role, leaseOptions)
871
885
  }
872
886
  if (!(yield* canEverServe(role, avoid))) {
873
887
  const own = yield* takeOwn("nobody")
@@ -906,14 +920,14 @@ export const makeRoster = Effect.fn("@llm4ts/flow/Roster.make")(function* (
906
920
  })
907
921
  )
908
922
  yield* say(
909
- `waiting for an executor to take ${role}${leaseOptions.label === undefined ? "" : ` for ${leaseOptions.label}`}: ${yield* waitingReport(role, avoid)}`
923
+ `waiting for an executor to take ${role}${leaseOptions.label === undefined ? "" : ` for ${leaseOptions.label}`}${leaseOptions.purpose === undefined ? "" : ` (${leaseOptions.purpose})`}: ${yield* waitingReport(role, avoid)}`
910
924
  )
911
925
  }
912
926
  yield* probeDue
913
927
  const recovered = yield* attempt(role, leaseOptions)
914
928
  if (recovered !== undefined) {
915
929
  yield* waited
916
- return yield* leaseOf(recovered, role, leaseOptions.label)
930
+ return yield* leaseOf(recovered, role, leaseOptions)
917
931
  }
918
932
  yield* waitOrProbe(role)
919
933
  }
@@ -924,7 +938,7 @@ export const makeRoster = Effect.fn("@llm4ts/flow/Roster.make")(function* (
924
938
  leaseOptions: LeaseOptions = {}
925
939
  ): Effect.Effect<Lease | undefined, never, Scope.Scope> =>
926
940
  Effect.flatMap(attempt(role, leaseOptions), (spec) =>
927
- spec === undefined ? Effect.succeed(undefined) : leaseOf(spec, role, leaseOptions.label)
941
+ spec === undefined ? Effect.succeed(undefined) : leaseOf(spec, role, leaseOptions)
928
942
  )
929
943
 
930
944
  const exclude = (exclusion: Exclusion): Effect.Effect<void> =>
@@ -13,6 +13,7 @@ import type { LlmServiceShape } from "@llm4ts/core/LlmService"
13
13
  import { Message, type ConnectorCapabilities, type LlmChunk } from "@llm4ts/core/Models"
14
14
  import { describeFlowError, type FlowError } from "./FlowError.ts"
15
15
  import { ExecutorHandedOver, type FlowEventsShape } from "./FlowEvents.ts"
16
+ import { callPurpose } from "./Timing.ts"
16
17
  import {
17
18
  describeExclusion,
18
19
  hasRole,
@@ -141,10 +142,12 @@ export const rosterSeat = (
141
142
  // The roster decides who serves: an independent executor, or, when
142
143
  // none can ever serve or every one is held by a coder, the context's own.
143
144
  const borrow = options.borrow === undefined ? undefined : yield* options.borrow
145
+ const purpose = yield* callPurpose
144
146
  const lease = yield* roster
145
147
  .lease(role, {
146
148
  avoid,
147
149
  ...(options.label === undefined ? {} : { label: options.label }),
150
+ ...(purpose === undefined ? {} : { purpose }),
148
151
  ...(borrow === undefined ? {} : { borrow })
149
152
  })
150
153
  .pipe(Effect.mapError(asLlmError))
package/src/Timing.ts CHANGED
@@ -178,6 +178,22 @@ const TimedRole = Context.Reference<string | undefined>("@llm4ts/flow/Timing/Tim
178
178
  defaultValue: () => undefined
179
179
  })
180
180
 
181
+ /**
182
+ * What a call is for, beyond its role: a review lens, a vote. A roster
183
+ * lease names it (`topolino takes reviewer for home · adversarial lens`),
184
+ * so parallel calls of one role read as the different questions they are.
185
+ */
186
+ const CallPurpose = Context.Reference<string | undefined>("@llm4ts/flow/Timing/CallPurpose", {
187
+ defaultValue: () => undefined
188
+ })
189
+
190
+ /** The purpose the current call is made for, when its caller named one. */
191
+ export const callPurpose: Effect.Effect<string | undefined> = CallPurpose
192
+
193
+ /** Runs `effect`'s calls under `purpose`, for the roster's lease events. */
194
+ export const withCallPurpose = <A, E, R>(purpose: string, effect: Effect.Effect<A, E, R>) =>
195
+ Effect.provideService(effect, CallPurpose, purpose)
196
+
181
197
  /** The role the current call is made for: the caller's, or else `label`. */
182
198
  export const roleOr = (label: string): Effect.Effect<string> =>
183
199
  Effect.map(TimedRole, (role) => role ?? label)