liminal 0.16.0 → 0.17.0

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.
Files changed (145) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/Digest.ts +30 -0
  3. package/Envelope.ts +15 -47
  4. package/F/F.ts +1 -0
  5. package/L/L.ts +6 -7
  6. package/L/append.ts +4 -4
  7. package/L/assistant.ts +2 -2
  8. package/L/assistantSchema.ts +2 -2
  9. package/L/assistantStream.ts +2 -2
  10. package/L/branch.ts +4 -17
  11. package/L/clear.ts +4 -4
  12. package/L/disable.ts +2 -2
  13. package/L/enable.ts +2 -2
  14. package/L/events.ts +2 -2
  15. package/L/{userJson.ts → json.ts} +3 -5
  16. package/L/line.ts +22 -0
  17. package/L/{handle.ts → listen.ts} +2 -2
  18. package/L/messages.ts +2 -2
  19. package/L/prev.ts +4 -0
  20. package/L/provide.ts +10 -0
  21. package/L/{Self.ts → self.ts} +1 -1
  22. package/L/send.ts +56 -0
  23. package/L/system.ts +18 -7
  24. package/L/thread.ts +5 -17
  25. package/L/toolkit.ts +2 -2
  26. package/L/user.test.ts +4 -1
  27. package/L/user.ts +15 -5
  28. package/LEvent.ts +23 -13
  29. package/Thread.ts +19 -21
  30. package/dist/Digest.d.ts +17 -0
  31. package/dist/Digest.js +7 -0
  32. package/dist/Digest.js.map +1 -0
  33. package/dist/Envelope.d.ts +13 -15
  34. package/dist/Envelope.js +1 -29
  35. package/dist/Envelope.js.map +1 -1
  36. package/dist/F/F.d.ts +1 -0
  37. package/dist/F/F.js +2 -0
  38. package/dist/F/F.js.map +1 -0
  39. package/dist/L/L.d.ts +6 -7
  40. package/dist/L/L.js +6 -7
  41. package/dist/L/L.js.map +1 -1
  42. package/dist/L/append.js +4 -4
  43. package/dist/L/append.js.map +1 -1
  44. package/dist/L/assistant.js +2 -2
  45. package/dist/L/assistantSchema.js +2 -2
  46. package/dist/L/assistantStream.js +2 -2
  47. package/dist/L/branch.d.ts +1 -4
  48. package/dist/L/branch.js +4 -7
  49. package/dist/L/branch.js.map +1 -1
  50. package/dist/L/clear.js +4 -4
  51. package/dist/L/clear.js.map +1 -1
  52. package/dist/L/disable.js +2 -2
  53. package/dist/L/enable.js +2 -2
  54. package/dist/L/events.js +2 -2
  55. package/dist/L/{userJson.d.ts → json.d.ts} +1 -2
  56. package/dist/L/{userJson.js → json.js} +3 -4
  57. package/dist/L/json.js.map +1 -0
  58. package/dist/L/line.d.ts +5 -0
  59. package/dist/L/line.js +11 -0
  60. package/dist/L/line.js.map +1 -0
  61. package/dist/L/{handle.js → listen.js} +3 -3
  62. package/dist/L/{handle.js.map → listen.js.map} +1 -1
  63. package/dist/L/messages.js +2 -2
  64. package/dist/L/prev.d.ts +5 -0
  65. package/dist/L/prev.js +5 -0
  66. package/dist/L/prev.js.map +1 -0
  67. package/dist/L/provide.d.ts +3 -0
  68. package/dist/L/provide.js +4 -0
  69. package/dist/L/provide.js.map +1 -0
  70. package/dist/L/{Self.d.ts → self.d.ts} +1 -1
  71. package/dist/L/self.js +4 -0
  72. package/dist/L/self.js.map +1 -0
  73. package/dist/L/send.d.ts +6 -0
  74. package/dist/L/send.js +34 -0
  75. package/dist/L/send.js.map +1 -0
  76. package/dist/L/system.d.ts +3 -3
  77. package/dist/L/system.js +6 -4
  78. package/dist/L/system.js.map +1 -1
  79. package/dist/L/thread.d.ts +1 -4
  80. package/dist/L/thread.js +5 -7
  81. package/dist/L/thread.js.map +1 -1
  82. package/dist/L/toolkit.js +2 -2
  83. package/dist/L/user.d.ts +5 -3
  84. package/dist/L/user.js +3 -3
  85. package/dist/L/user.js.map +1 -1
  86. package/dist/L/user.test.js.map +1 -1
  87. package/dist/LEvent.d.ts +143 -15
  88. package/dist/LEvent.js +12 -9
  89. package/dist/LEvent.js.map +1 -1
  90. package/dist/Thread.d.ts +15 -18
  91. package/dist/Thread.js +7 -13
  92. package/dist/Thread.js.map +1 -1
  93. package/dist/index.d.ts +4 -2
  94. package/dist/index.js +4 -2
  95. package/dist/index.js.map +1 -1
  96. package/dist/patterns/Route.js +2 -2
  97. package/dist/patterns/Route.js.map +1 -1
  98. package/dist/tsconfig.tsbuildinfo +1 -1
  99. package/dist/util/Taggable.d.ts +5 -4
  100. package/dist/util/Taggable.js +11 -0
  101. package/dist/util/Taggable.js.map +1 -1
  102. package/dist/util/extract.d.ts +3 -0
  103. package/dist/util/extract.js +2 -0
  104. package/dist/util/extract.js.map +1 -0
  105. package/dist/util/messageCodec.d.ts +98 -0
  106. package/dist/util/messageCodec.js +5 -0
  107. package/dist/util/messageCodec.js.map +1 -0
  108. package/dist/util/raw.d.ts +6 -0
  109. package/dist/util/raw.js +5 -0
  110. package/dist/util/raw.js.map +1 -0
  111. package/index.ts +4 -2
  112. package/package.json +22 -10
  113. package/patterns/Route.ts +5 -1
  114. package/util/Taggable.ts +30 -11
  115. package/util/extract.ts +7 -0
  116. package/util/messageCodec.ts +5 -0
  117. package/util/raw.ts +27 -0
  118. package/L/fqn.ts +0 -9
  119. package/L/raw.ts +0 -30
  120. package/L/sequence.ts +0 -9
  121. package/L/to.ts +0 -4
  122. package/LPretty.ts +0 -41
  123. package/dist/L/Self.js +0 -4
  124. package/dist/L/Self.js.map +0 -1
  125. package/dist/L/fqn.d.ts +0 -3
  126. package/dist/L/fqn.js +0 -8
  127. package/dist/L/fqn.js.map +0 -1
  128. package/dist/L/raw.d.ts +0 -3
  129. package/dist/L/raw.js +0 -18
  130. package/dist/L/raw.js.map +0 -1
  131. package/dist/L/sequence.d.ts +0 -2
  132. package/dist/L/sequence.js +0 -5
  133. package/dist/L/sequence.js.map +0 -1
  134. package/dist/L/to.d.ts +0 -3
  135. package/dist/L/to.js +0 -3
  136. package/dist/L/to.js.map +0 -1
  137. package/dist/L/userJson.js.map +0 -1
  138. package/dist/LPretty.d.ts +0 -4
  139. package/dist/LPretty.js +0 -37
  140. package/dist/LPretty.js.map +0 -1
  141. package/dist/util/Sequencer.d.ts +0 -4
  142. package/dist/util/Sequencer.js +0 -2
  143. package/dist/util/Sequencer.js.map +0 -1
  144. package/util/Sequencer.ts +0 -13
  145. /package/dist/L/{handle.d.ts → listen.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # liminal
2
2
 
3
+ ## 0.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8212083: Refactor L.sequence -> L.line. Decouple thread effects from the providing of effects via L.provide.
8
+
3
9
  ## 0.16.0
4
10
 
5
11
  ### Minor Changes
package/Digest.ts ADDED
@@ -0,0 +1,30 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Layer from "effect/Layer"
3
+ import * as Option from "effect/Option"
4
+ import * as Schema from "effect/Schema"
5
+ import type { Thread } from "./Thread.ts"
6
+ import { prefix } from "./util/prefix.ts"
7
+
8
+ export const DigestTypeId: unique symbol = Symbol.for(prefix("Digest"))
9
+ export type DigestTypeId = typeof DigestTypeId
10
+
11
+ export declare const Digest: <const K extends string, A = string, I = string>(
12
+ key: K,
13
+ schema?: Schema.Schema<A, I>,
14
+ ) => <Self>() => DigestClass<K, A, I, Self>
15
+
16
+ export interface DigestClass<K extends string, A, I, Self> extends Effect.Effect<A, never, Self> {
17
+ new(_: never): this
18
+
19
+ readonly Type: A
20
+ readonly Encoded: I
21
+ readonly [DigestTypeId]: DigestTypeId
22
+ readonly key: K
23
+ readonly digest: Effect.Effect<void, never, Self>
24
+ readonly layer: <E, R>(
25
+ f: (
26
+ state: Option.Option<A>,
27
+ inbox: Thread,
28
+ ) => Effect.Effect<A, E, R>,
29
+ ) => Layer.Layer<Self, E, Exclude<R, Self | Thread>>
30
+ }
package/Envelope.ts CHANGED
@@ -1,48 +1,16 @@
1
- import * as AiInput from "@effect/ai/AiInput"
2
- import * as Effect from "effect/Effect"
3
- import * as Option from "effect/Option"
4
- import { append } from "./L/append.ts"
5
- import { raw } from "./L/raw.ts"
6
- import { Self } from "./L/Self.ts"
7
- import type { Thread } from "./Thread.ts"
8
- import { type Taggable } from "./util/Taggable.ts"
1
+ import { Thread } from "./Thread.ts"
9
2
 
10
- export interface MessageHeaders {
11
- readonly to: Array<Thread>
12
- readonly cc?: Array<Thread> | undefined
13
- readonly bcc?: Array<Thread> | undefined
14
- }
15
-
16
- export interface EnvelopeMembers {
17
- readonly headers: MessageHeaders
18
- readonly cc: (...cc: Array<Thread>) => Envelope
19
- readonly bcc: (...bcc: Array<Thread>) => Envelope
20
- }
21
-
22
- export interface Envelope extends Taggable<void, never, Thread>, EnvelopeMembers {}
23
-
24
- // TODO: cc, bcc
25
- export const Envelope = (headers: MessageHeaders): Envelope =>
26
- Object.assign(
27
- Effect.fnUntraced(function*(a0, ...aRest) {
28
- const { state: { fqn } } = yield* Self
29
- const name = Option.getOrElse(fqn, () => "anonymous-entity")
30
- const text = yield* raw(a0, ...aRest)
31
- if (!text) return
32
- const { to, cc: _cc, bcc: _bcc } = headers
33
- for (const recipient of to) {
34
- yield* append(AiInput.UserMessage.make({
35
- parts: [
36
- AiInput.TextPart.make({
37
- text: `[FROM: ${name}]\n${text}`,
38
- }),
39
- ],
40
- })).pipe(Effect.provideService(Self, recipient))
41
- }
42
- }) satisfies Taggable<void, never, Thread>,
43
- {
44
- headers,
45
- cc: (...cc) => Envelope({ ...headers, cc }),
46
- bcc: (...bcc) => Envelope({ ...headers, bcc }),
47
- } satisfies EnvelopeMembers,
48
- )
3
+ export type Envelope =
4
+ & ({
5
+ to: Array<Thread>
6
+ cc?: never
7
+ } | {
8
+ to?: never
9
+ cc: Array<Thread>
10
+ } | {
11
+ to: Array<Thread>
12
+ cc: Array<Thread>
13
+ })
14
+ & {
15
+ bcc?: Array<Thread>
16
+ }
package/F/F.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "../L/json.ts"
package/L/L.ts CHANGED
@@ -7,15 +7,14 @@ export * from "./clear.ts"
7
7
  export * from "./disable.ts"
8
8
  export * from "./enable.ts"
9
9
  export * from "./events.ts"
10
- export * from "./fqn.ts"
11
- export * from "./handle.ts"
10
+ export * from "./line.ts"
11
+ export * from "./listen.ts"
12
12
  export * from "./messages.ts"
13
- export * from "./raw.ts"
14
- export * from "./Self.ts"
15
- export * from "./sequence.ts"
13
+ export * from "./prev.ts"
14
+ export * from "./provide.ts"
15
+ export * from "./self.ts"
16
+ export * from "./send.ts"
16
17
  export * from "./system.ts"
17
18
  export * from "./thread.ts"
18
- export * from "./to.ts"
19
19
  export * from "./toolkit.ts"
20
20
  export * from "./user.ts"
21
- export * from "./userJson.ts"
package/L/append.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import type { Message } from "@effect/ai/AiInput"
2
2
  import * as Effect from "effect/Effect"
3
- import { MessagesAppended } from "../LEvent.ts"
3
+ import { MessagesAppendedEvent } from "../LEvent.ts"
4
4
  import type { Thread } from "../Thread.ts"
5
- import { Self } from "./Self.ts"
5
+ import { self } from "./self.ts"
6
6
 
7
7
  /** Append messages to the thread. */
8
8
  export const append: (
9
9
  ...messages: Array<Message>
10
10
  ) => Effect.Effect<void, never, Thread> = Effect.fnUntraced(function*(...messages) {
11
- const { state, events } = yield* Self
11
+ const { state, events } = yield* self
12
12
  state.messages.push(...messages)
13
- yield* events.publish(MessagesAppended.make({ messages }))
13
+ yield* events.publish(MessagesAppendedEvent.make({ messages }))
14
14
  })
package/L/assistant.ts CHANGED
@@ -5,13 +5,13 @@ import * as Effect from "effect/Effect"
5
5
  import * as Option from "effect/Option"
6
6
  import type { Thread } from "../Thread.ts"
7
7
  import { append } from "./append.ts"
8
- import { Self } from "./Self.ts"
8
+ import { self } from "./self.ts"
9
9
  import { toolkit } from "./toolkit.ts"
10
10
 
11
11
  /** Infer an assistant message and append it to the thread. */
12
12
  export const assistant: Effect.Effect<string, AiError, AiLanguageModel | Thread> = Effect.gen(function*() {
13
13
  const model = yield* AiLanguageModel
14
- const { state: { system, messages: prompt } } = yield* Self
14
+ const { state: { system, messages: prompt } } = yield* self
15
15
  let { text, results } = yield* model.generateText({
16
16
  system: Option.getOrUndefined(system),
17
17
  prompt,
@@ -8,7 +8,7 @@ import * as SchemaAST from "effect/SchemaAST"
8
8
  import type { Thread } from "../Thread.ts"
9
9
  import { encodeJsonc, type JsonValue } from "../util/JsonValue.ts"
10
10
  import { append } from "./append.ts"
11
- import { Self } from "./Self.ts"
11
+ import { self } from "./self.ts"
12
12
 
13
13
  /** Infer a structured assistant message and append its JSON representation to the conversation. */
14
14
  export const assistantSchema: {
@@ -20,7 +20,7 @@ export const assistantSchema: {
20
20
  ): Effect.Effect<O, AiError, AiLanguageModel | Thread>
21
21
  } = Effect.fnUntraced(function*(schema) {
22
22
  const model = yield* AiLanguageModel
23
- const { state: { system, messages } } = yield* Self
23
+ const { state: { system, messages } } = yield* self
24
24
 
25
25
  const isSchema = Schema.isSchema(schema)
26
26
  const schema_ = isSchema ? schema : Schema.Struct(schema) as Schema.Schema.AnyNoContext
@@ -5,14 +5,14 @@ import * as Effect from "effect/Effect"
5
5
  import * as Option from "effect/Option"
6
6
  import * as Stream from "effect/Stream"
7
7
  import type { Thread } from "../Thread.ts"
8
- import { Self } from "./Self.ts"
8
+ import { self } from "./self.ts"
9
9
  import { toolkit } from "./toolkit.ts"
10
10
 
11
11
  /** Get a stream of an assistant message (does not append the message to the thread). */
12
12
  export const assistantStream: Stream.Stream<AiResponse, AiError, AiLanguageModel | Thread> = Stream.unwrap(
13
13
  Effect.gen(function*() {
14
14
  const model = yield* AiLanguageModel
15
- const { state: { system, messages: prompt } } = yield* Self
15
+ const { state: { system, messages: prompt } } = yield* self
16
16
  return model.streamText({
17
17
  system: Option.getOrUndefined(system),
18
18
  prompt,
package/L/branch.ts CHANGED
@@ -1,33 +1,20 @@
1
1
  import * as Effect from "effect/Effect"
2
- import { flow } from "effect/Function"
3
2
  import * as Option from "effect/Option"
4
3
  import * as PubSub from "effect/PubSub"
5
4
  import type { LEvent } from "../LEvent.ts"
6
5
  import { Thread, ThreadState } from "../Thread.ts"
7
- import type { Sequencer } from "../util/Sequencer.ts"
8
- import { Self } from "./Self.ts"
9
- import { sequence } from "./sequence.ts"
6
+ import { self } from "./self.ts"
10
7
 
11
- export interface branch extends Sequencer<never, Thread>, Effect.Effect<Thread, never, Thread> {}
12
-
13
- const branch_ = Effect.gen(function*() {
14
- const parent = yield* Self
8
+ export const branch: Effect.Effect<Thread, never, Thread> = Effect.gen(function*() {
9
+ const parent = yield* self
15
10
  return Thread({
11
+ id: parent.id,
16
12
  parent: Option.some(parent),
17
13
  events: yield* PubSub.unbounded<LEvent>(),
18
14
  state: ThreadState.make({
19
- fqn: parent.state.fqn,
20
15
  system: parent.state.system,
21
16
  messages: [...parent.state.messages ?? []],
22
17
  }),
23
18
  tools: parent.tools.pipe(Option.map((v) => new Set(v))),
24
19
  })
25
20
  })
26
-
27
- export const branch: branch = Object.assign(
28
- flow(
29
- sequence,
30
- Effect.provideServiceEffect(Self, branch_),
31
- ),
32
- branch_,
33
- )
package/L/clear.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import type { Message } from "@effect/ai/AiInput"
2
2
  import * as Effect from "effect/Effect"
3
- import { MessagesCleared } from "../LEvent.ts"
3
+ import { ThreadClearedEvent } from "../LEvent.ts"
4
4
  import type { Thread } from "../Thread.ts"
5
- import { Self } from "./Self.ts"
5
+ import { self } from "./self.ts"
6
6
 
7
7
  /** Clear the thread of messages. */
8
8
  export const clear: Effect.Effect<Array<Message>, never, Thread> = Effect.gen(function*() {
9
- const { state, events } = yield* Self
9
+ const { state, events } = yield* self
10
10
  const cleared = state.messages
11
11
  state.messages = []
12
- yield* events.publish(MessagesCleared.make({ cleared }))
12
+ yield* events.publish(ThreadClearedEvent.make({ cleared }))
13
13
  return cleared
14
14
  })
package/L/disable.ts CHANGED
@@ -3,10 +3,10 @@ import * as Effect from "effect/Effect"
3
3
  import * as Option from "effect/Option"
4
4
  import type { Thread } from "../Thread.ts"
5
5
  import type { NeverTool } from "../util/NeverTool.ts"
6
- import { Self } from "./Self.ts"
6
+ import { self } from "./self.ts"
7
7
 
8
8
  export const disable = (tool: AiTool.Any): Effect.Effect<void, never, Thread> =>
9
- Effect.map(Self, ({ tools }) => {
9
+ Effect.map(self, ({ tools }) => {
10
10
  if (Option.isSome(tools)) {
11
11
  tools.value.delete(tool as NeverTool)
12
12
  }
package/L/enable.ts CHANGED
@@ -4,7 +4,7 @@ import * as Option from "effect/Option"
4
4
  import type { Schema } from "effect/Schema"
5
5
  import type { Thread } from "../Thread.ts"
6
6
  import type { NeverTool } from "../util/NeverTool.ts"
7
- import { Self } from "./Self.ts"
7
+ import { self } from "./self.ts"
8
8
 
9
9
  export const enable = <
10
10
  K extends string,
@@ -13,7 +13,7 @@ export const enable = <
13
13
  >(
14
14
  tool: AiTool<K, AnyStructSchema, Schema.Any, E, R>,
15
15
  ): Effect.Effect<void, E, Handler<K> | Thread | R> =>
16
- Effect.map(Self, (thread) => {
16
+ Effect.map(self, (thread) => {
17
17
  const tool_: NeverTool = tool as never
18
18
  Option.match(thread.tools, {
19
19
  onSome: (value) => value.add(tool_),
package/L/events.ts CHANGED
@@ -2,10 +2,10 @@ import * as Effect from "effect/Effect"
2
2
  import * as Stream from "effect/Stream"
3
3
  import type { LEvent } from "../LEvent.ts"
4
4
  import type { Thread } from "../Thread.ts"
5
- import { Self } from "./Self.ts"
5
+ import { self } from "./self.ts"
6
6
 
7
7
  /** A stream of thread events. */
8
- export const events: Stream.Stream<LEvent, never, Thread> = Self.pipe(
8
+ export const events: Stream.Stream<LEvent, never, Thread> = self.pipe(
9
9
  Effect.map(({ events }) => Stream.fromPubSub(events)),
10
10
  Stream.unwrap,
11
11
  )
@@ -1,14 +1,12 @@
1
1
  import * as Effect from "effect/Effect"
2
2
  import type * as Schema from "effect/Schema"
3
- import type { Thread } from "../Thread.ts"
4
3
  import { encodeJsonc, type JsonValue } from "../util/JsonValue.ts"
5
- import { user } from "./user.ts"
6
4
 
7
5
  /** Stringify and append some JSON as a user message to the conversation. */
8
- export const userJson: <A, I extends JsonValue>(
6
+ export const json: <A, I extends JsonValue>(
9
7
  value: A,
10
8
  schema?: Schema.Schema<A, I>,
11
- ) => Effect.Effect<void, never, Thread> = Effect.fnUntraced(function*(value, schema) {
9
+ ) => Effect.Effect<string> = Effect.fnUntraced(function*(value, schema) {
12
10
  const encoded = schema ? encodeJsonc(schema)(value) : JSON.stringify(value, null, 2)
13
- return yield* user`\`\`\`json${schema ? "c" : ""}\n${encoded}\n\`\`\``
11
+ return `\`\`\`json${schema ? "c" : ""}\n${encoded}\n\`\`\``
14
12
  })
package/L/line.ts ADDED
@@ -0,0 +1,22 @@
1
+ import * as Effect from "effect/Effect"
2
+ import { prev } from "./prev.ts"
3
+
4
+ export interface line<RIn = never, E = never, ROut = never> {
5
+ <Arg extends Array<Effect.All.EffectAny>>(
6
+ ...steps: Arg
7
+ ): Effect.Effect<
8
+ Arg extends [] ? void
9
+ : Arg extends [...infer _0, infer L extends Effect.All.EffectAny] ? Effect.Effect.Success<L>
10
+ : never,
11
+ Effect.Effect.Error<Arg[number]> | E,
12
+ Exclude<Effect.Effect.Context<Arg[number]>, RIn> | ROut
13
+ >
14
+ }
15
+ export const line: line = Effect.fnUntraced(function*(...steps) {
16
+ let acc: unknown
17
+ while (steps.length) {
18
+ const current = steps.shift()!
19
+ acc = yield* (current.pipe(Effect.provideService(prev, acc)) as Effect.Effect<any>)
20
+ }
21
+ return acc as never
22
+ })
@@ -4,14 +4,14 @@ import * as Scope from "effect/Scope"
4
4
  import * as Stream from "effect/Stream"
5
5
  import type { LEvent } from "../LEvent.ts"
6
6
  import type { Thread } from "../Thread.ts"
7
- import { Self } from "./Self.ts"
7
+ import { self } from "./self.ts"
8
8
 
9
9
  /** Attach an event handler to process thread events. */
10
10
  export const listen: <A, E, R>(
11
11
  f: (event: LEvent) => Effect.Effect<A, E, R>,
12
12
  ) => Effect.Effect<RuntimeFiber<void, E>, never, Thread | R | Scope.Scope> = Effect.fnUntraced(function*(f) {
13
13
  const latch = yield* Effect.makeLatch(false)
14
- const { events } = yield* Self
14
+ const { events } = yield* self
15
15
  const dequeue = yield* events.subscribe
16
16
  const fiber = yield* latch.open.pipe(
17
17
  Effect.zipRight(
package/L/messages.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { Message } from "@effect/ai/AiInput"
2
2
  import * as Effect from "effect/Effect"
3
3
  import type { Thread } from "../Thread.ts"
4
- import { Self } from "./Self.ts"
4
+ import { self } from "./self.ts"
5
5
 
6
6
  /** Get a copy of the current list of messages. */
7
7
  export const messages: Effect.Effect<Array<Message>, never, Thread> = Effect.map(
8
- Self,
8
+ self,
9
9
  ({ state: { messages: [...messages] } }) => messages,
10
10
  )
package/L/prev.ts ADDED
@@ -0,0 +1,4 @@
1
+ import * as Context from "effect/Context"
2
+ import { prefix } from "../util/prefix.ts"
3
+
4
+ export class prev extends Context.Tag(prefix("prev"))<prev, unknown>() {}
package/L/provide.ts ADDED
@@ -0,0 +1,10 @@
1
+ import * as Effect from "effect/Effect"
2
+ import type { Thread } from "../Thread.ts"
3
+ import { self } from "./self.ts"
4
+
5
+ export const provide = <E, R>(
6
+ thread: Effect.Effect<Thread, E, R>,
7
+ ) =>
8
+ <A1, E1, R1>(
9
+ a: Effect.Effect<A1, E1, R1>,
10
+ ): Effect.Effect<A1, E | E1, R | Exclude<R1, Thread>> => Effect.provideServiceEffect(self, thread)(a)
@@ -2,4 +2,4 @@ import * as Context from "effect/Context"
2
2
  import type { Thread } from "../Thread.ts"
3
3
  import { prefix } from "../util/prefix.ts"
4
4
 
5
- export const Self: Context.Tag<Thread, Thread> = Context.GenericTag<Thread>(prefix("Self"))
5
+ export const self: Context.Tag<Thread, Thread> = Context.GenericTag<Thread>(prefix("Self"))
package/L/send.ts ADDED
@@ -0,0 +1,56 @@
1
+ import * as AiInput from "@effect/ai/AiInput"
2
+ import * as Array from "effect/Array"
3
+ import * as Effect from "effect/Effect"
4
+ import type { YieldWrap } from "effect/Utils"
5
+ import type { Envelope } from "../Envelope.ts"
6
+ import type { Thread } from "../Thread.ts"
7
+ import type { ExtractE, ExtractR } from "../util/extract.ts"
8
+ import { raw } from "../util/raw.ts"
9
+ import { normalize, type TaggableArg0, type TaggableArgRest } from "../util/Taggable.ts"
10
+ import { append } from "./append.ts"
11
+ import { self } from "./self.ts"
12
+
13
+ export const send = (envelope: Envelope) =>
14
+ Effect.fnUntraced(function*<
15
+ A0 extends TaggableArg0,
16
+ ARest extends TaggableArgRest<A0>,
17
+ >(
18
+ a0: A0,
19
+ ...aRest: ARest
20
+ ): Generator<
21
+ YieldWrap<
22
+ Effect.Effect<
23
+ void,
24
+ ExtractE<A0 | ARest[number]>,
25
+ ExtractR<A0 | ARest[number]> | Thread
26
+ >
27
+ >,
28
+ void,
29
+ never
30
+ > {
31
+ const text = yield* normalize(a0, ...aRest)
32
+ if (!text) return
33
+ const { to, cc, bcc } = envelope
34
+ const ccHeader = cc?.length
35
+ ? yield* raw`; ${Effect.all(cc).pipe(Effect.map(Array.join(", ")))}`
36
+ : ""
37
+ const headers = raw`[FROM: ${self}${ccHeader}]`
38
+ const knownRecipients = [...to ?? [], ...cc ?? []]
39
+ const program = append(
40
+ AiInput.UserMessage.make({
41
+ parts: [
42
+ AiInput.TextPart.make({
43
+ text: `${headers}\n${text}`,
44
+ }),
45
+ ],
46
+ }),
47
+ )
48
+ for (const recipient of knownRecipients) {
49
+ yield* program.pipe(Effect.provideService(self, recipient))
50
+ }
51
+ if (bcc?.length) {
52
+ for (const hiddenRecipient of bcc) {
53
+ yield* program.pipe(Effect.provideService(self, hiddenRecipient))
54
+ }
55
+ }
56
+ })
package/L/system.ts CHANGED
@@ -1,14 +1,25 @@
1
1
  import * as Effect from "effect/Effect"
2
2
  import * as Option from "effect/Option"
3
- import type { Thread } from "../Thread.ts"
4
- import type { Taggable } from "../util/Taggable.ts"
5
- import { raw } from "./raw.ts"
6
- import { Self } from "./Self.ts"
3
+ import type { YieldWrap } from "effect/Utils"
4
+ import { SystemSetEvent } from "../LEvent.ts"
5
+ import { normalize, type TaggableArg0, type TaggableArgRest } from "../util/Taggable.ts"
6
+ import { self } from "./self.ts"
7
7
 
8
8
  /** Set the thread's system instruction. */
9
- export const system: Taggable<Option.Option<string>, never, Thread> = Effect.fnUntraced(function*(a0, ...aRest) {
10
- const { state } = yield* Self
9
+ export const system = Effect.fnUntraced(function*<
10
+ A0 extends TaggableArg0,
11
+ ARest extends TaggableArgRest<A0>,
12
+ >(
13
+ a0: A0,
14
+ ...aRest: ARest
15
+ ): Generator<
16
+ YieldWrap<typeof self | ReturnType<typeof normalize<A0, ARest>> | Effect.Effect<boolean>>,
17
+ Option.Option<string>,
18
+ never
19
+ > {
20
+ const { state, events } = yield* self
11
21
  const { system } = state
12
- state.system = a0 ? Option.some(yield* raw(a0, ...aRest)) : Option.none()
22
+ state.system = a0 ? Option.some(yield* normalize(a0, ...aRest)) : Option.none()
23
+ yield* events.publish(new SystemSetEvent({ system: state.system }))
13
24
  return system
14
25
  })
package/L/thread.ts CHANGED
@@ -1,28 +1,16 @@
1
1
  import * as Effect from "effect/Effect"
2
- import { flow } from "effect/Function"
3
2
  import * as Option from "effect/Option"
4
3
  import * as PubSub from "effect/PubSub"
5
4
  import type { LEvent } from "../LEvent.ts"
6
- import { Thread, ThreadState } from "../Thread.ts"
7
- import type { Sequencer } from "../util/Sequencer.ts"
8
- import { Self } from "./Self.ts"
9
- import { sequence } from "./sequence.ts"
5
+ import { Thread, ThreadId, ThreadState } from "../Thread.ts"
6
+ import { self } from "./self.ts"
10
7
 
11
- export interface thread extends Sequencer<Thread>, Effect.Effect<Thread> {}
12
-
13
- const thread_ = Effect.gen(function*() {
8
+ export const thread: Effect.Effect<Thread> = Effect.gen(function*() {
14
9
  return Thread({
15
- parent: yield* Effect.serviceOption(Self),
10
+ id: ThreadId.make(crypto.randomUUID()),
11
+ parent: yield* Effect.serviceOption(self),
16
12
  events: yield* PubSub.unbounded<LEvent>(),
17
13
  state: ThreadState.default(),
18
14
  tools: Option.none(),
19
15
  })
20
16
  })
21
-
22
- export const thread: thread = Object.assign(
23
- flow(
24
- sequence,
25
- Effect.provideServiceEffect(Self, thread_),
26
- ),
27
- thread_,
28
- )
package/L/toolkit.ts CHANGED
@@ -2,9 +2,9 @@ import * as AiToolkit from "@effect/ai/AiToolkit"
2
2
  import * as Effect from "effect/Effect"
3
3
  import * as Option from "effect/Option"
4
4
  import type { NeverTool } from "../util/NeverTool.ts"
5
- import { Self } from "./Self.ts"
5
+ import { self } from "./self.ts"
6
6
 
7
- export const toolkit = Self.pipe(
7
+ export const toolkit = self.pipe(
8
8
  Effect.flatMap(({ tools }) =>
9
9
  Option.match(tools, {
10
10
  onSome: (tools) => AiToolkit.make(...tools),
package/L/user.test.ts CHANGED
@@ -2,6 +2,7 @@ import { TextPart, UserMessage } from "@effect/ai/AiInput"
2
2
  import { expect, it } from "@effect/vitest"
3
3
  import * as Effect from "effect/Effect"
4
4
  import { messages } from "./messages.ts"
5
+ import { provide } from "./provide.ts"
5
6
  import { thread } from "./thread.ts"
6
7
  import { user } from "./user.ts"
7
8
 
@@ -26,5 +27,7 @@ it.effect("test success", () =>
26
27
  }),
27
28
  ])
28
29
  }).pipe(
29
- thread,
30
+ provide(
31
+ thread,
32
+ ),
30
33
  ))
package/L/user.ts CHANGED
@@ -1,13 +1,23 @@
1
1
  import { TextPart, UserMessage } from "@effect/ai/AiInput"
2
2
  import * as Effect from "effect/Effect"
3
3
  import type { Thread } from "../Thread.ts"
4
- import type { Taggable } from "../util/Taggable.ts"
4
+ import type { ExtractE, ExtractR } from "../util/extract.ts"
5
+ import { normalize, type TaggableArg0, type TaggableArgRest } from "../util/Taggable.ts"
5
6
  import { append } from "./append.ts"
6
- import { raw } from "./raw.ts"
7
7
 
8
- /** Append a user message to the conversation. */
9
- export const user: Taggable<void, never, Thread> = (a0, ...aRest) =>
10
- raw(a0, ...aRest).pipe(
8
+ /** Append a user message to the thread. */
9
+ export const user = <
10
+ A0 extends TaggableArg0,
11
+ ARest extends TaggableArgRest<A0>,
12
+ >(
13
+ a0: A0,
14
+ ...aRest: ARest
15
+ ): Effect.Effect<
16
+ void,
17
+ ExtractE<A0 | ARest[number]>,
18
+ ExtractR<A0 | ARest[number]> | Thread
19
+ > =>
20
+ normalize(a0, ...aRest).pipe(
11
21
  Effect.flatMap((text) =>
12
22
  text
13
23
  ? append(
package/LEvent.ts CHANGED
@@ -1,23 +1,33 @@
1
1
  import { Message } from "@effect/ai/AiInput"
2
2
  import * as Schema from "effect/Schema"
3
3
 
4
- export class Messages extends Schema.Array(Message) {}
5
-
6
- /** An event in which one or more messages were added to the thread. */
7
- export class MessagesAppended extends Schema.TaggedClass<MessagesAppended>("MessagesAppended")("MessagesAppended", {
8
- messages: Messages,
4
+ /** Event in which the thread's system is set to a new string. */
5
+ export class SystemSetEvent extends Schema.TaggedClass<SystemSetEvent>("SystemSetEvent")("system_set", {
6
+ system: Schema.Option(Schema.String),
9
7
  }) {}
10
8
 
11
- /** An event in which the thread is cleared of messages. */
12
- export class MessagesCleared extends Schema.TaggedClass<MessagesCleared>("MessagesCleared")("MessagesCleared", {
13
- cleared: Messages,
14
- }) {}
9
+ /** Event in which one or more messages were added to the thread. */
10
+ export class MessagesAppendedEvent
11
+ extends Schema.TaggedClass<MessagesAppendedEvent>("MessagesAppendedEvent")("messages_appended", {
12
+ messages: Schema.Array(Message),
13
+ })
14
+ {}
15
+
16
+ /** Event in which the thread is cleared of messages. */
17
+ export class ThreadClearedEvent
18
+ extends Schema.TaggedClass<ThreadClearedEvent>("MessagesClearedEvent")("messages_cleared", {})
19
+ {}
15
20
 
16
21
  export type LEvent = typeof LEvent["Type"]
17
22
  export const LEvent: Schema.Union<[
18
- typeof MessagesAppended,
19
- typeof MessagesCleared,
23
+ typeof SystemSetEvent,
24
+ typeof MessagesAppendedEvent,
25
+ typeof ThreadClearedEvent,
20
26
  ]> = Schema.Union(
21
- MessagesAppended,
22
- MessagesCleared,
27
+ SystemSetEvent,
28
+ MessagesAppendedEvent,
29
+ ThreadClearedEvent,
23
30
  )
31
+
32
+ export const encodeLEvent = Schema.encode(LEvent)
33
+ export const decodeLEvent = Schema.decode(LEvent)