effect-assistant 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +22 -2
- package/dist/AppEvent.d.ts +111 -0
- package/dist/AppEvent.d.ts.map +1 -0
- package/dist/AppEvent.js +71 -0
- package/dist/AppEvent.js.map +1 -0
- package/dist/Assistant.d.ts +403 -0
- package/dist/Assistant.d.ts.map +1 -0
- package/dist/Assistant.js +126 -0
- package/dist/Assistant.js.map +1 -0
- package/dist/AssistantActivity.d.ts +85 -0
- package/dist/AssistantActivity.d.ts.map +1 -0
- package/dist/AssistantActivity.js +105 -0
- package/dist/AssistantActivity.js.map +1 -0
- package/dist/AssistantEngine.d.ts +116 -0
- package/dist/AssistantEngine.d.ts.map +1 -0
- package/dist/AssistantEngine.js +255 -0
- package/dist/AssistantEngine.js.map +1 -0
- package/dist/AssistantEvent.d.ts +614 -0
- package/dist/AssistantEvent.d.ts.map +1 -0
- package/dist/AssistantEvent.js +523 -0
- package/dist/AssistantEvent.js.map +1 -0
- package/dist/AssistantLoop.d.ts +171 -0
- package/dist/AssistantLoop.d.ts.map +1 -0
- package/dist/AssistantLoop.js +108 -0
- package/dist/AssistantLoop.js.map +1 -0
- package/dist/AssistantToolCall.d.ts +32 -0
- package/dist/AssistantToolCall.d.ts.map +1 -0
- package/dist/AssistantToolCall.js +37 -0
- package/dist/AssistantToolCall.js.map +1 -0
- package/dist/Block.d.ts +69 -0
- package/dist/Block.d.ts.map +1 -0
- package/dist/Block.js +39 -0
- package/dist/Block.js.map +1 -0
- package/dist/BlockKit.d.ts +184 -0
- package/dist/BlockKit.d.ts.map +1 -0
- package/dist/BlockKit.js +151 -0
- package/dist/BlockKit.js.map +1 -0
- package/dist/ChatId.d.ts +31 -0
- package/dist/ChatId.d.ts.map +1 -0
- package/dist/ChatId.js +36 -0
- package/dist/ChatId.js.map +1 -0
- package/dist/ChatLog.d.ts +109 -0
- package/dist/ChatLog.d.ts.map +1 -0
- package/dist/ChatLog.js +177 -0
- package/dist/ChatLog.js.map +1 -0
- package/dist/ChatView.d.ts +459 -0
- package/dist/ChatView.d.ts.map +1 -0
- package/dist/ChatView.js +496 -0
- package/dist/ChatView.js.map +1 -0
- package/dist/Chats.d.ts +110 -0
- package/dist/Chats.d.ts.map +1 -0
- package/dist/Chats.js +98 -0
- package/dist/Chats.js.map +1 -0
- package/dist/Compaction.d.ts +105 -0
- package/dist/Compaction.d.ts.map +1 -0
- package/dist/Compaction.js +133 -0
- package/dist/Compaction.js.map +1 -0
- package/dist/CurrentChat.d.ts +40 -0
- package/dist/CurrentChat.d.ts.map +1 -0
- package/dist/CurrentChat.js +33 -0
- package/dist/CurrentChat.js.map +1 -0
- package/dist/Decision.d.ts +145 -0
- package/dist/Decision.d.ts.map +1 -0
- package/dist/Decision.js +178 -0
- package/dist/Decision.js.map +1 -0
- package/dist/DecisionId.d.ts +37 -0
- package/dist/DecisionId.d.ts.map +1 -0
- package/dist/DecisionId.js +44 -0
- package/dist/DecisionId.js.map +1 -0
- package/dist/InputId.d.ts +38 -0
- package/dist/InputId.d.ts.map +1 -0
- package/dist/InputId.js +43 -0
- package/dist/InputId.js.map +1 -0
- package/dist/Instructions.d.ts +56 -0
- package/dist/Instructions.d.ts.map +1 -0
- package/dist/Instructions.js +50 -0
- package/dist/Instructions.js.map +1 -0
- package/dist/Memory.d.ts +190 -0
- package/dist/Memory.d.ts.map +1 -0
- package/dist/Memory.js +216 -0
- package/dist/Memory.js.map +1 -0
- package/dist/RunId.d.ts +41 -0
- package/dist/RunId.d.ts.map +1 -0
- package/dist/RunId.js +46 -0
- package/dist/RunId.js.map +1 -0
- package/dist/Skill.d.ts +72 -0
- package/dist/Skill.d.ts.map +1 -0
- package/dist/Skill.js +58 -0
- package/dist/Skill.js.map +1 -0
- package/dist/Specialist.d.ts +267 -0
- package/dist/Specialist.d.ts.map +1 -0
- package/dist/Specialist.js +119 -0
- package/dist/Specialist.js.map +1 -0
- package/dist/ToolDiscovery.d.ts +108 -0
- package/dist/ToolDiscovery.d.ts.map +1 -0
- package/dist/ToolDiscovery.js +139 -0
- package/dist/ToolDiscovery.js.map +1 -0
- package/dist/ToolFailure.d.ts +66 -0
- package/dist/ToolFailure.d.ts.map +1 -0
- package/dist/ToolFailure.js +79 -0
- package/dist/ToolFailure.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +26 -0
- package/dist/internal/chat.js +202 -0
- package/dist/internal/chat.js.map +1 -0
- package/dist/internal/decisions.js +66 -0
- package/dist/internal/decisions.js.map +1 -0
- package/dist/internal/events.d.ts +46 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +65 -0
- package/dist/internal/events.js.map +1 -0
- package/dist/internal/history.js +185 -0
- package/dist/internal/history.js.map +1 -0
- package/dist/internal/instructions.js +11 -0
- package/dist/internal/instructions.js.map +1 -0
- package/dist/internal/loop.d.ts +73 -0
- package/dist/internal/loop.d.ts.map +1 -0
- package/dist/internal/loop.js +166 -0
- package/dist/internal/loop.js.map +1 -0
- package/dist/internal/schema.js +14 -0
- package/dist/internal/schema.js.map +1 -0
- package/dist/internal/skills.js +28 -0
- package/dist/internal/skills.js.map +1 -0
- package/dist/internal/specialists.js +273 -0
- package/dist/internal/specialists.js.map +1 -0
- package/dist/internal/telemetry.js +85 -0
- package/dist/internal/telemetry.js.map +1 -0
- package/dist/internal/toolkit.d.ts +76 -0
- package/dist/internal/toolkit.d.ts.map +1 -0
- package/dist/internal/toolkit.js +129 -0
- package/dist/internal/toolkit.js.map +1 -0
- package/dist/internal/tools.js +132 -0
- package/dist/internal/tools.js.map +1 -0
- package/dist/internal/turns.d.ts +64 -0
- package/dist/internal/turns.d.ts.map +1 -0
- package/dist/internal/turns.js +221 -0
- package/dist/internal/turns.js.map +1 -0
- package/dist/internal/uuid.js +45 -0
- package/dist/internal/uuid.js.map +1 -0
- package/dist/internal/writer.d.ts +12 -0
- package/dist/internal/writer.d.ts.map +1 -0
- package/package.json +51 -3
- package/src/AppEvent.ts +134 -0
- package/src/Assistant.ts +589 -0
- package/src/AssistantActivity.ts +150 -0
- package/src/AssistantEngine.ts +343 -0
- package/src/AssistantEvent.ts +635 -0
- package/src/AssistantLoop.ts +207 -0
- package/src/AssistantToolCall.ts +40 -0
- package/src/Block.ts +84 -0
- package/src/BlockKit.ts +311 -0
- package/src/ChatId.ts +36 -0
- package/src/ChatLog.ts +247 -0
- package/src/ChatView.ts +658 -0
- package/src/Chats.ts +122 -0
- package/src/Compaction.ts +224 -0
- package/src/CurrentChat.ts +47 -0
- package/src/Decision.ts +241 -0
- package/src/DecisionId.ts +46 -0
- package/src/InputId.ts +43 -0
- package/src/Instructions.ts +72 -0
- package/src/Memory.ts +276 -0
- package/src/RunId.ts +46 -0
- package/src/Skill.ts +91 -0
- package/src/Specialist.ts +355 -0
- package/src/ToolDiscovery.ts +187 -0
- package/src/ToolFailure.ts +74 -0
- package/src/index.ts +130 -0
- package/src/internal/chat.ts +372 -0
- package/src/internal/decisions.ts +131 -0
- package/src/internal/events.ts +100 -0
- package/src/internal/history.ts +366 -0
- package/src/internal/instructions.ts +9 -0
- package/src/internal/loop.ts +344 -0
- package/src/internal/schema.ts +25 -0
- package/src/internal/skills.ts +37 -0
- package/src/internal/specialists.ts +414 -0
- package/src/internal/telemetry.ts +125 -0
- package/src/internal/toolkit.ts +231 -0
- package/src/internal/tools.ts +210 -0
- package/src/internal/turns.ts +429 -0
- package/src/internal/uuid.ts +50 -0
- package/src/internal/writer.ts +12 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { isLibraryEvent } from "../AssistantEvent.js";
|
|
2
|
+
import { Answer, Props, fromEntries, kind } from "../Decision.js";
|
|
3
|
+
import { fromEntries as fromEntries$1 } from "../BlockKit.js";
|
|
4
|
+
import * as Predicate from "effect/Predicate";
|
|
5
|
+
import * as Context from "effect/Context";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import * as Data from "effect/Data";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
import * as Response from "effect/ai/Response";
|
|
10
|
+
//#region src/internal/decisions.ts
|
|
11
|
+
/** @internal */
|
|
12
|
+
const TurnDecisions = Context.Reference("effect-assistant/TurnDecisions", { defaultValue: Option.none });
|
|
13
|
+
const Verdicts = Data.taggedEnum();
|
|
14
|
+
const store = Verdicts.Store();
|
|
15
|
+
const refuse = (reason) => Verdicts.Refuse({ reason });
|
|
16
|
+
const decodeProps = Schema.decodeUnknownOption(Props);
|
|
17
|
+
const decodeAnswer = Schema.decodeUnknownOption(Answer);
|
|
18
|
+
const messageOf = (entries, id) => entries.map(({ event }) => event).find((event) => isLibraryEvent(event) && Predicate.isTagged(event, "AssistantMessage") && event.id === id);
|
|
19
|
+
const answerVerdict = (entries, answer) => {
|
|
20
|
+
const message = messageOf(entries, answer.messageId);
|
|
21
|
+
const onDecisionBlock = message?.block?.kind === kind;
|
|
22
|
+
if (!onDecisionBlock && answer.kind !== "decision") return store;
|
|
23
|
+
if (message?.block === void 0 || !onDecisionBlock || answer.kind !== "decision") return refuse(`${answer.messageId} has no decision block.`);
|
|
24
|
+
if (fromEntries$1(entries).get(message.id)?.open !== true) return refuse(`The block on ${message.id} is closed.`);
|
|
25
|
+
const props = decodeProps(message.block.props);
|
|
26
|
+
const value = decodeAnswer(answer.value);
|
|
27
|
+
if (Option.isNone(props) || Option.isNone(value)) return refuse(`That isn't an answer to the block on ${message.id}.`);
|
|
28
|
+
const asked = props.value.decisionIds;
|
|
29
|
+
const given = Object.keys(value.value.decisions);
|
|
30
|
+
if (given.length !== asked.length || !given.every((id) => asked.some((askedId) => askedId === id))) return refuse(`An answer decides exactly the decisions its block asks for: ${asked.join(", ")}.`);
|
|
31
|
+
const states = fromEntries(entries);
|
|
32
|
+
const decided = [];
|
|
33
|
+
for (const id of asked) {
|
|
34
|
+
const state = states.get(id);
|
|
35
|
+
const outcome = value.value.decisions[id];
|
|
36
|
+
if (state === void 0 || outcome === void 0) return refuse(`No decision ${id}.`);
|
|
37
|
+
if (state.outcome !== void 0) return refuse(`${id} is already decided.`);
|
|
38
|
+
decided.push({
|
|
39
|
+
request: state.request,
|
|
40
|
+
outcome
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return Verdicts.Decide({
|
|
44
|
+
decided,
|
|
45
|
+
answer: answer.id
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
/** @internal */
|
|
49
|
+
const verdict = (entries, input) => isLibraryEvent(input) && Predicate.isTagged(input, "BlockAnswer") ? answerVerdict(entries, input) : store;
|
|
50
|
+
/** @internal */
|
|
51
|
+
const unresolved = (entries) => [...fromEntries(entries).values()].flatMap((state) => state.outcome !== void 0 && state.answer !== void 0 && !state.resolved ? [{
|
|
52
|
+
request: state.request,
|
|
53
|
+
outcome: state.outcome,
|
|
54
|
+
answer: state.answer
|
|
55
|
+
}] : []);
|
|
56
|
+
/** @internal */
|
|
57
|
+
const approvedCalls = (entries) => [...fromEntries(entries).values()].flatMap(({ request, outcome, resolved, result }) => resolved && outcome === "approved" && result === void 0 ? [Response.makePart("tool-call", {
|
|
58
|
+
id: request.origin.callId,
|
|
59
|
+
name: request.origin.tool,
|
|
60
|
+
params: request.origin.params,
|
|
61
|
+
providerExecuted: false
|
|
62
|
+
})] : []);
|
|
63
|
+
//#endregion
|
|
64
|
+
export { TurnDecisions, approvedCalls, unresolved, verdict };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.js","names":["Decision.Props","Decision.Answer","AssistantEvent.isLibraryEvent","Decision.kind","BlockKit.fromEntries","Decision.fromEntries"],"sources":["../../src/internal/decisions.ts"],"sourcesContent":["import * as Response from \"effect/ai/Response\"\nimport * as Context from \"effect/Context\"\nimport * as Data from \"effect/Data\"\nimport type * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport * as BlockKit from \"../BlockKit.ts\"\nimport * as Decision from \"../Decision.ts\"\nimport type { InputId } from \"../InputId.ts\"\n\n// Only a turn's loop parks calls and runs approved ones. A turn's specialists inherit its context, so their runs set\n// it back to none.\n/** @internal */\nexport const TurnDecisions = Context.Reference<\n Option.Option<{\n readonly approved: Effect.Effect<ReadonlyArray<AssistantEvent.ToolCallPart>>\n }>\n>(\"effect-assistant/TurnDecisions\", { defaultValue: Option.none })\n\n/** @internal */\nexport interface Decided {\n readonly request: AssistantEvent.DecisionRequested\n readonly outcome: AssistantEvent.DecisionOutcome\n}\n\n/** @internal */\nexport type Verdict = Data.TaggedEnum<{\n Store: Record<never, never>\n Refuse: { readonly reason: string }\n Decide: { readonly decided: ReadonlyArray<Decided>; readonly answer: InputId }\n}>\n\nconst Verdicts = Data.taggedEnum<Verdict>()\n\nconst store = Verdicts.Store()\n\nconst refuse = (reason: string) => Verdicts.Refuse({ reason })\n\nconst decodeProps = Schema.decodeUnknownOption(Decision.Props)\n\nconst decodeAnswer = Schema.decodeUnknownOption(Decision.Answer)\n\nconst messageOf = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, id: string) =>\n entries\n .map(({ event }) => event)\n .find(\n (event): event is AssistantEvent.AssistantMessage =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"AssistantMessage\") && event.id === id\n )\n\nconst answerVerdict = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, answer: AssistantEvent.BlockAnswer) => {\n const message = messageOf(entries, answer.messageId)\n const onDecisionBlock = message?.block?.kind === Decision.kind\n\n if (!onDecisionBlock && answer.kind !== Decision.kind) return store\n\n if (message?.block === undefined || !onDecisionBlock || answer.kind !== Decision.kind) {\n return refuse(`${answer.messageId} has no decision block.`)\n }\n\n if (BlockKit.fromEntries(entries).get(message.id)?.open !== true)\n return refuse(`The block on ${message.id} is closed.`)\n\n const props = decodeProps(message.block.props)\n const value = decodeAnswer(answer.value)\n\n if (Option.isNone(props) || Option.isNone(value)) return refuse(`That isn't an answer to the block on ${message.id}.`)\n\n const asked = props.value.decisionIds\n const given = Object.keys(value.value.decisions)\n\n if (given.length !== asked.length || !given.every((id) => asked.some((askedId) => askedId === id))) {\n return refuse(`An answer decides exactly the decisions its block asks for: ${asked.join(\", \")}.`)\n }\n\n const states = Decision.fromEntries(entries)\n const decided: Array<Decided> = []\n\n for (const id of asked) {\n const state = states.get(id)\n const outcome = value.value.decisions[id]\n\n if (state === undefined || outcome === undefined) return refuse(`No decision ${id}.`)\n\n if (state.outcome !== undefined) return refuse(`${id} is already decided.`)\n\n decided.push({ request: state.request, outcome })\n }\n\n return Verdicts.Decide({ decided, answer: answer.id })\n}\n\n/** @internal */\nexport const verdict = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n input: AssistantEvent.Input | AssistantEvent.StoredAppEvent\n): Verdict =>\n AssistantEvent.isLibraryEvent(input) && Predicate.isTagged(input, \"BlockAnswer\")\n ? answerVerdict(entries, input)\n : store\n\n// A restart can cut the chat off between storing an answer and resolving what it decided.\n/** @internal */\nexport const unresolved = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>\n): ReadonlyArray<Decided & { readonly answer: InputId }> =>\n [...Decision.fromEntries(entries).values()].flatMap((state) =>\n state.outcome !== undefined && state.answer !== undefined && !state.resolved\n ? [{ request: state.request, outcome: state.outcome, answer: state.answer }]\n : []\n )\n\n// They run with the ids and params the model gave them, before the model's next step.\n/** @internal */\nexport const approvedCalls = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>\n): ReadonlyArray<AssistantEvent.ToolCallPart> =>\n [...Decision.fromEntries(entries).values()].flatMap(({ request, outcome, resolved, result }) =>\n resolved && outcome === \"approved\" && result === undefined\n ? [\n Response.makePart(\"tool-call\", {\n id: request.origin.callId,\n name: request.origin.tool,\n params: request.origin.params,\n providerExecuted: false\n })\n ]\n : []\n )\n"],"mappings":";;;;;;;;;;;AAeA,MAAa,gBAAgB,QAAQ,UAInC,kCAAkC,EAAE,cAAc,OAAO,KAAK,CAAC;AAejE,MAAM,WAAW,KAAK,WAAoB;AAE1C,MAAM,QAAQ,SAAS,MAAM;AAE7B,MAAM,UAAU,WAAmB,SAAS,OAAO,EAAE,OAAO,CAAC;AAE7D,MAAM,cAAc,OAAO,oBAAoBA,KAAc;AAE7D,MAAM,eAAe,OAAO,oBAAoBC,MAAe;AAE/D,MAAM,aAAa,SAAiD,OAClE,QACG,KAAK,EAAE,YAAY,KAAK,CAAC,CACzB,MACE,UACCC,eAA8B,KAAK,KAAK,UAAU,SAAS,OAAO,kBAAkB,KAAK,MAAM,OAAO,EAC1G;AAEJ,MAAM,iBAAiB,SAAiD,WAAuC;CAC7G,MAAM,UAAU,UAAU,SAAS,OAAO,SAAS;CACnD,MAAM,kBAAkB,SAAS,OAAO,SAASC;CAEjD,IAAI,CAAC,mBAAmB,OAAO,SAAS,YAAe,OAAO;CAE9D,IAAI,SAAS,UAAU,KAAA,KAAa,CAAC,mBAAmB,OAAO,SAAS,YACtE,OAAO,OAAO,GAAG,OAAO,UAAU,wBAAwB;CAG5D,IAAIC,cAAqB,OAAO,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,EAAE,SAAS,MAC1D,OAAO,OAAO,gBAAgB,QAAQ,GAAG,YAAY;CAEvD,MAAM,QAAQ,YAAY,QAAQ,MAAM,KAAK;CAC7C,MAAM,QAAQ,aAAa,OAAO,KAAK;CAEvC,IAAI,OAAO,OAAO,KAAK,KAAK,OAAO,OAAO,KAAK,GAAG,OAAO,OAAO,wCAAwC,QAAQ,GAAG,EAAE;CAErH,MAAM,QAAQ,MAAM,MAAM;CAC1B,MAAM,QAAQ,OAAO,KAAK,MAAM,MAAM,SAAS;CAE/C,IAAI,MAAM,WAAW,MAAM,UAAU,CAAC,MAAM,OAAO,OAAO,MAAM,MAAM,YAAY,YAAY,EAAE,CAAC,GAC/F,OAAO,OAAO,+DAA+D,MAAM,KAAK,IAAI,EAAE,EAAE;CAGlG,MAAM,SAASC,YAAqB,OAAO;CAC3C,MAAM,UAA0B,CAAC;CAEjC,KAAK,MAAM,MAAM,OAAO;EACtB,MAAM,QAAQ,OAAO,IAAI,EAAE;EAC3B,MAAM,UAAU,MAAM,MAAM,UAAU;EAEtC,IAAI,UAAU,KAAA,KAAa,YAAY,KAAA,GAAW,OAAO,OAAO,eAAe,GAAG,EAAE;EAEpF,IAAI,MAAM,YAAY,KAAA,GAAW,OAAO,OAAO,GAAG,GAAG,qBAAqB;EAE1E,QAAQ,KAAK;GAAE,SAAS,MAAM;GAAS;EAAQ,CAAC;CAClD;CAEA,OAAO,SAAS,OAAO;EAAE;EAAS,QAAQ,OAAO;CAAG,CAAC;AACvD;;AAGA,MAAa,WACX,SACA,UAEAH,eAA8B,KAAK,KAAK,UAAU,SAAS,OAAO,aAAa,IAC3E,cAAc,SAAS,KAAK,IAC5B;;AAIN,MAAa,cACX,YAEA,CAAC,GAAGG,YAAqB,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,UACnD,MAAM,YAAY,KAAA,KAAa,MAAM,WAAW,KAAA,KAAa,CAAC,MAAM,WAChE,CAAC;CAAE,SAAS,MAAM;CAAS,SAAS,MAAM;CAAS,QAAQ,MAAM;AAAO,CAAC,IACzE,CAAC,CACP;;AAIF,MAAa,iBACX,YAEA,CAAC,GAAGA,YAAqB,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,SAAS,UAAU,aACjF,YAAY,YAAY,cAAc,WAAW,KAAA,IAC7C,CACE,SAAS,SAAS,aAAa;CAC7B,IAAI,QAAQ,OAAO;CACnB,MAAM,QAAQ,OAAO;CACrB,QAAQ,QAAQ,OAAO;CACvB,kBAAkB;AACpB,CAAC,CACH,IACA,CAAC,CACP"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Any, AppEvent_d_exports, EventSchema, Render } from "../AppEvent.js";
|
|
2
|
+
import { AnyEntry, AssistantEvent_d_exports } from "../AssistantEvent.js";
|
|
3
|
+
import { Assistant_d_exports, Entry } from "../Assistant.js";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Schema from "effect/Schema";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
7
|
+
declare namespace events_d_exports {
|
|
8
|
+
export { AppEvents, DecodedEntries, decodeEntries, decodeEntry, encode, isRegistered, make, read };
|
|
9
|
+
}
|
|
10
|
+
/** @internal */
|
|
11
|
+
export interface AppEvents {
|
|
12
|
+
readonly byTag: ReadonlyMap<string, Registered>;
|
|
13
|
+
readonly wakes: ReadonlySet<string>;
|
|
14
|
+
}
|
|
15
|
+
interface Registered {
|
|
16
|
+
readonly schema: EventSchema;
|
|
17
|
+
readonly render: Render<{
|
|
18
|
+
readonly _tag: string;
|
|
19
|
+
}> | undefined;
|
|
20
|
+
}
|
|
21
|
+
/** @internal */
|
|
22
|
+
export declare const make: (events: ReadonlyArray<Any>) => AppEvents;
|
|
23
|
+
/** @internal */
|
|
24
|
+
export declare const encode: (app: AppEvents, event: {
|
|
25
|
+
readonly _tag: string;
|
|
26
|
+
}) => Effect.Effect<{
|
|
27
|
+
readonly [x: string]: Schema.Json;
|
|
28
|
+
readonly _tag: string & import("effect/Brand").Brand<"~effect-assistant/AppTag">;
|
|
29
|
+
}, never, never>;
|
|
30
|
+
/** @internal */
|
|
31
|
+
export declare const isRegistered: (app: AppEvents, event: {
|
|
32
|
+
readonly _tag: string;
|
|
33
|
+
}) => boolean;
|
|
34
|
+
/** @internal */
|
|
35
|
+
export declare const decodeEntry: (app: AppEvents, entry: AnyEntry) => Option.Option<Entry<any>>;
|
|
36
|
+
/** @internal */
|
|
37
|
+
export declare const decodeEntries: (app: AppEvents, entries: ReadonlyArray<AnyEntry>) => ReadonlyArray<Entry<any>>;
|
|
38
|
+
/** @internal */
|
|
39
|
+
export type DecodedEntries = ReturnType<typeof decodeEntries>;
|
|
40
|
+
/** @internal */
|
|
41
|
+
export declare const read: (app: AppEvents, event: {
|
|
42
|
+
readonly _tag: string;
|
|
43
|
+
}) => string | undefined;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { events_d_exports };
|
|
46
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","names":[],"sources":["../../src/internal/events.ts"],"mappings":";;;;;;;;;;iBASiB;WACN,OAAO,oBAAoB;WAC3B,OAAO;;UAGR;WACC,QAAQ;WACR,QAAQ;aAA2B;;;;qBAIjC,OAAI,QAAY,cAAc,SAAgB;;qBAe9C,SAAM,KAAA,WAAA;WAAwE;;;;;;qBAY9E,eAAY,KAAS,WAAS;WAAoB;;;qBAIlD,cAAW,KAAS,WAAS,OAAS,aAA0B,OAAO,OAAO;;qBAgB9E,gBAAa,KACnB,WAAS,SACL,cAAc,cACtB,cAAc;;YAGL,iBAAiB,kBAAkB;;qBAWlC,OAAI,KAAS,WAAS;WAAoB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { StoredAppEvent, isLibraryEvent } from "../AssistantEvent.js";
|
|
2
|
+
import { toJsonText } from "./schema.js";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
6
|
+
//#region src/internal/events.ts
|
|
7
|
+
/** @internal */
|
|
8
|
+
const make = (events) => {
|
|
9
|
+
const byTag = new Map(events.map((event) => [event.tag, {
|
|
10
|
+
schema: event.schema,
|
|
11
|
+
render: event.render
|
|
12
|
+
}]));
|
|
13
|
+
for (const tag of byTag.keys()) if (isLibraryEvent({ _tag: tag })) throw new Error(`${tag} is one of the library's events.`);
|
|
14
|
+
if (byTag.size !== events.length) throw new Error("An assistant's events have different tags.");
|
|
15
|
+
return {
|
|
16
|
+
byTag,
|
|
17
|
+
wakes: new Set(events.flatMap((event) => event.wakes ? [event.tag] : []))
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
/** @internal */
|
|
21
|
+
const encode = Effect.fnUntraced(function* (app, event) {
|
|
22
|
+
const registered = app.byTag.get(event._tag);
|
|
23
|
+
if (registered === void 0) return yield* Effect.die(`${event._tag} isn't one of this assistant's events.`);
|
|
24
|
+
const json = yield* Schema.encodeUnknownEffect(Schema.toCodecJson(registered.schema))(event);
|
|
25
|
+
return yield* Schema.decodeUnknownEffect(StoredAppEvent)(json);
|
|
26
|
+
}, Effect.orDie);
|
|
27
|
+
/** @internal */
|
|
28
|
+
const isRegistered = (app, event) => app.byTag.has(event._tag);
|
|
29
|
+
/** @internal */
|
|
30
|
+
const decodeEntry = (app, entry) => {
|
|
31
|
+
const { event } = entry;
|
|
32
|
+
if (isLibraryEvent(event)) return Option.some({
|
|
33
|
+
...toEntry(entry),
|
|
34
|
+
event
|
|
35
|
+
});
|
|
36
|
+
const registered = app.byTag.get(event._tag);
|
|
37
|
+
if (registered === void 0) return Option.none();
|
|
38
|
+
return Option.map(Schema.decodeUnknownOption(Schema.toCodecJson(registered.schema))(event), (decoded) => ({
|
|
39
|
+
...toEntry(entry),
|
|
40
|
+
event: decoded
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
43
|
+
/** @internal */
|
|
44
|
+
const decodeEntries = (app, entries) => entries.flatMap((entry) => Option.toArray(decodeEntry(app, entry)));
|
|
45
|
+
const decodeStored = Schema.decodeUnknownOption(StoredAppEvent);
|
|
46
|
+
const toEntry = (entry) => ({
|
|
47
|
+
chatId: entry.chatId,
|
|
48
|
+
seq: entry.seq,
|
|
49
|
+
at: entry.at
|
|
50
|
+
});
|
|
51
|
+
/** @internal */
|
|
52
|
+
const read = (app, event) => {
|
|
53
|
+
const registered = app.byTag.get(event._tag);
|
|
54
|
+
if (registered?.render === void 0) return void 0;
|
|
55
|
+
if (registered.render !== "json") return registered.render(event);
|
|
56
|
+
const stored = Schema.encodeUnknownOption(Schema.toCodecJson(registered.schema))(event).pipe(Option.flatMap(decodeStored));
|
|
57
|
+
return Option.match(stored, {
|
|
58
|
+
onNone: () => void 0,
|
|
59
|
+
onSome: ({ _tag, ...fields }) => `<${_tag}>\n${toJsonText(fields)}\n</${_tag}>`
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
export { decodeEntries, decodeEntry, encode, isRegistered, make, read };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","names":["AssistantEvent.isLibraryEvent","AssistantEvent.StoredAppEvent"],"sources":["../../src/internal/events.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport type * as AppEvent from \"../AppEvent.ts\"\nimport type * as Assistant from \"../Assistant.ts\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport { toJsonText } from \"./schema.ts\"\n\n/** @internal */\nexport interface AppEvents {\n readonly byTag: ReadonlyMap<string, Registered>\n readonly wakes: ReadonlySet<string>\n}\n\ninterface Registered {\n readonly schema: AppEvent.EventSchema\n readonly render: AppEvent.Render<{ readonly _tag: string }> | undefined\n}\n\n/** @internal */\nexport const make = (events: ReadonlyArray<AppEvent.Any>): AppEvents => {\n const byTag = new Map(\n events.map((event): [string, Registered] => [event.tag, { schema: event.schema, render: event.render }])\n )\n\n for (const tag of byTag.keys()) {\n if (AssistantEvent.isLibraryEvent({ _tag: tag })) throw new Error(`${tag} is one of the library's events.`)\n }\n\n if (byTag.size !== events.length) throw new Error(\"An assistant's events have different tags.\")\n\n return { byTag, wakes: new Set(events.flatMap((event) => (event.wakes ? [event.tag] : []))) }\n}\n\n/** @internal */\nexport const encode = Effect.fnUntraced(function* (app: AppEvents, event: { readonly _tag: string }) {\n const registered = app.byTag.get(event._tag)\n\n // The types only let registered events through, so one with no schema here is a defect.\n if (registered === undefined) return yield* Effect.die(`${event._tag} isn't one of this assistant's events.`)\n\n const json = yield* Schema.encodeUnknownEffect(Schema.toCodecJson(registered.schema))(event)\n\n return yield* Schema.decodeUnknownEffect(AssistantEvent.StoredAppEvent)(json)\n}, Effect.orDie)\n\n/** @internal */\nexport const isRegistered = (app: AppEvents, event: { readonly _tag: string }): boolean => app.byTag.has(event._tag)\n\n// An app event that doesn't decode (say, one from an older version of its schema) is left out.\n/** @internal */\nexport const decodeEntry = (app: AppEvents, entry: AssistantEvent.AnyEntry): Option.Option<Assistant.Entry<any>> => {\n const { event } = entry\n\n if (AssistantEvent.isLibraryEvent(event)) return Option.some({ ...toEntry(entry), event })\n\n const registered = app.byTag.get(event._tag)\n\n if (registered === undefined) return Option.none()\n\n return Option.map(Schema.decodeUnknownOption(Schema.toCodecJson(registered.schema))(event), (decoded) => ({\n ...toEntry(entry),\n event: decoded\n }))\n}\n\n/** @internal */\nexport const decodeEntries = (\n app: AppEvents,\n entries: ReadonlyArray<AssistantEvent.AnyEntry>\n): ReadonlyArray<Assistant.Entry<any>> => entries.flatMap((entry) => Option.toArray(decodeEntry(app, entry)))\n\n/** @internal */\nexport type DecodedEntries = ReturnType<typeof decodeEntries>\n\nconst decodeStored = Schema.decodeUnknownOption(AssistantEvent.StoredAppEvent)\n\nconst toEntry = (entry: AssistantEvent.AnyEntry) => ({\n chatId: entry.chatId,\n seq: entry.seq,\n at: entry.at\n})\n\n/** @internal */\nexport const read = (app: AppEvents, event: { readonly _tag: string }): string | undefined => {\n const registered = app.byTag.get(event._tag)\n\n if (registered?.render === undefined) return undefined\n\n if (registered.render !== \"json\") return registered.render(event)\n\n const stored = Schema.encodeUnknownOption(Schema.toCodecJson(registered.schema))(event).pipe(\n Option.flatMap(decodeStored)\n )\n\n return Option.match(stored, {\n onNone: () => undefined,\n onSome: ({ _tag, ...fields }) => `<${_tag}>\\n${toJsonText(fields)}\\n</${_tag}>`\n })\n}\n"],"mappings":";;;;;;;AAoBA,MAAa,QAAQ,WAAmD;CACtE,MAAM,QAAQ,IAAI,IAChB,OAAO,KAAK,UAAgC,CAAC,MAAM,KAAK;EAAE,QAAQ,MAAM;EAAQ,QAAQ,MAAM;CAAO,CAAC,CAAC,CACzG;CAEA,KAAK,MAAM,OAAO,MAAM,KAAK,GAC3B,IAAIA,eAA8B,EAAE,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,GAAG,IAAI,iCAAiC;CAG5G,IAAI,MAAM,SAAS,OAAO,QAAQ,MAAM,IAAI,MAAM,4CAA4C;CAE9F,OAAO;EAAE;EAAO,OAAO,IAAI,IAAI,OAAO,SAAS,UAAW,MAAM,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,CAAE,CAAC;CAAE;AAC9F;;AAGA,MAAa,SAAS,OAAO,WAAW,WAAW,KAAgB,OAAkC;CACnG,MAAM,aAAa,IAAI,MAAM,IAAI,MAAM,IAAI;CAG3C,IAAI,eAAe,KAAA,GAAW,OAAO,OAAO,OAAO,IAAI,GAAG,MAAM,KAAK,uCAAuC;CAE5G,MAAM,OAAO,OAAO,OAAO,oBAAoB,OAAO,YAAY,WAAW,MAAM,CAAC,CAAC,CAAC,KAAK;CAE3F,OAAO,OAAO,OAAO,oBAAoBC,cAA6B,CAAC,CAAC,IAAI;AAC9E,GAAG,OAAO,KAAK;;AAGf,MAAa,gBAAgB,KAAgB,UAA8C,IAAI,MAAM,IAAI,MAAM,IAAI;;AAInH,MAAa,eAAe,KAAgB,UAAwE;CAClH,MAAM,EAAE,UAAU;CAElB,IAAID,eAA8B,KAAK,GAAG,OAAO,OAAO,KAAK;EAAE,GAAG,QAAQ,KAAK;EAAG;CAAM,CAAC;CAEzF,MAAM,aAAa,IAAI,MAAM,IAAI,MAAM,IAAI;CAE3C,IAAI,eAAe,KAAA,GAAW,OAAO,OAAO,KAAK;CAEjD,OAAO,OAAO,IAAI,OAAO,oBAAoB,OAAO,YAAY,WAAW,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,aAAa;EACxG,GAAG,QAAQ,KAAK;EAChB,OAAO;CACT,EAAE;AACJ;;AAGA,MAAa,iBACX,KACA,YACwC,QAAQ,SAAS,UAAU,OAAO,QAAQ,YAAY,KAAK,KAAK,CAAC,CAAC;AAK5G,MAAM,eAAe,OAAO,oBAAoBC,cAA6B;AAE7E,MAAM,WAAW,WAAoC;CACnD,QAAQ,MAAM;CACd,KAAK,MAAM;CACX,IAAI,MAAM;AACZ;;AAGA,MAAa,QAAQ,KAAgB,UAAyD;CAC5F,MAAM,aAAa,IAAI,MAAM,IAAI,MAAM,IAAI;CAE3C,IAAI,YAAY,WAAW,KAAA,GAAW,OAAO,KAAA;CAE7C,IAAI,WAAW,WAAW,QAAQ,OAAO,WAAW,OAAO,KAAK;CAEhE,MAAM,SAAS,OAAO,oBAAoB,OAAO,YAAY,WAAW,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KACtF,OAAO,QAAQ,YAAY,CAC7B;CAEA,OAAO,OAAO,MAAM,QAAQ;EAC1B,cAAc,KAAA;EACd,SAAS,EAAE,MAAM,GAAG,aAAa,IAAI,KAAK,KAAK,WAAW,MAAM,EAAE,MAAM,KAAK;CAC/E,CAAC;AACH"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { fromKey } from "./uuid.js";
|
|
2
|
+
import { isInput as isInput$1, isLibraryEvent, isTurnEnd, isUserInput, isWaking, textWithAttachments } from "../AssistantEvent.js";
|
|
3
|
+
import { Answer } from "../Decision.js";
|
|
4
|
+
import { toJson, toJsonText } from "./schema.js";
|
|
5
|
+
import * as Predicate from "effect/Predicate";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
import * as Response from "effect/ai/Response";
|
|
9
|
+
import * as Prompt from "effect/ai/Prompt";
|
|
10
|
+
import * as Match from "effect/Match";
|
|
11
|
+
//#region src/internal/history.ts
|
|
12
|
+
/** @internal */
|
|
13
|
+
const userText = (text) => Prompt.userMessage({ content: [Prompt.textPart({ text })] });
|
|
14
|
+
/** @internal */
|
|
15
|
+
const toolMessage = (result) => Prompt.toolMessage({ content: [Prompt.toolResultPart({
|
|
16
|
+
id: result.callId,
|
|
17
|
+
name: result.tool,
|
|
18
|
+
isFailure: result.outcome === "failure",
|
|
19
|
+
result: result.result,
|
|
20
|
+
providerExecuted: false
|
|
21
|
+
})] });
|
|
22
|
+
/** @internal */
|
|
23
|
+
const unansweredCalls = (history) => {
|
|
24
|
+
const answered = new Set(history.flatMap((message) => message.role === "tool" ? message.content.flatMap((part) => part.type === "tool-result" ? [part.id] : []) : []));
|
|
25
|
+
return history.flatMap((message) => message.role === "assistant" ? message.content.flatMap((part) => part.type === "tool-call" && !part.providerExecuted && !answered.has(part.id) ? [Response.makePart("tool-call", {
|
|
26
|
+
id: part.id,
|
|
27
|
+
name: part.name,
|
|
28
|
+
params: toJson(part.params),
|
|
29
|
+
providerExecuted: false,
|
|
30
|
+
metadata: part.options
|
|
31
|
+
})] : []) : []);
|
|
32
|
+
};
|
|
33
|
+
/** @internal */
|
|
34
|
+
const notRunOutput = "Not run: the turn ended before this call ran.";
|
|
35
|
+
/** @internal */
|
|
36
|
+
const mayHaveRunOutput = "Stopped: the turn ended while this call ran. It may have done its work; check before doing it again.";
|
|
37
|
+
const ago = (millis) => {
|
|
38
|
+
const seconds = Math.max(0, Math.round(millis / 1e3));
|
|
39
|
+
return seconds < 90 ? `${seconds} seconds` : `${Math.round(seconds / 60)} minutes`;
|
|
40
|
+
};
|
|
41
|
+
const finishedText = (entries, index, finished) => {
|
|
42
|
+
const at = entries[index];
|
|
43
|
+
const isStart = ({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "SpecialistStarted") && event.runId === finished.runId;
|
|
44
|
+
const from = entries.slice(0, index).findLastIndex(isStart);
|
|
45
|
+
const started = from === -1 ? void 0 : entries[from];
|
|
46
|
+
const wrote = (started === void 0 ? [] : entries.slice(from + 1, index)).some(({ event }) => Predicate.isTagged(event, "UserMessage"));
|
|
47
|
+
const lines = [
|
|
48
|
+
"A report from a specialist you started. The user hasn't seen it.",
|
|
49
|
+
started === void 0 ? `You started it for: ${finished.goal}` : `You started it ${ago(at.at - started.at)} ago for: ${finished.goal}`,
|
|
50
|
+
wrote ? "The user has written since you started it." : "The user hasn't written since you started it.",
|
|
51
|
+
Match.value(finished.status).pipe(Match.when("answered", () => `Report:\n${finished.report ?? ""}`), Match.when("failed", () => `It failed: ${finished.error ?? ""}`), Match.when("cut_off", () => "A restart cut it off before it finished. Spawn it again if it's still needed."), Match.when("stopped", () => "You stopped it."), Match.exhaustive)
|
|
52
|
+
];
|
|
53
|
+
return `<specialist name="${finished.specialist}" run="${finished.runId}" status="${finished.status}">\n${lines.join("\n")}\n</specialist>`;
|
|
54
|
+
};
|
|
55
|
+
const answerText = (answer) => `<answer message="${answer.messageId}" kind="${answer.kind}">\nThe user answered with the block on your message ${answer.messageId}.\n${toJsonText(answer.value)}\n</answer>`;
|
|
56
|
+
/** @internal */
|
|
57
|
+
const parkedResult = (request, origin) => Prompt.toolMessage({ content: [Prompt.toolResultPart({
|
|
58
|
+
id: origin.callId,
|
|
59
|
+
name: origin.tool,
|
|
60
|
+
isFailure: false,
|
|
61
|
+
result: {
|
|
62
|
+
status: "waiting_for_decision",
|
|
63
|
+
decisionId: request.id,
|
|
64
|
+
next: "Not run: it runs only if the user approves it. Send them a message with a decision block for this decisionId that says what they approve (the call's key facts). Their answer runs it, or declines it."
|
|
65
|
+
},
|
|
66
|
+
providerExecuted: false
|
|
67
|
+
})] });
|
|
68
|
+
const whatDecided = (request, outcome) => outcome === "approved" ? `approved: ${request.origin.tool} runs now, and its result follows.` : `declined: ${request.origin.tool} didn't run, and won't.`;
|
|
69
|
+
const decodeDecisionAnswer = Schema.decodeUnknownOption(Answer);
|
|
70
|
+
const decisionAnswerText = (answer, requests) => Option.match(decodeDecisionAnswer(answer.value), {
|
|
71
|
+
onNone: () => answerText(answer),
|
|
72
|
+
onSome: ({ decisions }) => {
|
|
73
|
+
const lines = Object.entries(decisions).map(([id, outcome]) => {
|
|
74
|
+
const request = requests.get(id);
|
|
75
|
+
return `- ${id}: ${request === void 0 ? outcome : whatDecided(request, outcome)}`;
|
|
76
|
+
});
|
|
77
|
+
return `<answer message="${answer.messageId}" kind="${answer.kind}">\nThe user decided with the block on your message ${answer.messageId}:\n${lines.join("\n")}\n</answer>`;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const resultText = (result) => `<result tool="${result.tool}" call="${result.callId}" outcome="${result.outcome}">\nThe result of a call from earlier in this chat.\n${toJsonText(result.result)}\n</result>`;
|
|
81
|
+
const approvedResultText = (request, tool, result) => `<decision id="${request.id}" tool="${tool}" outcome="approved">\nThe user approved it, and ${tool} ran: ${result.outcome}.\n${toJsonText(result.result)}\n</decision>`;
|
|
82
|
+
/** @internal */
|
|
83
|
+
const fromEntries = (entries, readApp) => {
|
|
84
|
+
const history = [];
|
|
85
|
+
const held = [];
|
|
86
|
+
const open = /* @__PURE__ */ new Set();
|
|
87
|
+
const requests = /* @__PURE__ */ new Map();
|
|
88
|
+
const parked = /* @__PURE__ */ new Map();
|
|
89
|
+
const called = /* @__PURE__ */ new Set();
|
|
90
|
+
const flushIfClosed = () => {
|
|
91
|
+
if (open.size === 0) history.push(...held.splice(0));
|
|
92
|
+
};
|
|
93
|
+
const nextSeen = (from) => {
|
|
94
|
+
const next = entries.slice(from + 1).find(({ event }) => Predicate.isTagged(event, "ModelOutput"))?.event;
|
|
95
|
+
return next !== void 0 && isLibraryEvent(next) && Predicate.isTagged(next, "ModelOutput") ? next.seenThroughSeq : void 0;
|
|
96
|
+
};
|
|
97
|
+
const input = (index, item) => {
|
|
98
|
+
const seen = nextSeen(index);
|
|
99
|
+
if (open.size > 0 || seen !== void 0 && seen < entries[index].seq) held.push(item);
|
|
100
|
+
else history.push(item);
|
|
101
|
+
};
|
|
102
|
+
entries.forEach(({ event }, index) => {
|
|
103
|
+
if (!isLibraryEvent(event)) {
|
|
104
|
+
const text = readApp(event);
|
|
105
|
+
if (text !== void 0) input(index, userText(text));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
Match.value(event).pipe(Match.tagsExhaustive({
|
|
109
|
+
UserMessage: (message) => input(index, userText(textWithAttachments(message.text, message.attachments))),
|
|
110
|
+
BlockAnswer: (answer) => input(index, userText(answer.kind === "decision" ? decisionAnswerText(answer, requests) : answerText(answer))),
|
|
111
|
+
ModelOutput: (output) => {
|
|
112
|
+
history.push(...Prompt.fromResponseParts(output.parts).content);
|
|
113
|
+
for (const part of output.parts) if (part.type === "tool-call") {
|
|
114
|
+
open.add(part.id);
|
|
115
|
+
called.add(part.id);
|
|
116
|
+
}
|
|
117
|
+
flushIfClosed();
|
|
118
|
+
},
|
|
119
|
+
ToolResult: (result) => {
|
|
120
|
+
const request = parked.get(result.callId);
|
|
121
|
+
if (request !== void 0) return input(index, userText(approvedResultText(request, result.tool, result)));
|
|
122
|
+
if (!called.has(result.callId)) return input(index, userText(resultText(result)));
|
|
123
|
+
history.push(toolMessage(result));
|
|
124
|
+
open.delete(result.callId);
|
|
125
|
+
flushIfClosed();
|
|
126
|
+
},
|
|
127
|
+
DecisionRequested: (request) => {
|
|
128
|
+
requests.set(request.id, request);
|
|
129
|
+
parked.set(request.origin.callId, request);
|
|
130
|
+
history.push(parkedResult(request, request.origin));
|
|
131
|
+
open.delete(request.origin.callId);
|
|
132
|
+
flushIfClosed();
|
|
133
|
+
},
|
|
134
|
+
DecisionResolved: () => void 0,
|
|
135
|
+
AssistantMessage: () => void 0,
|
|
136
|
+
ToolStarted: () => void 0,
|
|
137
|
+
TurnStarted: () => void 0,
|
|
138
|
+
TurnCompleted: () => void 0,
|
|
139
|
+
TurnFailed: () => void 0,
|
|
140
|
+
SpecialistFinished: (finished) => input(index, userText(finishedText(entries, index, finished))),
|
|
141
|
+
SpecialistStarted: () => void 0,
|
|
142
|
+
BlocksClosed: () => void 0,
|
|
143
|
+
ChatDeleted: () => void 0
|
|
144
|
+
}));
|
|
145
|
+
});
|
|
146
|
+
return [...history, ...held];
|
|
147
|
+
};
|
|
148
|
+
/** @internal */
|
|
149
|
+
const isInput = (event, wakes) => isInput$1(event) || !isLibraryEvent(event) && wakes.has(event._tag);
|
|
150
|
+
/** @internal */
|
|
151
|
+
const handledThrough = (entries) => {
|
|
152
|
+
const last = entries.findLast(({ event }) => isTurnEnd(event))?.event;
|
|
153
|
+
return last !== void 0 && isTurnEnd(last) ? last.throughSeq : 0;
|
|
154
|
+
};
|
|
155
|
+
/** @internal */
|
|
156
|
+
const unhandledInputs = (entries, wakes) => {
|
|
157
|
+
const seen = handledThrough(entries);
|
|
158
|
+
return entries.flatMap(({ event, seq }) => seq > seen && isInput(event, wakes) && !(isInput$1(event) && !isWaking(event)) ? [event] : []);
|
|
159
|
+
};
|
|
160
|
+
/** @internal */
|
|
161
|
+
const needsTurn = (entries, wakes) => unhandledInputs(entries, wakes).length > 0;
|
|
162
|
+
/** @internal */
|
|
163
|
+
const owesReply = (entries, throughSeq) => {
|
|
164
|
+
const latest = entries.findLast(({ event, seq }) => seq <= throughSeq && isInput$1(event) && isUserInput(event));
|
|
165
|
+
return latest !== void 0 && !entries.some(({ event, seq }) => seq > latest.seq && (Predicate.isTagged(event, "AssistantMessage") || Predicate.isTagged(event, "TurnFailed")));
|
|
166
|
+
};
|
|
167
|
+
const sentToWaitOn = Schema.decodeUnknownOption(Schema.Struct({ wait: Schema.Literal(false) }));
|
|
168
|
+
const progressMessages = (entries) => new Set(entries.flatMap(({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "ModelOutput") ? event.parts.flatMap((part) => part.type === "tool-call" && part.name === "send_user_message" && Option.isSome(sentToWaitOn(part.params)) ? [fromKey(`message:${part.id}`)] : []) : []));
|
|
169
|
+
/** @internal */
|
|
170
|
+
const owesDelivery = (entries, throughSeq) => {
|
|
171
|
+
const latest = entries.findLast(({ event, seq }) => seq <= throughSeq && isInput$1(event) && isUserInput(event));
|
|
172
|
+
if (latest === void 0) return false;
|
|
173
|
+
const after = entries.flatMap(({ seq, event }) => seq > latest.seq && isLibraryEvent(event) ? [event] : []);
|
|
174
|
+
const messages = after.flatMap((event) => Predicate.isTagged(event, "AssistantMessage") ? [event] : []);
|
|
175
|
+
const progress = progressMessages(entries);
|
|
176
|
+
const started = after.flatMap((event) => Predicate.isTagged(event, "SpecialistStarted") ? [event.runId] : []);
|
|
177
|
+
const finished = new Set(entries.flatMap(({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "SpecialistFinished") ? [event.runId] : []));
|
|
178
|
+
return messages.length > 0 && messages.every((message) => message.block === void 0 && progress.has(message.id)) && !after.some((event) => Predicate.isTagged(event, "TurnFailed")) && started.every((runId) => finished.has(runId));
|
|
179
|
+
};
|
|
180
|
+
/** @internal */
|
|
181
|
+
const unreadAfter = (entries, throughSeq, wakes) => entries.some(({ event, seq }) => seq > throughSeq && isInput(event, wakes));
|
|
182
|
+
//#endregion
|
|
183
|
+
export { fromEntries, handledThrough, isInput, mayHaveRunOutput, needsTurn, notRunOutput, owesDelivery, owesReply, parkedResult, toolMessage, unansweredCalls, unhandledInputs, unreadAfter, userText };
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","names":["AssistantEvent.isLibraryEvent","Decision.Answer","AssistantEvent.textWithAttachments","AssistantEvent.isInput","AssistantEvent.isTurnEnd","AssistantEvent.isWaking","AssistantEvent.isUserInput","Uuid.fromKey"],"sources":["../../src/internal/history.ts"],"sourcesContent":["import * as Prompt from \"effect/ai/Prompt\"\nimport * as Response from \"effect/ai/Response\"\nimport * as Match from \"effect/Match\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport * as Decision from \"../Decision.ts\"\nimport { toJson, toJsonText } from \"./schema.ts\"\nimport * as Uuid from \"./uuid.ts\"\n\n/** @internal */\nexport const userText = (text: string): Prompt.UserMessage =>\n Prompt.userMessage({ content: [Prompt.textPart({ text })] })\n\n/** @internal */\nexport const toolMessage = (result: AssistantEvent.ToolResult): Prompt.ToolMessage =>\n Prompt.toolMessage({\n content: [\n Prompt.toolResultPart({\n id: result.callId,\n name: result.tool,\n isFailure: result.outcome === \"failure\",\n result: result.result,\n providerExecuted: false\n })\n ]\n })\n\n// A call with no result is one a crash cut off.\n/** @internal */\nexport const unansweredCalls = (history: ReadonlyArray<Prompt.Message>): ReadonlyArray<AssistantEvent.ToolCallPart> => {\n const answered = new Set(\n history.flatMap((message) =>\n message.role === \"tool\" ? message.content.flatMap((part) => (part.type === \"tool-result\" ? [part.id] : [])) : []\n )\n )\n\n return history.flatMap((message) =>\n message.role === \"assistant\"\n ? message.content.flatMap((part) =>\n part.type === \"tool-call\" && !part.providerExecuted && !answered.has(part.id)\n ? [\n Response.makePart(\"tool-call\", {\n id: part.id,\n name: part.name,\n params: toJson(part.params),\n providerExecuted: false,\n metadata: part.options\n })\n ]\n : []\n )\n : []\n )\n}\n\n/** @internal */\nexport const notRunOutput = \"Not run: the turn ended before this call ran.\"\n\n/** @internal */\nexport const mayHaveRunOutput =\n \"Stopped: the turn ended while this call ran. It may have done its work; check before doing it again.\"\n\nconst ago = (millis: number) => {\n const seconds = Math.max(0, Math.round(millis / 1000))\n\n return seconds < 90 ? `${seconds} seconds` : `${Math.round(seconds / 60)} minutes`\n}\n\nconst finishedText = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n index: number,\n finished: AssistantEvent.SpecialistFinished\n) => {\n const at = entries[index]!\n\n const isStart = ({ event }: AssistantEvent.AnyEntry) =>\n AssistantEvent.isLibraryEvent(event) &&\n Predicate.isTagged(event, \"SpecialistStarted\") &&\n event.runId === finished.runId\n\n const from = entries.slice(0, index).findLastIndex(isStart)\n const started = from === -1 ? undefined : entries[from]\n const since = started === undefined ? [] : entries.slice(from + 1, index)\n const wrote = since.some(({ event }) => Predicate.isTagged(event, \"UserMessage\"))\n\n const lines = [\n \"A report from a specialist you started. The user hasn't seen it.\",\n started === undefined\n ? `You started it for: ${finished.goal}`\n : `You started it ${ago(at.at - started.at)} ago for: ${finished.goal}`,\n wrote ? \"The user has written since you started it.\" : \"The user hasn't written since you started it.\",\n Match.value(finished.status).pipe(\n Match.when(\"answered\", () => `Report:\\n${finished.report ?? \"\"}`),\n Match.when(\"failed\", () => `It failed: ${finished.error ?? \"\"}`),\n Match.when(\"cut_off\", () => \"A restart cut it off before it finished. Spawn it again if it's still needed.\"),\n Match.when(\"stopped\", () => \"You stopped it.\"),\n Match.exhaustive\n )\n ]\n\n return `<specialist name=\"${finished.specialist}\" run=\"${finished.runId}\" status=\"${finished.status}\">\\n${lines.join(\n \"\\n\"\n )}\\n</specialist>`\n}\n\nconst answerText = (answer: AssistantEvent.BlockAnswer) =>\n `<answer message=\"${answer.messageId}\" kind=\"${answer.kind}\">\\nThe user answered with the block on your message ${answer.messageId}.\\n${toJsonText(answer.value)}\\n</answer>`\n\n/** @internal */\nexport const parkedResult = (\n request: AssistantEvent.DecisionRequested,\n origin: AssistantEvent.CallOrigin\n): Prompt.ToolMessage =>\n Prompt.toolMessage({\n content: [\n Prompt.toolResultPart({\n id: origin.callId,\n name: origin.tool,\n isFailure: false,\n result: {\n status: \"waiting_for_decision\",\n decisionId: request.id,\n next: \"Not run: it runs only if the user approves it. Send them a message with a decision block for this decisionId that says what they approve (the call's key facts). Their answer runs it, or declines it.\"\n },\n providerExecuted: false\n })\n ]\n })\n\nconst whatDecided = (request: AssistantEvent.DecisionRequested, outcome: AssistantEvent.DecisionOutcome) =>\n outcome === \"approved\"\n ? `approved: ${request.origin.tool} runs now, and its result follows.`\n : `declined: ${request.origin.tool} didn't run, and won't.`\n\nconst decodeDecisionAnswer = Schema.decodeUnknownOption(Decision.Answer)\n\nconst decisionAnswerText = (\n answer: AssistantEvent.BlockAnswer,\n requests: ReadonlyMap<string, AssistantEvent.DecisionRequested>\n) =>\n Option.match(decodeDecisionAnswer(answer.value), {\n onNone: () => answerText(answer),\n onSome: ({ decisions }) => {\n const lines = Object.entries(decisions).map(([id, outcome]) => {\n const request = requests.get(id)\n\n return `- ${id}: ${request === undefined ? outcome : whatDecided(request, outcome)}`\n })\n\n return `<answer message=\"${answer.messageId}\" kind=\"${answer.kind}\">\\nThe user decided with the block on your message ${answer.messageId}:\\n${lines.join(\"\\n\")}\\n</answer>`\n }\n })\n\nconst resultText = (result: AssistantEvent.ToolResult) =>\n `<result tool=\"${result.tool}\" call=\"${result.callId}\" outcome=\"${result.outcome}\">\\nThe result of a call from earlier in this chat.\\n${toJsonText(result.result)}\\n</result>`\n\nconst approvedResultText = (\n request: AssistantEvent.DecisionRequested,\n tool: string,\n result: AssistantEvent.ToolResult\n) =>\n `<decision id=\"${request.id}\" tool=\"${tool}\" outcome=\"approved\">\\nThe user approved it, and ${tool} ran: ${result.outcome}.\\n${toJsonText(result.result)}\\n</decision>`\n\n// In the order the model saw the entries, not the order they were stored.\n/** @internal */\nexport const fromEntries = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n readApp: (event: { readonly _tag: string }) => string | undefined\n): ReadonlyArray<Prompt.Message> => {\n const history: Array<Prompt.Message> = []\n const held: Array<Prompt.Message> = []\n const open = new Set<string>()\n const requests = new Map<string, AssistantEvent.DecisionRequested>()\n // Parked calls by call id: their result came with the parking, so a later one reads as the decision's.\n const parked = new Map<string, AssistantEvent.DecisionRequested>()\n const called = new Set<string>()\n\n const flushIfClosed = () => {\n if (open.size === 0) history.push(...held.splice(0))\n }\n\n const nextSeen = (from: number) => {\n const next = entries.slice(from + 1).find(({ event }) => Predicate.isTagged(event, \"ModelOutput\"))?.event\n\n return next !== undefined && AssistantEvent.isLibraryEvent(next) && Predicate.isTagged(next, \"ModelOutput\")\n ? next.seenThroughSeq\n : undefined\n }\n\n const input = (index: number, item: Prompt.Message) => {\n const seen = nextSeen(index)\n\n if (open.size > 0 || (seen !== undefined && seen < entries[index]!.seq)) held.push(item)\n else history.push(item)\n }\n\n entries.forEach(({ event }, index) => {\n if (!AssistantEvent.isLibraryEvent(event)) {\n const text = readApp(event)\n\n if (text !== undefined) input(index, userText(text))\n\n return\n }\n\n Match.value(event).pipe(\n Match.tagsExhaustive({\n UserMessage: (message) =>\n input(index, userText(AssistantEvent.textWithAttachments(message.text, message.attachments))),\n BlockAnswer: (answer) =>\n input(\n index,\n userText(answer.kind === Decision.kind ? decisionAnswerText(answer, requests) : answerText(answer))\n ),\n ModelOutput: (output) => {\n history.push(...Prompt.fromResponseParts(output.parts).content)\n\n for (const part of output.parts) {\n if (part.type === \"tool-call\") {\n open.add(part.id)\n called.add(part.id)\n }\n }\n\n flushIfClosed()\n },\n ToolResult: (result) => {\n const request = parked.get(result.callId)\n\n if (request !== undefined) return input(index, userText(approvedResultText(request, result.tool, result)))\n\n // A view that starts after the call (since a summary, say) keeps its result as text: a tool result needs its\n // call right before it. An approved call's result can come long after the call was parked.\n if (!called.has(result.callId)) return input(index, userText(resultText(result)))\n\n history.push(toolMessage(result))\n open.delete(result.callId)\n flushIfClosed()\n },\n DecisionRequested: (request) => {\n requests.set(request.id, request)\n parked.set(request.origin.callId, request)\n history.push(parkedResult(request, request.origin))\n open.delete(request.origin.callId)\n flushIfClosed()\n },\n DecisionResolved: () => undefined,\n AssistantMessage: () => undefined,\n ToolStarted: () => undefined,\n TurnStarted: () => undefined,\n TurnCompleted: () => undefined,\n TurnFailed: () => undefined,\n SpecialistFinished: (finished) => input(index, userText(finishedText(entries, index, finished))),\n // Every run starts in a call of the model's own (a spawn or a follow-up), whose result says so.\n SpecialistStarted: () => undefined,\n BlocksClosed: () => undefined,\n ChatDeleted: () => undefined\n })\n )\n })\n\n return [...history, ...held]\n}\n\n/** @internal */\nexport const isInput = (event: { readonly _tag: string }, wakes: ReadonlySet<string>): boolean =>\n AssistantEvent.isInput(event) || (!AssistantEvent.isLibraryEvent(event) && wakes.has(event._tag))\n\n/** @internal */\nexport const handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): number => {\n const last = entries.findLast(({ event }) => AssistantEvent.isTurnEnd(event))?.event\n\n return last !== undefined && AssistantEvent.isTurnEnd(last) ? last.throughSeq : 0\n}\n\n// A run the assistant stopped isn't an input here: it wakes nothing.\n/** @internal */\nexport const unhandledInputs = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n wakes: ReadonlySet<string>\n): ReadonlyArray<{ readonly _tag: string }> => {\n const seen = handledThrough(entries)\n\n return entries.flatMap(({ event, seq }) =>\n seq > seen && isInput(event, wakes) && !(AssistantEvent.isInput(event) && !AssistantEvent.isWaking(event))\n ? [event]\n : []\n )\n}\n\n/** @internal */\nexport const needsTurn = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, wakes: ReadonlySet<string>): boolean =>\n unhandledInputs(entries, wakes).length > 0\n\n// A failure the user was shown counts as a reply.\n/** @internal */\nexport const owesReply = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSeq: number): boolean => {\n const latest = entries.findLast(\n ({ event, seq }) => seq <= throughSeq && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)\n )\n\n return (\n latest !== undefined &&\n !entries.some(\n ({ event, seq }) =>\n seq > latest.seq && (Predicate.isTagged(event, \"AssistantMessage\") || Predicate.isTagged(event, \"TurnFailed\"))\n )\n )\n}\n\nconst sentToWaitOn = Schema.decodeUnknownOption(Schema.Struct({ wait: Schema.Literal(false) }))\n\n// Messages sent with `wait: false` are progress while the model kept working, like \"On it\".\nconst progressMessages = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlySet<string> =>\n new Set(\n entries.flatMap(({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"ModelOutput\")\n ? event.parts.flatMap((part) =>\n part.type === \"tool-call\" && part.name === \"send_user_message\" && Option.isSome(sentToWaitOn(part.params))\n ? [Uuid.fromKey(`message:${part.id}`)]\n : []\n )\n : []\n )\n )\n\n// The model said it was on it and stopped without the result: only progress messages since the user's latest input.\n// A specialist still working doesn't count, since its report is what comes next.\n/** @internal */\nexport const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSeq: number): boolean => {\n const latest = entries.findLast(\n ({ event, seq }) => seq <= throughSeq && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)\n )\n\n if (latest === undefined) return false\n\n const after = entries.flatMap(({ seq, event }) =>\n seq > latest.seq && AssistantEvent.isLibraryEvent(event) ? [event] : []\n )\n\n const messages = after.flatMap((event) => (Predicate.isTagged(event, \"AssistantMessage\") ? [event] : []))\n const progress = progressMessages(entries)\n const started = after.flatMap((event) => (Predicate.isTagged(event, \"SpecialistStarted\") ? [event.runId] : []))\n\n const finished = new Set(\n entries.flatMap(({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"SpecialistFinished\") ? [event.runId] : []\n )\n )\n\n return (\n messages.length > 0 &&\n messages.every((message) => message.block === undefined && progress.has(message.id)) &&\n !after.some((event) => Predicate.isTagged(event, \"TurnFailed\")) &&\n started.every((runId) => finished.has(runId))\n )\n}\n\n/** @internal */\nexport const unreadAfter = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n throughSeq: number,\n wakes: ReadonlySet<string>\n): boolean => entries.some(({ event, seq }) => seq > throughSeq && isInput(event, wakes))\n"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as Predicate from "effect/Predicate";
|
|
2
|
+
//#region src/internal/instructions.ts
|
|
3
|
+
/** @internal */
|
|
4
|
+
const mapText = (instructions, f) => Predicate.isString(instructions) ? { text: f(instructions) } : {
|
|
5
|
+
...instructions,
|
|
6
|
+
text: f(instructions.text)
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
export { mapText };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=instructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.js","names":[],"sources":["../../src/internal/instructions.ts"],"sourcesContent":["import * as Predicate from \"effect/Predicate\"\nimport type * as Instructions from \"../Instructions.ts\"\n\n/** @internal */\nexport const mapText = (\n instructions: string | Instructions.Resolved,\n f: (text: string) => string\n): Instructions.Resolved =>\n Predicate.isString(instructions) ? { text: f(instructions) } : { ...instructions, text: f(instructions.text) }\n"],"mappings":";;;AAIA,MAAa,WACX,cACA,MAEA,UAAU,SAAS,YAAY,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI;CAAE,GAAG;CAAc,MAAM,EAAE,aAAa,IAAI;AAAE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { AssistantEvent_d_exports, DecisionRequested, ModelOutput, ToolResult, ToolStarted } from "../AssistantEvent.js";
|
|
2
|
+
import { Instructions_d_exports, Resolved } from "../Instructions.js";
|
|
3
|
+
import { Tools, toolkit_d_exports } from "./toolkit.js";
|
|
4
|
+
import * as Context from "effect/Context";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Stream from "effect/Stream";
|
|
7
|
+
import * as Data from "effect/Data";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
import * as Prompt from "effect/ai/Prompt";
|
|
10
|
+
import * as LanguageModel from "effect/ai/LanguageModel";
|
|
11
|
+
import * as AiError from "effect/ai/AiError";
|
|
12
|
+
declare namespace loop_d_exports {
|
|
13
|
+
export { ChatHistory, Event, Options, Step, StepOptions, StepResult, TurnTools, loop, next, run, step, stop, value };
|
|
14
|
+
}
|
|
15
|
+
/** @internal */
|
|
16
|
+
export type Event = ModelOutput | ToolStarted | ToolResult | DecisionRequested;
|
|
17
|
+
/** @internal */
|
|
18
|
+
export declare const TurnTools: Context.Reference<Option.Option<Tools<never>>>;
|
|
19
|
+
/** @internal */
|
|
20
|
+
export interface ChatHistory {
|
|
21
|
+
readonly messages: ReadonlyArray<Prompt.Message>;
|
|
22
|
+
readonly throughSeq: number;
|
|
23
|
+
}
|
|
24
|
+
/** @internal */
|
|
25
|
+
export interface StepOptions<R = never> {
|
|
26
|
+
readonly instructions: string | Resolved;
|
|
27
|
+
readonly toolkit?: Tools<R> | undefined;
|
|
28
|
+
readonly history: Effect.Effect<ReadonlyArray<Prompt.Message> | ChatHistory>;
|
|
29
|
+
readonly toolChoice?: LanguageModel.ToolChoice<string> | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** @internal */
|
|
32
|
+
export interface Options<R = never> extends StepOptions<R> {
|
|
33
|
+
readonly maxSteps?: number | undefined;
|
|
34
|
+
}
|
|
35
|
+
/** @internal */
|
|
36
|
+
export interface StepResult {
|
|
37
|
+
/** The model's output; unset when the step only ran calls a crash cut off. */
|
|
38
|
+
readonly output: ModelOutput | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the model is done: it made no tool calls, or ended its turn (`wait`, or a message
|
|
41
|
+
* with `wait: true`) and none of the step's calls failed.
|
|
42
|
+
*/
|
|
43
|
+
readonly done: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The history's length with this step's output and results: a longer one got something new meanwhile.
|
|
46
|
+
*/
|
|
47
|
+
readonly historyLength: number;
|
|
48
|
+
}
|
|
49
|
+
/** @internal */
|
|
50
|
+
export type Step<A, S> = Data.TaggedEnum<{
|
|
51
|
+
Value: {
|
|
52
|
+
readonly value: A;
|
|
53
|
+
};
|
|
54
|
+
Next: {
|
|
55
|
+
readonly state: S;
|
|
56
|
+
};
|
|
57
|
+
Stop: Record<never, never>;
|
|
58
|
+
}>;
|
|
59
|
+
/** @internal */
|
|
60
|
+
export declare const value: <A>(value: A) => Step<A, never>;
|
|
61
|
+
/** @internal */
|
|
62
|
+
export declare const next: <S>(state: S) => Stream.Stream<Step<never, S>>;
|
|
63
|
+
/** @internal */
|
|
64
|
+
export declare const stop: <S = never>() => Stream.Stream<Step<never, S>>;
|
|
65
|
+
/** @internal */
|
|
66
|
+
export declare const loop: <S, A, E, R>(initial: S, body: (state: S) => Stream.Stream<Step<A, S>, E, R>) => Stream.Stream<A, E, R>;
|
|
67
|
+
/** @internal */
|
|
68
|
+
export declare const step: <R, S, E2 = never, R2 = never>(options: StepOptions<R>, then: (result: StepResult) => Stream.Stream<Step<never, S>, E2, R2>) => Stream.Stream<Step<Event, S>, AiError.AiError | E2, LanguageModel.LanguageModel | R | R2>;
|
|
69
|
+
/** @internal */
|
|
70
|
+
export declare const run: <R = never>(options: Options<R>) => Stream.Stream<Event, AiError.AiError, LanguageModel.LanguageModel | R>;
|
|
71
|
+
//#endregion
|
|
72
|
+
export { loop_d_exports };
|
|
73
|
+
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.d.ts","names":[],"sources":["../../src/internal/loop.ts"],"mappings":";;;;;;;;;;;;;;;YAyBY,QACR,cACA,cACA,aACA;;qBAKS,WAAS,QAAA,UAAA,OAAA,OAAA;;iBAKL;WACN,UAAU,cAAc,OAAO;WAC/B;;;iBAUM,YAAY;WAClB,uBAAuB;WACvB,UAAU,MAAe;WACzB,SAAS,OAAO,OAAO,cAAc,OAAO,WAAW;WACvD,aAAa,cAAc;;;iBAIrB,QAAQ,mBAAmB,YAAY;WAC7C;;;iBAKM;;WAEN,QAAQ;;;;;WAKR;;;;WAIA;;;YAmHC,KAAK,GAAG,KAAK,KAAK;EAC5B;aAAkB,OAAO;;EACzB;aAAiB,OAAO;;EACxB,MAAM;;;qBAUK,QAAS,GAAC,OAAS,MAAI,KAAK;;qBAG5B,OAAQ,GAAC,OAAS,MAAI,OAAO,OAAO,YAAY;;qBAIhD,OAAQ,gBAAc,OAAO,OAAO,YAAY;;qBAOhD,OAAQ,GAAG,GAAG,GAAG,GAAC,SACpB,GAAC,OACH,OAAO,MAAM,OAAO,OAAO,KAAK,GAAG,IAAI,GAAG,OAChD,OAAO,OAAO,GAAG,GAAG;;qBAmBV,OAAQ,GAAG,GAAG,YAAY,YAAU,SACtC,YAAY,IAAE,OAChB,QAAQ,eAAe,OAAO,OAAO,YAAY,IAAI,IAAI,QAC/D,OAAO,OAAO,KAAK,OAAO,IAAI,QAAQ,UAAU,IAAI,cAAc,gBAAgB,IAAI;;qBA4E5E,MAAO,WAAS,SAClB,QAAQ,OAChB,OAAO,OAAO,OAAO,QAAQ,SAAS,cAAc,gBAAgB"}
|