effect-assistant 0.1.0 → 0.3.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.
- package/README.md +1 -1
- package/dist/Assistant.d.ts +74 -28
- package/dist/Assistant.d.ts.map +1 -1
- package/dist/Assistant.js +11 -4
- package/dist/Assistant.js.map +1 -1
- package/dist/AssistantActivity.d.ts +10 -16
- package/dist/AssistantActivity.d.ts.map +1 -1
- package/dist/AssistantActivity.js +11 -4
- package/dist/AssistantActivity.js.map +1 -1
- package/dist/AssistantEngine.d.ts +39 -1
- package/dist/AssistantEngine.d.ts.map +1 -1
- package/dist/AssistantEngine.js +81 -28
- package/dist/AssistantEngine.js.map +1 -1
- package/dist/AssistantEvent.d.ts +52 -70
- package/dist/AssistantEvent.d.ts.map +1 -1
- package/dist/AssistantEvent.js +28 -57
- package/dist/AssistantEvent.js.map +1 -1
- package/dist/AssistantLoop.d.ts +7 -5
- package/dist/AssistantLoop.d.ts.map +1 -1
- package/dist/AssistantLoop.js +6 -3
- package/dist/AssistantLoop.js.map +1 -1
- package/dist/Block.js.map +1 -1
- package/dist/BlockKit.d.ts +14 -18
- package/dist/BlockKit.d.ts.map +1 -1
- package/dist/BlockKit.js +4 -7
- package/dist/BlockKit.js.map +1 -1
- package/dist/ChatLog.js +1 -1
- package/dist/Compaction.d.ts +8 -4
- package/dist/Compaction.d.ts.map +1 -1
- package/dist/Compaction.js +12 -15
- package/dist/Compaction.js.map +1 -1
- package/dist/CurrentChat.d.ts +2 -1
- package/dist/CurrentChat.d.ts.map +1 -1
- package/dist/CurrentChat.js.map +1 -1
- package/dist/Instructions.js +1 -1
- package/dist/Skill.d.ts +33 -0
- package/dist/Skill.d.ts.map +1 -1
- package/dist/Skill.js +27 -1
- package/dist/Skill.js.map +1 -1
- package/dist/Specialist.d.ts +4 -1
- package/dist/Specialist.d.ts.map +1 -1
- package/dist/Specialist.js.map +1 -1
- package/dist/Timeline.d.ts +482 -0
- package/dist/Timeline.d.ts.map +1 -0
- package/dist/Timeline.js +572 -0
- package/dist/Timeline.js.map +1 -0
- package/dist/ToolDiscovery.d.ts +4 -5
- package/dist/ToolDiscovery.d.ts.map +1 -1
- package/dist/ToolDiscovery.js +5 -4
- package/dist/ToolDiscovery.js.map +1 -1
- package/dist/ToolFailure.d.ts +1 -1
- package/dist/ToolFailure.js +1 -1
- package/dist/ToolFailure.js.map +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.js +4 -7
- package/dist/internal/chat.js +11 -37
- package/dist/internal/chat.js.map +1 -1
- package/dist/internal/events.d.ts +15 -16
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +29 -28
- package/dist/internal/events.js.map +1 -1
- package/dist/internal/history.js +26 -58
- package/dist/internal/history.js.map +1 -1
- package/dist/internal/input.js +7 -0
- package/dist/internal/input.js.map +1 -0
- package/dist/internal/loop.d.ts +2 -2
- package/dist/internal/loop.d.ts.map +1 -1
- package/dist/internal/loop.js +21 -56
- package/dist/internal/loop.js.map +1 -1
- package/dist/internal/skills.js +6 -1
- package/dist/internal/skills.js.map +1 -1
- package/dist/internal/specialists.js +7 -7
- package/dist/internal/specialists.js.map +1 -1
- package/dist/internal/toolkit.d.ts +6 -20
- package/dist/internal/toolkit.d.ts.map +1 -1
- package/dist/internal/toolkit.js +10 -31
- package/dist/internal/toolkit.js.map +1 -1
- package/dist/internal/tools.js +2 -21
- package/dist/internal/tools.js.map +1 -1
- package/dist/internal/turns.d.ts.map +1 -1
- package/dist/internal/turns.js +24 -43
- package/dist/internal/turns.js.map +1 -1
- package/package.json +3 -2
- package/src/Assistant.ts +75 -29
- package/src/AssistantActivity.ts +11 -18
- package/src/AssistantEngine.ts +110 -43
- package/src/AssistantEvent.ts +46 -74
- package/src/AssistantLoop.ts +7 -5
- package/src/Block.ts +1 -1
- package/src/BlockKit.ts +18 -31
- package/src/Compaction.ts +8 -21
- package/src/CurrentChat.ts +2 -1
- package/src/Skill.ts +34 -1
- package/src/Specialist.ts +4 -1
- package/src/Timeline.ts +861 -0
- package/src/ToolDiscovery.ts +5 -13
- package/src/ToolFailure.ts +1 -1
- package/src/index.ts +5 -20
- package/src/internal/chat.ts +8 -57
- package/src/internal/events.ts +43 -43
- package/src/internal/history.ts +36 -101
- package/src/internal/input.ts +3 -0
- package/src/internal/loop.ts +31 -101
- package/src/internal/skills.ts +11 -0
- package/src/internal/specialists.ts +6 -9
- package/src/internal/toolkit.ts +17 -60
- package/src/internal/tools.ts +2 -53
- package/src/internal/turns.ts +37 -79
- package/dist/AppEvent.d.ts +0 -111
- package/dist/AppEvent.d.ts.map +0 -1
- package/dist/AppEvent.js +0 -71
- package/dist/AppEvent.js.map +0 -1
- package/dist/ChatView.d.ts +0 -459
- package/dist/ChatView.d.ts.map +0 -1
- package/dist/ChatView.js +0 -496
- package/dist/ChatView.js.map +0 -1
- package/dist/Decision.d.ts +0 -145
- package/dist/Decision.d.ts.map +0 -1
- package/dist/Decision.js +0 -178
- package/dist/Decision.js.map +0 -1
- package/dist/DecisionId.d.ts +0 -37
- package/dist/DecisionId.d.ts.map +0 -1
- package/dist/DecisionId.js +0 -44
- package/dist/DecisionId.js.map +0 -1
- package/dist/internal/decisions.js +0 -66
- package/dist/internal/decisions.js.map +0 -1
- package/src/AppEvent.ts +0 -134
- package/src/ChatView.ts +0 -658
- package/src/Decision.ts +0 -241
- package/src/DecisionId.ts +0 -46
- package/src/internal/decisions.ts +0 -131
package/src/Decision.ts
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lets the user approve or decline a tool call the assistant can't make on its own, such as
|
|
3
|
-
* placing an order or publishing a post.
|
|
4
|
-
*
|
|
5
|
-
* When the model calls a tool marked with Effect's `needsApproval`, the call is parked instead of
|
|
6
|
-
* run. The model asks the user with the decision {@link block}, and the user's answer to that
|
|
7
|
-
* block runs the call or declines it. {@link fromEntries} computes where every decision in a
|
|
8
|
-
* chat's log stands.
|
|
9
|
-
*
|
|
10
|
-
* @since 0.1.0
|
|
11
|
-
*/
|
|
12
|
-
import * as Option from "effect/Option"
|
|
13
|
-
import * as Predicate from "effect/Predicate"
|
|
14
|
-
import * as Schema from "effect/Schema"
|
|
15
|
-
import * as AssistantEvent from "./AssistantEvent.ts"
|
|
16
|
-
import * as Block from "./Block.ts"
|
|
17
|
-
import * as DecisionId from "./DecisionId.ts"
|
|
18
|
-
import type { InputId } from "./InputId.ts"
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The kind of the decision block.
|
|
22
|
-
*
|
|
23
|
-
* @category utils
|
|
24
|
-
* @since 0.1.0
|
|
25
|
-
*/
|
|
26
|
-
export const kind = "decision"
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Schema for the decision block's props, which list the decisions it asks for and, optionally,
|
|
30
|
-
* its buttons' labels.
|
|
31
|
-
*
|
|
32
|
-
* @category schemas
|
|
33
|
-
* @since 0.1.0
|
|
34
|
-
*/
|
|
35
|
-
export const Props = Schema.Struct({
|
|
36
|
-
// Plain strings: a strict tool schema with the id's pattern is too much for some providers; the
|
|
37
|
-
// ids are checked against the decisions waiting when the message is sent.
|
|
38
|
-
decisionIds: Schema.Array(Schema.String).check(Schema.isMinLength(1)).annotate({
|
|
39
|
-
description:
|
|
40
|
-
"The decisions to ask for: each waiting call's decisionId. Several go in one block when the user decides them together."
|
|
41
|
-
}),
|
|
42
|
-
approveLabel: Schema.optional(Schema.String).annotate({
|
|
43
|
-
description: 'The approve button, like "Place order".'
|
|
44
|
-
}),
|
|
45
|
-
declineLabel: Schema.optional(Schema.String).annotate({
|
|
46
|
-
description: 'The decline button, like "Cancel".'
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Represents the decision block's props.
|
|
52
|
-
*
|
|
53
|
-
* @category models
|
|
54
|
-
* @since 0.1.0
|
|
55
|
-
*/
|
|
56
|
-
export type Props = typeof Props.Type
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Schema for the decision block's answer, which holds an outcome for every decision the block
|
|
60
|
-
* asks for, by id.
|
|
61
|
-
*
|
|
62
|
-
* @category schemas
|
|
63
|
-
* @since 0.1.0
|
|
64
|
-
*/
|
|
65
|
-
export const Answer = Schema.Struct({
|
|
66
|
-
decisions: Schema.Record(Schema.String, AssistantEvent.DecisionOutcome)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Represents the decision block's answer.
|
|
71
|
-
*
|
|
72
|
-
* @category models
|
|
73
|
-
* @since 0.1.0
|
|
74
|
-
*/
|
|
75
|
-
export type Answer = typeof Answer.Type
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Block with approve and decline buttons for the decisions waiting for the user.
|
|
79
|
-
*
|
|
80
|
-
* **Details**
|
|
81
|
-
*
|
|
82
|
-
* Put it in the assistant's `BlockKit`. An assistant with a tool that needs a decision must have
|
|
83
|
-
* it. The client renders it like any other block, with the calls from {@link fromEntries} (their
|
|
84
|
-
* tool and params).
|
|
85
|
-
*
|
|
86
|
-
* **Example** (Asking the user before placing an order)
|
|
87
|
-
*
|
|
88
|
-
* ```ts
|
|
89
|
-
* import { Assistant, BlockKit, Decision } from "effect-assistant"
|
|
90
|
-
* import * as Tool from "effect/ai/Tool"
|
|
91
|
-
* import * as Toolkit from "effect/ai/Toolkit"
|
|
92
|
-
* import * as Effect from "effect/Effect"
|
|
93
|
-
* import * as Layer from "effect/Layer"
|
|
94
|
-
* import * as Schema from "effect/Schema"
|
|
95
|
-
*
|
|
96
|
-
* export const Shop = Assistant.make("shop", { blocks: BlockKit.make([Decision.block]) })
|
|
97
|
-
*
|
|
98
|
-
* const OrderTools = Toolkit.make(
|
|
99
|
-
* Tool.make("place_order", {
|
|
100
|
-
* description: "Places an order.",
|
|
101
|
-
* parameters: Schema.Struct({ sku: Schema.String, quantity: Schema.Int }),
|
|
102
|
-
* success: Schema.Struct({ reference: Schema.String }),
|
|
103
|
-
* needsApproval: true
|
|
104
|
-
* })
|
|
105
|
-
* )
|
|
106
|
-
*
|
|
107
|
-
* const OrderToolsLive = OrderTools.toLayer({
|
|
108
|
-
* place_order: () => Effect.succeed({ reference: "R-1042" })
|
|
109
|
-
* })
|
|
110
|
-
*
|
|
111
|
-
* // The call waits until the user answers the decision block.
|
|
112
|
-
* const ShopLive = Shop.toLayer((turn) =>
|
|
113
|
-
* turn.loop({ instructions: "You place orders for the team.", toolkit: OrderTools })
|
|
114
|
-
* ).pipe(Layer.provide(OrderToolsLive))
|
|
115
|
-
* ```
|
|
116
|
-
*
|
|
117
|
-
* @category constructors
|
|
118
|
-
* @since 0.1.0
|
|
119
|
-
*/
|
|
120
|
-
export const block = Block.make(kind, {
|
|
121
|
-
description:
|
|
122
|
-
"Approve / decline buttons for decisions waiting for the user: a call of yours that waits for their decision. The only way such a decision is made: write what they decide on (the call's key facts), and put the decision ids in decisionIds.",
|
|
123
|
-
props: Props,
|
|
124
|
-
answer: Answer
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Creates an answer that gives every decision the block asks for the same outcome.
|
|
129
|
-
*
|
|
130
|
-
* This is what an "Approve" or "Decline" button sends.
|
|
131
|
-
*
|
|
132
|
-
* @category constructors
|
|
133
|
-
* @since 0.1.0
|
|
134
|
-
*/
|
|
135
|
-
export const answerAll = (props: Props, outcome: AssistantEvent.DecisionOutcome): Answer => ({
|
|
136
|
-
decisions: Object.fromEntries(props.decisionIds.map((id) => [id, outcome]))
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Represents the state of a decision.
|
|
141
|
-
*
|
|
142
|
-
* @category models
|
|
143
|
-
* @since 0.1.0
|
|
144
|
-
*/
|
|
145
|
-
export interface State {
|
|
146
|
-
readonly request: AssistantEvent.DecisionRequested
|
|
147
|
-
/** How the user decided, or `undefined` while the decision waits. */
|
|
148
|
-
readonly outcome: AssistantEvent.DecisionOutcome | undefined
|
|
149
|
-
/** The id of the answer that decided it. */
|
|
150
|
-
readonly answer: InputId | undefined
|
|
151
|
-
/** Whether its `DecisionResolved` is stored. A workflow's decision has been passed on by then. */
|
|
152
|
-
readonly resolved: boolean
|
|
153
|
-
/** The approved call's result, once the call has run. */
|
|
154
|
-
readonly result: AssistantEvent.ToolResult | undefined
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const decodeAnswer = Schema.decodeUnknownOption(Answer)
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Returns the decision states after one more event.
|
|
161
|
-
*
|
|
162
|
-
* @category utils
|
|
163
|
-
* @since 0.1.0
|
|
164
|
-
*/
|
|
165
|
-
export const apply = (
|
|
166
|
-
states: ReadonlyMap<string, State>,
|
|
167
|
-
event: { readonly _tag: string }
|
|
168
|
-
): ReadonlyMap<string, State> => {
|
|
169
|
-
if (!AssistantEvent.isLibraryEvent(event)) return states
|
|
170
|
-
|
|
171
|
-
const update = (id: string, f: (state: State) => State) => {
|
|
172
|
-
const state = states.get(id)
|
|
173
|
-
|
|
174
|
-
return state === undefined ? states : new Map(states).set(id, f(state))
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (Predicate.isTagged(event, "DecisionRequested")) {
|
|
178
|
-
return states.has(event.id)
|
|
179
|
-
? states
|
|
180
|
-
: new Map(states).set(event.id, {
|
|
181
|
-
request: event,
|
|
182
|
-
outcome: undefined,
|
|
183
|
-
answer: undefined,
|
|
184
|
-
resolved: false,
|
|
185
|
-
result: undefined
|
|
186
|
-
})
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (Predicate.isTagged(event, "DecisionResolved")) {
|
|
190
|
-
return update(event.id, (state) => ({
|
|
191
|
-
...state,
|
|
192
|
-
outcome: event.outcome,
|
|
193
|
-
answer: event.answer,
|
|
194
|
-
resolved: true
|
|
195
|
-
}))
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// A stored decision answer was checked against its block when it was submitted: the first one
|
|
199
|
-
// for a decision decides it.
|
|
200
|
-
if (Predicate.isTagged(event, "BlockAnswer") && event.kind === kind) {
|
|
201
|
-
return Option.match(decodeAnswer(event.value), {
|
|
202
|
-
onNone: () => states,
|
|
203
|
-
onSome: ({ decisions }) => {
|
|
204
|
-
const next = new Map(states)
|
|
205
|
-
|
|
206
|
-
for (const [id, outcome] of Object.entries(decisions)) {
|
|
207
|
-
const state = states.get(id)
|
|
208
|
-
|
|
209
|
-
if (state !== undefined && state.outcome === undefined) next.set(id, { ...state, outcome, answer: event.id })
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return next
|
|
213
|
-
}
|
|
214
|
-
})
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (Predicate.isTagged(event, "ToolResult")) {
|
|
218
|
-
const id = DecisionId.fromCallId(event.callId)
|
|
219
|
-
const state = states.get(id)
|
|
220
|
-
|
|
221
|
-
return state !== undefined && Predicate.isTagged(state.request.origin, "Call") && state.outcome === "approved"
|
|
222
|
-
? update(id, (current) => ({ ...current, result: event }))
|
|
223
|
-
: states
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return states
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Returns every decision in the log and where it stands, by id.
|
|
231
|
-
*
|
|
232
|
-
* @category utils
|
|
233
|
-
* @since 0.1.0
|
|
234
|
-
*/
|
|
235
|
-
export const fromEntries = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlyMap<string, State> => {
|
|
236
|
-
let all: ReadonlyMap<string, State> = new Map()
|
|
237
|
-
|
|
238
|
-
for (const entry of entries) all = apply(all, entry.event)
|
|
239
|
-
|
|
240
|
-
return all
|
|
241
|
-
}
|
package/src/DecisionId.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module provides the id of a decision the user is asked to make, which is `decision:`
|
|
3
|
-
* followed by a UUID.
|
|
4
|
-
*
|
|
5
|
-
* The model passes the id to a decision block, and the user's answer refers to it.
|
|
6
|
-
*
|
|
7
|
-
* @since 0.1.0
|
|
8
|
-
*/
|
|
9
|
-
import * as Schema from "effect/Schema"
|
|
10
|
-
import * as Uuid from "./internal/uuid.ts"
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Schema for decision ids of the form `` `decision:${uuid}` ``, branded.
|
|
14
|
-
*
|
|
15
|
-
* @category schemas
|
|
16
|
-
* @since 0.1.0
|
|
17
|
-
*/
|
|
18
|
-
export const DecisionId = Schema.TemplateLiteral(["decision:", Uuid.Uuid]).pipe(
|
|
19
|
-
Schema.brand("~effect-assistant/DecisionId")
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Represents a decision id.
|
|
24
|
-
*
|
|
25
|
-
* @category models
|
|
26
|
-
* @since 0.1.0
|
|
27
|
-
*/
|
|
28
|
-
export type DecisionId = typeof DecisionId.Type
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Creates the decision id for a key. The same key always gives the same id, so a decision that is
|
|
32
|
-
* requested again is stored only once.
|
|
33
|
-
*
|
|
34
|
-
* @category constructors
|
|
35
|
-
* @since 0.1.0
|
|
36
|
-
*/
|
|
37
|
-
export const fromKey = (key: string): DecisionId => DecisionId.make(`decision:${Uuid.fromKey(key)}`)
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Returns the decision id for a tool call that waits for the user's decision, derived from the
|
|
41
|
-
* call's id.
|
|
42
|
-
*
|
|
43
|
-
* @category constructors
|
|
44
|
-
* @since 0.1.0
|
|
45
|
-
*/
|
|
46
|
-
export const fromCallId = (callId: string): DecisionId => fromKey(`tool-call:${callId}`)
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import * as Response from "effect/ai/Response"
|
|
2
|
-
import * as Context from "effect/Context"
|
|
3
|
-
import * as Data from "effect/Data"
|
|
4
|
-
import type * as Effect from "effect/Effect"
|
|
5
|
-
import * as Option from "effect/Option"
|
|
6
|
-
import * as Predicate from "effect/Predicate"
|
|
7
|
-
import * as Schema from "effect/Schema"
|
|
8
|
-
import * as AssistantEvent from "../AssistantEvent.ts"
|
|
9
|
-
import * as BlockKit from "../BlockKit.ts"
|
|
10
|
-
import * as Decision from "../Decision.ts"
|
|
11
|
-
import type { InputId } from "../InputId.ts"
|
|
12
|
-
|
|
13
|
-
// Only a turn's loop parks calls and runs approved ones. A turn's specialists inherit its context, so their runs set
|
|
14
|
-
// it back to none.
|
|
15
|
-
/** @internal */
|
|
16
|
-
export const TurnDecisions = Context.Reference<
|
|
17
|
-
Option.Option<{
|
|
18
|
-
readonly approved: Effect.Effect<ReadonlyArray<AssistantEvent.ToolCallPart>>
|
|
19
|
-
}>
|
|
20
|
-
>("effect-assistant/TurnDecisions", { defaultValue: Option.none })
|
|
21
|
-
|
|
22
|
-
/** @internal */
|
|
23
|
-
export interface Decided {
|
|
24
|
-
readonly request: AssistantEvent.DecisionRequested
|
|
25
|
-
readonly outcome: AssistantEvent.DecisionOutcome
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** @internal */
|
|
29
|
-
export type Verdict = Data.TaggedEnum<{
|
|
30
|
-
Store: Record<never, never>
|
|
31
|
-
Refuse: { readonly reason: string }
|
|
32
|
-
Decide: { readonly decided: ReadonlyArray<Decided>; readonly answer: InputId }
|
|
33
|
-
}>
|
|
34
|
-
|
|
35
|
-
const Verdicts = Data.taggedEnum<Verdict>()
|
|
36
|
-
|
|
37
|
-
const store = Verdicts.Store()
|
|
38
|
-
|
|
39
|
-
const refuse = (reason: string) => Verdicts.Refuse({ reason })
|
|
40
|
-
|
|
41
|
-
const decodeProps = Schema.decodeUnknownOption(Decision.Props)
|
|
42
|
-
|
|
43
|
-
const decodeAnswer = Schema.decodeUnknownOption(Decision.Answer)
|
|
44
|
-
|
|
45
|
-
const messageOf = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, id: string) =>
|
|
46
|
-
entries
|
|
47
|
-
.map(({ event }) => event)
|
|
48
|
-
.find(
|
|
49
|
-
(event): event is AssistantEvent.AssistantMessage =>
|
|
50
|
-
AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, "AssistantMessage") && event.id === id
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
const answerVerdict = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, answer: AssistantEvent.BlockAnswer) => {
|
|
54
|
-
const message = messageOf(entries, answer.messageId)
|
|
55
|
-
const onDecisionBlock = message?.block?.kind === Decision.kind
|
|
56
|
-
|
|
57
|
-
if (!onDecisionBlock && answer.kind !== Decision.kind) return store
|
|
58
|
-
|
|
59
|
-
if (message?.block === undefined || !onDecisionBlock || answer.kind !== Decision.kind) {
|
|
60
|
-
return refuse(`${answer.messageId} has no decision block.`)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (BlockKit.fromEntries(entries).get(message.id)?.open !== true)
|
|
64
|
-
return refuse(`The block on ${message.id} is closed.`)
|
|
65
|
-
|
|
66
|
-
const props = decodeProps(message.block.props)
|
|
67
|
-
const value = decodeAnswer(answer.value)
|
|
68
|
-
|
|
69
|
-
if (Option.isNone(props) || Option.isNone(value)) return refuse(`That isn't an answer to the block on ${message.id}.`)
|
|
70
|
-
|
|
71
|
-
const asked = props.value.decisionIds
|
|
72
|
-
const given = Object.keys(value.value.decisions)
|
|
73
|
-
|
|
74
|
-
if (given.length !== asked.length || !given.every((id) => asked.some((askedId) => askedId === id))) {
|
|
75
|
-
return refuse(`An answer decides exactly the decisions its block asks for: ${asked.join(", ")}.`)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const states = Decision.fromEntries(entries)
|
|
79
|
-
const decided: Array<Decided> = []
|
|
80
|
-
|
|
81
|
-
for (const id of asked) {
|
|
82
|
-
const state = states.get(id)
|
|
83
|
-
const outcome = value.value.decisions[id]
|
|
84
|
-
|
|
85
|
-
if (state === undefined || outcome === undefined) return refuse(`No decision ${id}.`)
|
|
86
|
-
|
|
87
|
-
if (state.outcome !== undefined) return refuse(`${id} is already decided.`)
|
|
88
|
-
|
|
89
|
-
decided.push({ request: state.request, outcome })
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return Verdicts.Decide({ decided, answer: answer.id })
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** @internal */
|
|
96
|
-
export const verdict = (
|
|
97
|
-
entries: ReadonlyArray<AssistantEvent.AnyEntry>,
|
|
98
|
-
input: AssistantEvent.Input | AssistantEvent.StoredAppEvent
|
|
99
|
-
): Verdict =>
|
|
100
|
-
AssistantEvent.isLibraryEvent(input) && Predicate.isTagged(input, "BlockAnswer")
|
|
101
|
-
? answerVerdict(entries, input)
|
|
102
|
-
: store
|
|
103
|
-
|
|
104
|
-
// A restart can cut the chat off between storing an answer and resolving what it decided.
|
|
105
|
-
/** @internal */
|
|
106
|
-
export const unresolved = (
|
|
107
|
-
entries: ReadonlyArray<AssistantEvent.AnyEntry>
|
|
108
|
-
): ReadonlyArray<Decided & { readonly answer: InputId }> =>
|
|
109
|
-
[...Decision.fromEntries(entries).values()].flatMap((state) =>
|
|
110
|
-
state.outcome !== undefined && state.answer !== undefined && !state.resolved
|
|
111
|
-
? [{ request: state.request, outcome: state.outcome, answer: state.answer }]
|
|
112
|
-
: []
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
// They run with the ids and params the model gave them, before the model's next step.
|
|
116
|
-
/** @internal */
|
|
117
|
-
export const approvedCalls = (
|
|
118
|
-
entries: ReadonlyArray<AssistantEvent.AnyEntry>
|
|
119
|
-
): ReadonlyArray<AssistantEvent.ToolCallPart> =>
|
|
120
|
-
[...Decision.fromEntries(entries).values()].flatMap(({ request, outcome, resolved, result }) =>
|
|
121
|
-
resolved && outcome === "approved" && result === undefined
|
|
122
|
-
? [
|
|
123
|
-
Response.makePart("tool-call", {
|
|
124
|
-
id: request.origin.callId,
|
|
125
|
-
name: request.origin.tool,
|
|
126
|
-
params: request.origin.params,
|
|
127
|
-
providerExecuted: false
|
|
128
|
-
})
|
|
129
|
-
]
|
|
130
|
-
: []
|
|
131
|
-
)
|