@effect/ai 0.10.2 → 0.10.3

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.
@@ -0,0 +1,163 @@
1
+ import * as Cause from "effect/Cause"
2
+ import * as Context from "effect/Context"
3
+ import * as Effect from "effect/Effect"
4
+ import { CommitPrototype } from "effect/Effectable"
5
+ import * as Exit from "effect/Exit"
6
+ import { dual, identity } from "effect/Function"
7
+ import * as Option from "effect/Option"
8
+ import { pipeArguments } from "effect/Pipeable"
9
+ import * as Predicate from "effect/Predicate"
10
+ import * as Schedule from "effect/Schedule"
11
+ import * as Scope from "effect/Scope"
12
+ import type * as AiModel from "../AiModel.js"
13
+ import { AiModels } from "../AiModels.js"
14
+ import type * as AiPlan from "../AiPlan.js"
15
+
16
+ /** @internal */
17
+ export const TypeId: AiPlan.TypeId = Symbol.for("@effect/ai/AiPlan") as AiPlan.TypeId
18
+
19
+ /** @internal */
20
+ export const PlanPrototype = {
21
+ ...CommitPrototype,
22
+ [TypeId]: TypeId,
23
+ commit(this: AiPlan.AiPlan<any, any, any>) {
24
+ return buildPlan(this)
25
+ },
26
+ pipe() {
27
+ return pipeArguments(this, arguments)
28
+ }
29
+ } as any
30
+
31
+ const makePlan = <
32
+ Steps extends ReadonlyArray<AiPlan.AiPlan.Step<any, any, any>>
33
+ >(steps: Steps): AiPlan.AiPlan<any, any, any> => {
34
+ const self = Object.create(PlanPrototype)
35
+ self.steps = steps
36
+ return self
37
+ }
38
+
39
+ const buildPlan = <Error, Provides, Requires>(
40
+ plan: AiPlan.AiPlan<Error, Provides, Requires>
41
+ ): Effect.Effect<
42
+ <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Provides>>,
43
+ never,
44
+ Requires | AiModels
45
+ > =>
46
+ Effect.map(Effect.context<AiModels | Requires>(), (context) => {
47
+ const models = Context.get(context, AiModels)
48
+ return Effect.fnUntraced(function*<A, E, R>(effect: Effect.Effect<A, E, R>) {
49
+ let exit: Exit.Exit<A, E> | undefined = undefined
50
+ for (const step of plan.steps) {
51
+ if (exit !== undefined && Exit.isFailure(exit) && Option.isSome(step.check)) {
52
+ const check = step.check.value(Cause.squash(exit.cause) as Error)
53
+ const isFatalError = !(Effect.isEffect(check) ? yield* check : check)
54
+ if (isFatalError) break
55
+ }
56
+ const retryOptions = getRetryOptions(step)
57
+ exit = yield* Effect.scopedWith((scope) =>
58
+ models.build(step.model, context).pipe(
59
+ Scope.extend(scope),
60
+ Effect.flatMap((context) =>
61
+ effect.pipe(
62
+ Option.isSome(retryOptions)
63
+ ? Effect.retry(retryOptions.value)
64
+ : identity,
65
+ Effect.provide(context)
66
+ )
67
+ ),
68
+ Effect.exit
69
+ )
70
+ )
71
+ if (Exit.isSuccess(exit)) break
72
+ }
73
+ return yield* exit!
74
+ })
75
+ })
76
+
77
+ const getRetryOptions = <Error, Provides, Requires>(
78
+ step: AiPlan.AiPlan.Step<Error, Provides, Requires>
79
+ ): Option.Option<Effect.Retry.Options<any>> => {
80
+ if (Option.isNone(step.schedule) && Option.isNone(step.check)) {
81
+ return Option.none()
82
+ }
83
+ return Option.some({
84
+ schedule: Option.getOrUndefined(step.schedule),
85
+ while: Option.getOrUndefined(step.check)
86
+ })
87
+ }
88
+
89
+ /** @internal */
90
+ export const fromModel = <Provides, Requires, EW, Out, ES, RW = never, RS = never>(
91
+ model: AiModel.AiModel<Provides, Requires>,
92
+ options?: {
93
+ readonly attempts?: number | undefined
94
+ readonly while?: ((error: EW) => boolean | Effect.Effect<boolean, never, RW>) | undefined
95
+ readonly schedule?: Schedule.Schedule<Out, ES, RS> | undefined
96
+ }
97
+ ): AiPlan.AiPlan<EW & ES, Provides, RW | RS | Requires> =>
98
+ makePlan([{
99
+ model,
100
+ check: Option.fromNullable(options?.while) as any,
101
+ schedule: resolveSchedule(options ?? {})
102
+ }])
103
+
104
+ /** @internal */
105
+ export const withFallback = dual(2, <
106
+ E,
107
+ Provides,
108
+ Requires,
109
+ Provides2 extends Provides,
110
+ Requires2,
111
+ Out,
112
+ EW,
113
+ ES,
114
+ RW = never,
115
+ RS = never
116
+ >(
117
+ self: AiPlan.AiPlan<E, Provides, Requires>,
118
+ options: {
119
+ readonly model: AiModel.AiModel<Provides2, Requires2>
120
+ readonly attempts?: number | undefined
121
+ readonly while?: ((error: EW) => boolean | Effect.Effect<boolean, never, RW>) | undefined
122
+ readonly schedule?: Schedule.Schedule<Out, ES, RS> | undefined
123
+ }
124
+ ): AiPlan.AiPlan<E & EW & ES, Provides & Provides2, Requires | Requires2 | RW | RS> =>
125
+ makePlan([
126
+ ...self.steps,
127
+ {
128
+ model: options.model,
129
+ check: Option.fromNullable(options.while) as any,
130
+ schedule: resolveSchedule(options)
131
+ }
132
+ ]))
133
+
134
+ const resolveSchedule = <E, R, Out, R2>(options: {
135
+ readonly attempts?: number | undefined
136
+ readonly schedule?: Schedule.Schedule<Out, E, R2> | undefined
137
+ }): Option.Option<Schedule.Schedule<Out, E, R | R2>> => {
138
+ if (
139
+ Predicate.isUndefined(options.attempts) &&
140
+ Predicate.isUndefined(options.schedule)
141
+ ) return Option.none()
142
+ let schedule = (options.schedule ?? Schedule.forever) as Schedule.Schedule<any, E, R | R2>
143
+ if (Predicate.isNotUndefined(options.attempts)) {
144
+ // In an `AiPlan`, the `attempts` represents the total number of times to
145
+ // attempt the call, not the number of retries, thus we subtract one from
146
+ // the total number of recurrences
147
+ schedule = Schedule.intersect(schedule, Schedule.recurs(options.attempts - 1))
148
+ }
149
+ return Option.some(schedule)
150
+ }
151
+
152
+ /** @internal */
153
+ export const concatSteps = dual<
154
+ <Error2, Provides2, Requires2>(
155
+ other: AiPlan.AiPlan<Error2, Provides2, Requires2>
156
+ ) => <Error, Provides, Requires>(
157
+ self: AiPlan.AiPlan<Error, Provides, Requires>
158
+ ) => AiPlan.AiPlan<Error & Error2, Provides & Provides2, Requires | Requires2>,
159
+ <Error, Provides, Requires, Error2, Provides2, Requires2>(
160
+ self: AiPlan.AiPlan<Error, Provides, Requires>,
161
+ other: AiPlan.AiPlan<Error2, Provides2, Requires2>
162
+ ) => AiPlan.AiPlan<Error & Error2, Provides & Provides2, Requires | Requires2>
163
+ >(2, (self, other) => makePlan([...self.steps, ...other.steps]))