@yolk-sdk/agent 0.0.1-canary.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 +93 -0
- package/dist/client/index.d.mts +3 -0
- package/dist/client/index.mjs +3 -0
- package/dist/client/state.d.mts +99 -0
- package/dist/client/state.d.mts.map +1 -0
- package/dist/client/state.mjs +245 -0
- package/dist/client/state.mjs.map +1 -0
- package/dist/client/transport.d.mts +67 -0
- package/dist/client/transport.d.mts.map +1 -0
- package/dist/client/transport.mjs +219 -0
- package/dist/client/transport.mjs.map +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/loop/accumulator.d.mts +11 -0
- package/dist/loop/accumulator.d.mts.map +1 -0
- package/dist/loop/accumulator.mjs +40 -0
- package/dist/loop/accumulator.mjs.map +1 -0
- package/dist/loop/error.d.mts +36 -0
- package/dist/loop/error.d.mts.map +1 -0
- package/dist/loop/error.mjs +84 -0
- package/dist/loop/error.mjs.map +1 -0
- package/dist/loop/index.d.mts +9 -0
- package/dist/loop/index.mjs +9 -0
- package/dist/loop/llm-event.d.mts +44 -0
- package/dist/loop/llm-event.d.mts.map +1 -0
- package/dist/loop/llm-event.mjs +34 -0
- package/dist/loop/llm-event.mjs.map +1 -0
- package/dist/loop/run.d.mts +37 -0
- package/dist/loop/run.d.mts.map +1 -0
- package/dist/loop/run.mjs +624 -0
- package/dist/loop/run.mjs.map +1 -0
- package/dist/loop/services/context-transformer.d.mts +18 -0
- package/dist/loop/services/context-transformer.d.mts.map +1 -0
- package/dist/loop/services/context-transformer.mjs +12 -0
- package/dist/loop/services/context-transformer.mjs.map +1 -0
- package/dist/loop/services/llm-provider.d.mts +20 -0
- package/dist/loop/services/llm-provider.d.mts.map +1 -0
- package/dist/loop/services/llm-provider.mjs +7 -0
- package/dist/loop/services/llm-provider.mjs.map +1 -0
- package/dist/loop/services/loop-config.d.mts +17 -0
- package/dist/loop/services/loop-config.d.mts.map +1 -0
- package/dist/loop/services/loop-config.mjs +15 -0
- package/dist/loop/services/loop-config.mjs.map +1 -0
- package/dist/loop/services/tool-executor.d.mts +12 -0
- package/dist/loop/services/tool-executor.d.mts.map +1 -0
- package/dist/loop/services/tool-executor.mjs +7 -0
- package/dist/loop/services/tool-executor.mjs.map +1 -0
- package/dist/loop/testing/faux-provider.d.mts +31 -0
- package/dist/loop/testing/faux-provider.d.mts.map +1 -0
- package/dist/loop/testing/faux-provider.mjs +47 -0
- package/dist/loop/testing/faux-provider.mjs.map +1 -0
- package/dist/loop/testing/index.d.mts +3 -0
- package/dist/loop/testing/index.mjs +3 -0
- package/dist/loop/testing/test-tool-executor.d.mts +10 -0
- package/dist/loop/testing/test-tool-executor.d.mts.map +1 -0
- package/dist/loop/testing/test-tool-executor.mjs +21 -0
- package/dist/loop/testing/test-tool-executor.mjs.map +1 -0
- package/dist/protocol/capability.d.mts +20 -0
- package/dist/protocol/capability.d.mts.map +1 -0
- package/dist/protocol/capability.mjs +34 -0
- package/dist/protocol/capability.mjs.map +1 -0
- package/dist/protocol/content.d.mts +31 -0
- package/dist/protocol/content.d.mts.map +1 -0
- package/dist/protocol/content.mjs +52 -0
- package/dist/protocol/content.mjs.map +1 -0
- package/dist/protocol/event.d.mts +228 -0
- package/dist/protocol/event.d.mts.map +1 -0
- package/dist/protocol/event.mjs +217 -0
- package/dist/protocol/event.mjs.map +1 -0
- package/dist/protocol/index.d.mts +14 -0
- package/dist/protocol/index.d.mts.map +1 -0
- package/dist/protocol/index.mjs +9 -0
- package/dist/protocol/message.d.mts +53 -0
- package/dist/protocol/message.d.mts.map +1 -0
- package/dist/protocol/message.mjs +49 -0
- package/dist/protocol/message.mjs.map +1 -0
- package/dist/protocol/reasoning.d.mts +8 -0
- package/dist/protocol/reasoning.d.mts.map +1 -0
- package/dist/protocol/reasoning.mjs +13 -0
- package/dist/protocol/reasoning.mjs.map +1 -0
- package/dist/protocol/session.d.mts +39 -0
- package/dist/protocol/session.d.mts.map +1 -0
- package/dist/protocol/session.mjs +38 -0
- package/dist/protocol/session.mjs.map +1 -0
- package/dist/protocol/tool.d.mts +101 -0
- package/dist/protocol/tool.d.mts.map +1 -0
- package/dist/protocol/tool.mjs +102 -0
- package/dist/protocol/tool.mjs.map +1 -0
- package/dist/protocol/usage.d.mts +26 -0
- package/dist/protocol/usage.d.mts.map +1 -0
- package/dist/protocol/usage.mjs +40 -0
- package/dist/protocol/usage.mjs.map +1 -0
- package/dist/runtime/error.d.mts +29 -0
- package/dist/runtime/error.d.mts.map +1 -0
- package/dist/runtime/error.mjs +46 -0
- package/dist/runtime/error.mjs.map +1 -0
- package/dist/runtime/index.d.mts +9 -0
- package/dist/runtime/index.d.mts.map +1 -0
- package/dist/runtime/index.mjs +4 -0
- package/dist/runtime/run-runtime.d.mts +47 -0
- package/dist/runtime/run-runtime.d.mts.map +1 -0
- package/dist/runtime/run-runtime.mjs +112 -0
- package/dist/runtime/run-runtime.mjs.map +1 -0
- package/dist/runtime/session-event-store.d.mts +75 -0
- package/dist/runtime/session-event-store.d.mts.map +1 -0
- package/dist/runtime/session-event-store.mjs +124 -0
- package/dist/runtime/session-event-store.mjs.map +1 -0
- package/dist/tools/index.d.mts +4 -0
- package/dist/tools/index.mjs +4 -0
- package/dist/tools/question.d.mts +21 -0
- package/dist/tools/question.d.mts.map +1 -0
- package/dist/tools/question.mjs +41 -0
- package/dist/tools/question.mjs.map +1 -0
- package/dist/tools/registry.d.mts +61 -0
- package/dist/tools/registry.d.mts.map +1 -0
- package/dist/tools/registry.mjs +113 -0
- package/dist/tools/registry.mjs.map +1 -0
- package/dist/tools/task.d.mts +34 -0
- package/dist/tools/task.d.mts.map +1 -0
- package/dist/tools/task.mjs +81 -0
- package/dist/tools/task.mjs.map +1 -0
- package/package.json +86 -0
- package/src/client/README.md +23 -0
- package/src/client/index.ts +43 -0
- package/src/client/state.ts +380 -0
- package/src/client/transport.ts +517 -0
- package/src/index.ts +2 -0
- package/src/loop/README.md +23 -0
- package/src/loop/accumulator.ts +71 -0
- package/src/loop/error.ts +105 -0
- package/src/loop/index.ts +35 -0
- package/src/loop/llm-event.ts +52 -0
- package/src/loop/run.ts +1237 -0
- package/src/loop/services/context-transformer.ts +24 -0
- package/src/loop/services/llm-provider.ts +20 -0
- package/src/loop/services/loop-config.ts +20 -0
- package/src/loop/services/tool-executor.ts +11 -0
- package/src/loop/testing/faux-provider.ts +94 -0
- package/src/loop/testing/index.ts +3 -0
- package/src/loop/testing/test-tool-executor.ts +28 -0
- package/src/protocol/README.md +24 -0
- package/src/protocol/capability.ts +29 -0
- package/src/protocol/content.ts +76 -0
- package/src/protocol/event.ts +286 -0
- package/src/protocol/index.ts +109 -0
- package/src/protocol/message.ts +86 -0
- package/src/protocol/reasoning.ts +4 -0
- package/src/protocol/session.ts +47 -0
- package/src/protocol/tool.ts +154 -0
- package/src/protocol/usage.ts +48 -0
- package/src/runtime/README.md +44 -0
- package/src/runtime/error.ts +70 -0
- package/src/runtime/index.ts +43 -0
- package/src/runtime/run-runtime.ts +307 -0
- package/src/runtime/session-event-store.ts +254 -0
- package/src/tools/README.md +22 -0
- package/src/tools/index.ts +29 -0
- package/src/tools/question.ts +58 -0
- package/src/tools/registry.ts +228 -0
- package/src/tools/task.ts +132 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AgentLoopError } from "./error.mjs";
|
|
2
|
+
import { ContextTransformer } from "./services/context-transformer.mjs";
|
|
3
|
+
import { LLMProvider } from "./services/llm-provider.mjs";
|
|
4
|
+
import { LoopConfig } from "./services/loop-config.mjs";
|
|
5
|
+
import { ToolExecutor } from "./services/tool-executor.mjs";
|
|
6
|
+
import { Stream } from "effect";
|
|
7
|
+
import { AgentEvent, AgentMessage, AgentModelCapabilities, AgentReasoningEffort, AgentUsage, HitlResponse, ToolCall, ToolDef } from "@yolk-sdk/agent/protocol";
|
|
8
|
+
|
|
9
|
+
//#region src/loop/run.d.ts
|
|
10
|
+
type AgentLoopRunId = string;
|
|
11
|
+
type RunConfig = {
|
|
12
|
+
readonly messages: ReadonlyArray<AgentMessage>;
|
|
13
|
+
readonly systemPrompt: string;
|
|
14
|
+
readonly tools: ReadonlyArray<ToolDef>;
|
|
15
|
+
readonly hitlResponses?: ReadonlyArray<HitlResponse>;
|
|
16
|
+
readonly model: string;
|
|
17
|
+
readonly reasoningEffort?: AgentReasoningEffort;
|
|
18
|
+
readonly capabilities?: AgentModelCapabilities;
|
|
19
|
+
};
|
|
20
|
+
type ModelTurnConfig = RunConfig & {
|
|
21
|
+
readonly turn: number;
|
|
22
|
+
};
|
|
23
|
+
type ToolBatchConfig = {
|
|
24
|
+
readonly calls: ReadonlyArray<ToolCall>;
|
|
25
|
+
readonly tools?: ReadonlyArray<ToolDef>;
|
|
26
|
+
readonly hitlResponses?: ReadonlyArray<HitlResponse>;
|
|
27
|
+
readonly model?: string;
|
|
28
|
+
readonly createdMessages?: ReadonlyArray<AgentMessage>;
|
|
29
|
+
readonly turn?: number;
|
|
30
|
+
readonly usage?: AgentUsage;
|
|
31
|
+
};
|
|
32
|
+
declare const runModelTurn: (config: ModelTurnConfig) => Stream.Stream<AgentEvent, AgentLoopError, ContextTransformer | LLMProvider | LoopConfig>;
|
|
33
|
+
declare const runToolBatch: (config: ToolBatchConfig) => Stream.Stream<AgentEvent, AgentLoopError, LoopConfig | ToolExecutor>;
|
|
34
|
+
declare const run: (config: RunConfig) => Stream.Stream<AgentEvent, AgentLoopError, ContextTransformer | LLMProvider | LoopConfig | ToolExecutor>;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { AgentLoopRunId, ModelTurnConfig, RunConfig, ToolBatchConfig, run, runModelTurn, runToolBatch };
|
|
37
|
+
//# sourceMappingURL=run.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.mts","names":[],"sources":["../../src/loop/run.ts"],"mappings":";;;;;;;;;KAqEY,cAAA;AAAA,KAEA,SAAA;EAAA,SACD,QAAA,EAAU,aAAA,CAAc,YAAA;EAAA,SACxB,YAAA;EAAA,SACA,KAAA,EAAO,aAAA,CAAc,OAAA;EAAA,SACrB,aAAA,GAAgB,aAAA,CAAc,YAAA;EAAA,SAC9B,KAAA;EAAA,SACA,eAAA,GAAkB,oBAAA;EAAA,SAClB,YAAA,GAAe,sBAAA;AAAA;AAAA,KAGd,eAAA,GAAkB,SAAS;EAAA,SAC5B,IAAI;AAAA;AAAA,KAGH,eAAA;EAAA,SACD,KAAA,EAAO,aAAA,CAAc,QAAA;EAAA,SACrB,KAAA,GAAQ,aAAA,CAAc,OAAA;EAAA,SACtB,aAAA,GAAgB,aAAA,CAAc,YAAA;EAAA,SAC9B,KAAA;EAAA,SACA,eAAA,GAAkB,aAAA,CAAc,YAAA;EAAA,SAChC,IAAA;EAAA,SACA,KAAA,GAAQ,UAAA;AAAA;AAAA,cAihCN,YAAA,GACX,MAAA,EAAQ,eAAA,KACP,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,cAAA,EAAgB,kBAAA,GAAqB,WAAA,GAAc,UAAA;AAAA,cAuBnE,YAAA,GACX,MAAA,EAAQ,eAAA,KACP,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,cAAA,EAAgB,UAAA,GAAa,YAAA;AAAA,cA4C7C,GAAA,GACX,MAAA,EAAQ,SAAA,KACP,MAAA,CAAO,MAAA,CACR,UAAA,EACA,cAAA,EACA,kBAAA,GAAqB,WAAA,GAAc,UAAA,GAAa,YAAA"}
|
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import { accumulateAssistantMessage, collectToolCalls } from "./accumulator.mjs";
|
|
2
|
+
import { AbortError, LLMError, ToolError } from "./error.mjs";
|
|
3
|
+
import { ContextTransformer } from "./services/context-transformer.mjs";
|
|
4
|
+
import { LLMProvider } from "./services/llm-provider.mjs";
|
|
5
|
+
import { LoopConfig } from "./services/loop-config.mjs";
|
|
6
|
+
import { ToolExecutor } from "./services/tool-executor.mjs";
|
|
7
|
+
import { Clock, Effect, Ref, Stream } from "effect";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
|
+
import { AgentAwaitingInput, AgentEnd, AgentRetry, AgentStart, AssistantMessageEvent, LLMReasoningDelta, LLMStreamEnd, LLMStreamStart, LLMTextDelta, ProviderToolResult, QuestionAnswered, QuestionCancelled, QuestionRequest, QuestionRequested, QuestionToolParams, SubagentCompleted, SubagentStarted, ToolApprovalDenied, ToolApprovalGranted, ToolApprovalRequest, ToolApprovalRequested, ToolExecutionCompleted, ToolExecutionError, ToolExecutionStarted, ToolInputDelta, ToolInputEnd, ToolInputStart, ToolResult, ToolResultMessage, TurnEnd, TurnStart, UsageUpdate, addAgentUsage, assistantHostToolCalls, contentParts, contentPreview, formatQuestionResponseContent, zeroAgentUsage } from "@yolk-sdk/agent/protocol";
|
|
10
|
+
//#region src/loop/run.ts
|
|
11
|
+
const questionToolName = "question";
|
|
12
|
+
const objectField = (input, key) => input !== null && typeof input === "object" ? Object.getOwnPropertyDescriptor(input, key)?.value : void 0;
|
|
13
|
+
const nonEmptyStringField = (input, key) => {
|
|
14
|
+
const value = objectField(input, key);
|
|
15
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
16
|
+
};
|
|
17
|
+
const taskCallMetadata = (call) => {
|
|
18
|
+
if (call.name !== "task") return;
|
|
19
|
+
const subagentType = nonEmptyStringField(call.params, "subagent_type");
|
|
20
|
+
const description = nonEmptyStringField(call.params, "description");
|
|
21
|
+
if (subagentType === void 0 || description === void 0) return;
|
|
22
|
+
return {
|
|
23
|
+
subagentRunId: `subagent:${call.id}`,
|
|
24
|
+
subagentType,
|
|
25
|
+
description
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
const subagentStartedEvent = (input) => {
|
|
29
|
+
const metadata = taskCallMetadata(input.call);
|
|
30
|
+
return metadata === void 0 ? void 0 : SubagentStarted.make({
|
|
31
|
+
parentToolCallId: input.call.id,
|
|
32
|
+
subagentRunId: metadata.subagentRunId,
|
|
33
|
+
subagentType: metadata.subagentType,
|
|
34
|
+
description: metadata.description,
|
|
35
|
+
model: input.model,
|
|
36
|
+
createdAtMs: input.startedAtMs
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
const subagentCompletedEvent = (input) => {
|
|
40
|
+
const metadata = taskCallMetadata(input.call);
|
|
41
|
+
return metadata === void 0 ? void 0 : SubagentCompleted.make({
|
|
42
|
+
parentToolCallId: input.call.id,
|
|
43
|
+
subagentRunId: metadata.subagentRunId,
|
|
44
|
+
subagentType: metadata.subagentType,
|
|
45
|
+
description: metadata.description,
|
|
46
|
+
model: input.model,
|
|
47
|
+
status: input.result.isError === true ? "error" : "completed",
|
|
48
|
+
durationMs: Math.max(0, input.endedAtMs - input.startedAtMs),
|
|
49
|
+
summary: contentPreview(input.result.content),
|
|
50
|
+
createdAtMs: input.endedAtMs
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
const unsupportedInputError = (message) => new LLMError({
|
|
54
|
+
cause: "validation_error",
|
|
55
|
+
message,
|
|
56
|
+
retryable: false
|
|
57
|
+
});
|
|
58
|
+
const validateContent = (message, capabilities) => Effect.forEach(contentPartsFromMessage(message), (part) => {
|
|
59
|
+
switch (part._tag) {
|
|
60
|
+
case "Text": return capabilities.input.text ? Effect.void : Effect.fail(unsupportedInputError("Text input is not supported by this model"));
|
|
61
|
+
case "Image": return capabilities.input.image ? Effect.void : Effect.fail(unsupportedInputError("Image input is not supported by this model"));
|
|
62
|
+
case "Audio": return capabilities.input.audio ? Effect.void : Effect.fail(unsupportedInputError("Audio input is not supported by this model"));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
const contentPartsFromMessage = (message) => {
|
|
66
|
+
switch (message._tag) {
|
|
67
|
+
case "User":
|
|
68
|
+
case "ToolResult": return contentParts(message.content);
|
|
69
|
+
case "Assistant": return message.parts.flatMap((part) => part._tag === "Text" ? contentParts(part.content) : []);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const validateCapabilities = (config, messages) => {
|
|
73
|
+
const capabilities = config.capabilities;
|
|
74
|
+
if (capabilities === void 0) return Effect.void;
|
|
75
|
+
if (!capabilities.tools && config.tools.length > 0) return Effect.fail(unsupportedInputError("Tools are not supported by this model"));
|
|
76
|
+
if (!capabilities.reasoning && config.reasoningEffort !== void 0) return Effect.fail(unsupportedInputError("Reasoning effort is not supported by this model"));
|
|
77
|
+
return Effect.forEach(messages, (message) => validateContent(message, capabilities)).pipe(Effect.asVoid);
|
|
78
|
+
};
|
|
79
|
+
const toLlmEvent = (event) => {
|
|
80
|
+
switch (event._tag) {
|
|
81
|
+
case "TextDelta": return [LLMTextDelta.make({ text: event.text })];
|
|
82
|
+
case "ReasoningDelta": return [LLMReasoningDelta.make({ text: event.text })];
|
|
83
|
+
case "ToolCall": return [ToolInputEnd.make({ call: event.call })];
|
|
84
|
+
case "ToolInputStart": return [ToolInputStart.make({
|
|
85
|
+
id: event.id,
|
|
86
|
+
name: event.name
|
|
87
|
+
})];
|
|
88
|
+
case "ToolInputDelta": return [ToolInputDelta.make({
|
|
89
|
+
id: event.id,
|
|
90
|
+
delta: event.delta
|
|
91
|
+
})];
|
|
92
|
+
case "ProviderToolResult": return [ProviderToolResult.make({
|
|
93
|
+
call: event.call,
|
|
94
|
+
result: event.result
|
|
95
|
+
})];
|
|
96
|
+
case "Usage": return [UsageUpdate.make({ usage: event.usage })];
|
|
97
|
+
case "Done": return [];
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const isLlmEvent = (event) => {
|
|
101
|
+
switch (event._tag) {
|
|
102
|
+
case "TextDelta":
|
|
103
|
+
case "ReasoningDelta":
|
|
104
|
+
case "Done":
|
|
105
|
+
case "ToolCall":
|
|
106
|
+
case "ToolInputStart":
|
|
107
|
+
case "ToolInputDelta":
|
|
108
|
+
case "ProviderToolResult":
|
|
109
|
+
case "Usage": return true;
|
|
110
|
+
default: return false;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const retryDelayMs = (baseDelayMs, attempt) => Math.max(0, Math.floor(baseDelayMs * 2 ** Math.max(0, attempt - 1)));
|
|
114
|
+
const retryReason = (error) => error.cause;
|
|
115
|
+
const retrySleep = (delayMs) => delayMs === 0 ? Effect.void : Effect.sleep(`${delayMs} millis`);
|
|
116
|
+
const failAgentLoopError = (error) => Stream.fail(error);
|
|
117
|
+
const sleepStream = (delayMs) => Stream.fromEffect(retrySleep(delayMs)).pipe(Stream.flatMap(() => Stream.empty));
|
|
118
|
+
const withProviderRetries = (stream, loopConfig, makeStream, attempt) => Stream.unwrap(Ref.make(false).pipe(Effect.map((emittedProviderEvent) => stream.pipe(Stream.tap(() => Ref.set(emittedProviderEvent, true)), Stream.catchTags({
|
|
119
|
+
LLMError: (error) => Stream.unwrap(Ref.get(emittedProviderEvent).pipe(Effect.map((emitted) => {
|
|
120
|
+
if (emitted || !error.retryable || error.cause === "context_overflow" || attempt > loopConfig.maxRetries) return failAgentLoopError(error);
|
|
121
|
+
const delayMs = retryDelayMs(loopConfig.retryBaseDelayMs, attempt);
|
|
122
|
+
return Stream.make(AgentRetry.make({
|
|
123
|
+
attempt,
|
|
124
|
+
reason: retryReason(error),
|
|
125
|
+
delayMs,
|
|
126
|
+
message: error.message
|
|
127
|
+
})).pipe(Stream.concat(sleepStream(delayMs)), Stream.concat(withProviderRetries(makeStream(), loopConfig, makeStream, attempt + 1)));
|
|
128
|
+
}))),
|
|
129
|
+
AbortError: failAgentLoopError,
|
|
130
|
+
FauxExhaustedError: failAgentLoopError
|
|
131
|
+
})))));
|
|
132
|
+
const makeToolExecutionStream = (executor, call, model) => Stream.unwrap(Effect.gen(function* () {
|
|
133
|
+
const startedAtMs = yield* Clock.currentTimeMillis;
|
|
134
|
+
const started = subagentStartedEvent({
|
|
135
|
+
call,
|
|
136
|
+
model,
|
|
137
|
+
startedAtMs
|
|
138
|
+
});
|
|
139
|
+
const startEvents = started === void 0 ? [ToolExecutionStarted.make({
|
|
140
|
+
call,
|
|
141
|
+
createdAtMs: startedAtMs
|
|
142
|
+
})] : [ToolExecutionStarted.make({
|
|
143
|
+
call,
|
|
144
|
+
createdAtMs: startedAtMs
|
|
145
|
+
}), started];
|
|
146
|
+
return Stream.fromIterable(startEvents).pipe(Stream.concat(Stream.fromEffect(executor.execute(call).pipe(Effect.flatMap((result) => Clock.currentTimeMillis.pipe(Effect.map((endedAtMs) => {
|
|
147
|
+
const completed = subagentCompletedEvent({
|
|
148
|
+
call,
|
|
149
|
+
result,
|
|
150
|
+
model,
|
|
151
|
+
startedAtMs,
|
|
152
|
+
endedAtMs
|
|
153
|
+
});
|
|
154
|
+
const toolCompleted = ToolExecutionCompleted.make({
|
|
155
|
+
call,
|
|
156
|
+
result,
|
|
157
|
+
createdAtMs: endedAtMs
|
|
158
|
+
});
|
|
159
|
+
return completed === void 0 ? [toolCompleted] : [toolCompleted, completed];
|
|
160
|
+
}))))).pipe(Stream.flatMap(Stream.fromIterable), Stream.catchTag("ToolError", (error) => Stream.fromEffect(Clock.currentTimeMillis).pipe(Stream.flatMap((endedAtMs) => Stream.make(ToolExecutionError.make({
|
|
161
|
+
call,
|
|
162
|
+
message: error.message,
|
|
163
|
+
code: toolErrorCode(error),
|
|
164
|
+
createdAtMs: endedAtMs
|
|
165
|
+
}))), Stream.concat(Stream.fail(error)))))));
|
|
166
|
+
}));
|
|
167
|
+
const boundedToolConcurrency = (loopConfig) => Math.max(1, loopConfig.toolConcurrency);
|
|
168
|
+
const toolResultMessageFromResult = (result) => ToolResultMessage.make({
|
|
169
|
+
toolCallId: result.toolCallId,
|
|
170
|
+
content: result.content,
|
|
171
|
+
isError: result.isError,
|
|
172
|
+
structuredContent: result.structuredContent
|
|
173
|
+
});
|
|
174
|
+
const toolDefFor = (tools, call) => tools.find((tool) => tool.name === call.name);
|
|
175
|
+
const approvalRequired = (tools, call) => toolDefFor(tools, call)?.approval?.mode === "manual";
|
|
176
|
+
const approvalRequestId = (call) => `approval:${call.id}`;
|
|
177
|
+
const questionRequestId = (call) => `question:${call.id}`;
|
|
178
|
+
const matchesApproval = (response, call) => response.toolCallId === call.id || response.requestId === approvalRequestId(call);
|
|
179
|
+
const matchesQuestion = (response, call) => response.toolCallId === call.id || response.requestId === questionRequestId(call);
|
|
180
|
+
const approvalResponseFor = (responses, call) => responses.flatMap((response) => response._tag === "ToolApprovalResponse" && matchesApproval(response, call) ? [response] : [])[0];
|
|
181
|
+
const questionResponseFor = (responses, call) => responses.flatMap((response) => response._tag === "QuestionResponse" && matchesQuestion(response, call) ? [response] : [])[0];
|
|
182
|
+
const toolApprovalRequest = (tools, call) => ToolApprovalRequest.make({
|
|
183
|
+
requestId: approvalRequestId(call),
|
|
184
|
+
toolCallId: call.id,
|
|
185
|
+
call,
|
|
186
|
+
policy: toolDefFor(tools, call)?.approval
|
|
187
|
+
});
|
|
188
|
+
const deniedToolResult = (call, response) => {
|
|
189
|
+
const reason = response.reason ?? "Denied by user";
|
|
190
|
+
return ToolResult.make({
|
|
191
|
+
toolCallId: call.id,
|
|
192
|
+
content: `Tool call denied: ${reason}`,
|
|
193
|
+
isError: true,
|
|
194
|
+
structuredContent: {
|
|
195
|
+
type: "tool_approval_denied",
|
|
196
|
+
reason,
|
|
197
|
+
source: response.source
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
const questionToolResult = (response, questions) => {
|
|
202
|
+
return ToolResult.make({
|
|
203
|
+
toolCallId: response.toolCallId,
|
|
204
|
+
content: formatQuestionResponseContent(response, questions),
|
|
205
|
+
isError: response.outcome === "cancelled" ? true : void 0,
|
|
206
|
+
structuredContent: {
|
|
207
|
+
type: "question_response",
|
|
208
|
+
outcome: response.outcome,
|
|
209
|
+
answers: response.answers ?? [],
|
|
210
|
+
reason: response.reason,
|
|
211
|
+
source: response.source
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
const invalidQuestionToolResult = (call) => ToolResult.make({
|
|
216
|
+
toolCallId: call.id,
|
|
217
|
+
content: "Invalid question arguments.",
|
|
218
|
+
isError: true,
|
|
219
|
+
structuredContent: { type: "question_invalid" }
|
|
220
|
+
});
|
|
221
|
+
const prepareQuestionCall = (call, index, responses) => Effect.gen(function* () {
|
|
222
|
+
const decoded = yield* Schema.decodeUnknownEffect(QuestionToolParams)(call.params).pipe(Effect.result);
|
|
223
|
+
if (decoded._tag === "Failure") return {
|
|
224
|
+
_tag: "Result",
|
|
225
|
+
index,
|
|
226
|
+
call,
|
|
227
|
+
result: invalidQuestionToolResult(call),
|
|
228
|
+
events: []
|
|
229
|
+
};
|
|
230
|
+
const response = questionResponseFor(responses, call);
|
|
231
|
+
if (response !== void 0) return {
|
|
232
|
+
_tag: "Result",
|
|
233
|
+
index,
|
|
234
|
+
call,
|
|
235
|
+
result: questionToolResult(response, decoded.success.questions),
|
|
236
|
+
events: [response.outcome === "answered" ? QuestionAnswered.make({ response }) : QuestionCancelled.make({ response })]
|
|
237
|
+
};
|
|
238
|
+
const request = QuestionRequest.make({
|
|
239
|
+
requestId: questionRequestId(call),
|
|
240
|
+
toolCallId: call.id,
|
|
241
|
+
call,
|
|
242
|
+
questions: decoded.success.questions
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
_tag: "Pending",
|
|
246
|
+
request,
|
|
247
|
+
events: [QuestionRequested.make({ request })]
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
const prepareApprovalCall = (tools, call, index, responses) => {
|
|
251
|
+
if (!approvalRequired(tools, call)) return {
|
|
252
|
+
_tag: "Execute",
|
|
253
|
+
index,
|
|
254
|
+
call,
|
|
255
|
+
events: []
|
|
256
|
+
};
|
|
257
|
+
const request = toolApprovalRequest(tools, call);
|
|
258
|
+
const response = approvalResponseFor(responses, call);
|
|
259
|
+
if (response === void 0) return {
|
|
260
|
+
_tag: "Pending",
|
|
261
|
+
request,
|
|
262
|
+
events: [ToolApprovalRequested.make({
|
|
263
|
+
call,
|
|
264
|
+
request
|
|
265
|
+
})]
|
|
266
|
+
};
|
|
267
|
+
if (response.decision === "denied") return {
|
|
268
|
+
_tag: "Result",
|
|
269
|
+
index,
|
|
270
|
+
call,
|
|
271
|
+
result: deniedToolResult(call, response),
|
|
272
|
+
events: [ToolApprovalDenied.make({
|
|
273
|
+
toolCallId: call.id,
|
|
274
|
+
reason: response.reason ?? "Denied by user",
|
|
275
|
+
response
|
|
276
|
+
})]
|
|
277
|
+
};
|
|
278
|
+
return {
|
|
279
|
+
_tag: "Execute",
|
|
280
|
+
index,
|
|
281
|
+
call,
|
|
282
|
+
events: [ToolApprovalGranted.make({
|
|
283
|
+
toolCallId: call.id,
|
|
284
|
+
response
|
|
285
|
+
})]
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
const prepareToolCall = (input) => input.call.name === questionToolName ? prepareQuestionCall(input.call, input.index, input.responses) : Effect.succeed(prepareApprovalCall(input.tools, input.call, input.index, input.responses));
|
|
289
|
+
const prepareToolBatch = (input) => Effect.gen(function* () {
|
|
290
|
+
const prepared = yield* Effect.forEach(input.calls, (call, index) => prepareToolCall({
|
|
291
|
+
tools: input.tools,
|
|
292
|
+
responses: input.responses,
|
|
293
|
+
call,
|
|
294
|
+
index
|
|
295
|
+
}));
|
|
296
|
+
return {
|
|
297
|
+
callsToExecute: prepared.flatMap((item) => item._tag === "Execute" ? [{
|
|
298
|
+
index: item.index,
|
|
299
|
+
call: item.call
|
|
300
|
+
}] : []),
|
|
301
|
+
resultMessages: prepared.flatMap((item) => item._tag === "Result" ? [{
|
|
302
|
+
index: item.index,
|
|
303
|
+
message: toolResultMessageFromResult(item.result)
|
|
304
|
+
}] : []),
|
|
305
|
+
resultEvents: syntheticToolCompletionEvents(prepared),
|
|
306
|
+
events: prepared.flatMap((item) => item._tag === "Pending" ? [] : item.events),
|
|
307
|
+
pendingRequests: prepared.flatMap((item) => item._tag === "Pending" ? [item.request] : []),
|
|
308
|
+
pendingEvents: prepared.flatMap((item) => item._tag === "Pending" ? item.events : [])
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
const orderedToolResultMessages = (results) => [...results].sort((left, right) => left.index - right.index).map((result) => result.message);
|
|
312
|
+
const syntheticToolCompletionEvents = (prepared) => prepared.flatMap((item) => item._tag === "Result" ? [ToolExecutionCompleted.make({
|
|
313
|
+
call: item.call,
|
|
314
|
+
result: item.result
|
|
315
|
+
})] : []);
|
|
316
|
+
const toolResultIds = (messages) => new Set(messages.flatMap((message) => message._tag === "ToolResult" ? [message.toolCallId] : []));
|
|
317
|
+
const pendingHostToolCalls = (messages) => {
|
|
318
|
+
const completed = toolResultIds(messages);
|
|
319
|
+
return messages.flatMap((message) => message._tag === "Assistant" ? assistantHostToolCalls(message).filter((call) => !completed.has(call.id)) : []);
|
|
320
|
+
};
|
|
321
|
+
const nonEmptyHitlRequests = (requests) => {
|
|
322
|
+
const first = requests[0];
|
|
323
|
+
return first === void 0 ? void 0 : [first, ...requests.slice(1)];
|
|
324
|
+
};
|
|
325
|
+
const parallelToolExecutionStream = (input) => Stream.mergeAll(input.calls.map(({ call, index }) => makeToolExecutionStream(input.executor, call, input.model).pipe(Stream.tap((event) => {
|
|
326
|
+
if (event._tag !== "ToolExecutionCompleted") return Effect.void;
|
|
327
|
+
return Ref.update(input.results, (results) => [...results, {
|
|
328
|
+
index,
|
|
329
|
+
message: toolResultMessageFromResult(event.result)
|
|
330
|
+
}]);
|
|
331
|
+
}))), { concurrency: boundedToolConcurrency(input.loopConfig) });
|
|
332
|
+
const toolErrorCode = (error) => {
|
|
333
|
+
switch (error.cause) {
|
|
334
|
+
case "validation":
|
|
335
|
+
case "invalid_input": return "validation_error";
|
|
336
|
+
case "timeout": return "tool_timeout";
|
|
337
|
+
case "permission":
|
|
338
|
+
case "denied": return "tool_denied";
|
|
339
|
+
case "execution":
|
|
340
|
+
case "not_found":
|
|
341
|
+
case "unavailable": return "tool_error";
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
const validateTurnCompletion = (events) => {
|
|
345
|
+
const doneEvents = events.filter((event) => event._tag === "Done");
|
|
346
|
+
const toolCalls = collectToolCalls(events);
|
|
347
|
+
const stopReason = toolCalls.length === 0 ? "stop" : "tool_use";
|
|
348
|
+
if (doneEvents.length !== 1) return Effect.fail(new LLMError({
|
|
349
|
+
cause: "invalid_response",
|
|
350
|
+
message: `Expected exactly one LLM done event, received ${doneEvents.length}`,
|
|
351
|
+
retryable: false
|
|
352
|
+
}));
|
|
353
|
+
const doneEvent = doneEvents[0];
|
|
354
|
+
if (doneEvent === void 0 || doneEvent.stopReason !== stopReason) return Effect.fail(new LLMError({
|
|
355
|
+
cause: "invalid_response",
|
|
356
|
+
message: `LLM done reason must be ${stopReason}`,
|
|
357
|
+
retryable: false
|
|
358
|
+
}));
|
|
359
|
+
return Effect.succeed({
|
|
360
|
+
toolCalls,
|
|
361
|
+
stopReason
|
|
362
|
+
});
|
|
363
|
+
};
|
|
364
|
+
const makeAfterLlmStream = (input, llmEventsRef) => Stream.unwrap(Effect.gen(function* () {
|
|
365
|
+
const llmEvents = yield* Ref.get(llmEventsRef);
|
|
366
|
+
const completion = yield* validateTurnCompletion(llmEvents);
|
|
367
|
+
const assistantMessage = accumulateAssistantMessage(llmEvents);
|
|
368
|
+
const turnEndEvents = [LLMStreamEnd.make({ turn: input.turn }), AssistantMessageEvent.make({ message: assistantMessage })];
|
|
369
|
+
yield* Ref.update(input.createdMessages, (messages) => [...messages, assistantMessage]);
|
|
370
|
+
if (completion.toolCalls.length === 0) {
|
|
371
|
+
const messages = yield* Ref.get(input.createdMessages);
|
|
372
|
+
const usage = yield* Ref.get(input.usage);
|
|
373
|
+
return Stream.fromIterable([
|
|
374
|
+
...turnEndEvents,
|
|
375
|
+
TurnEnd.make({
|
|
376
|
+
turn: input.turn,
|
|
377
|
+
reason: completion.stopReason
|
|
378
|
+
}),
|
|
379
|
+
AgentEnd.make({
|
|
380
|
+
messages,
|
|
381
|
+
turns: input.turn,
|
|
382
|
+
usage
|
|
383
|
+
})
|
|
384
|
+
]);
|
|
385
|
+
}
|
|
386
|
+
const toolResultMessages = yield* Ref.make([]);
|
|
387
|
+
const prepared = yield* prepareToolBatch({
|
|
388
|
+
tools: input.config.tools,
|
|
389
|
+
responses: input.config.hitlResponses ?? [],
|
|
390
|
+
calls: completion.toolCalls
|
|
391
|
+
});
|
|
392
|
+
if (prepared.resultMessages.length > 0) yield* Ref.update(toolResultMessages, (results) => [...results, ...prepared.resultMessages]);
|
|
393
|
+
if (prepared.pendingRequests.length > 0) {
|
|
394
|
+
const pendingRequests = nonEmptyHitlRequests(prepared.pendingRequests);
|
|
395
|
+
if (pendingRequests === void 0) return Stream.empty;
|
|
396
|
+
const readyResults = orderedToolResultMessages(yield* Ref.get(toolResultMessages));
|
|
397
|
+
if (readyResults.length > 0) yield* Ref.update(input.createdMessages, (messages) => [...messages, ...readyResults]);
|
|
398
|
+
const messages = yield* Ref.get(input.createdMessages);
|
|
399
|
+
const usage = yield* Ref.get(input.usage);
|
|
400
|
+
return Stream.fromIterable([
|
|
401
|
+
...turnEndEvents,
|
|
402
|
+
...prepared.events,
|
|
403
|
+
...prepared.pendingEvents,
|
|
404
|
+
TurnEnd.make({
|
|
405
|
+
turn: input.turn,
|
|
406
|
+
reason: completion.stopReason
|
|
407
|
+
}),
|
|
408
|
+
AgentAwaitingInput.make({
|
|
409
|
+
requests: pendingRequests,
|
|
410
|
+
messages,
|
|
411
|
+
turns: input.turn,
|
|
412
|
+
usage
|
|
413
|
+
})
|
|
414
|
+
]);
|
|
415
|
+
}
|
|
416
|
+
const toolExecutionStream = parallelToolExecutionStream({
|
|
417
|
+
calls: prepared.callsToExecute,
|
|
418
|
+
executor: input.executor,
|
|
419
|
+
loopConfig: input.loopConfig,
|
|
420
|
+
model: input.config.model,
|
|
421
|
+
results: toolResultMessages
|
|
422
|
+
});
|
|
423
|
+
const nextTurnStream = Stream.unwrap(Ref.get(toolResultMessages).pipe(Effect.flatMap((results) => {
|
|
424
|
+
const orderedResults = orderedToolResultMessages(results);
|
|
425
|
+
return Ref.update(input.createdMessages, (messages) => [...messages, ...orderedResults]).pipe(Effect.as(Stream.make(TurnEnd.make({
|
|
426
|
+
turn: input.turn,
|
|
427
|
+
reason: completion.stopReason
|
|
428
|
+
})).pipe(Stream.concat(makeTurnStream({
|
|
429
|
+
...input,
|
|
430
|
+
currentMessages: [
|
|
431
|
+
...input.currentMessages,
|
|
432
|
+
assistantMessage,
|
|
433
|
+
...orderedResults
|
|
434
|
+
],
|
|
435
|
+
turn: input.turn + 1
|
|
436
|
+
})))));
|
|
437
|
+
})));
|
|
438
|
+
return Stream.fromIterable(turnEndEvents).pipe(Stream.concat(Stream.fromIterable(prepared.events)), Stream.concat(toolExecutionStream), Stream.concat(nextTurnStream));
|
|
439
|
+
}));
|
|
440
|
+
const makeModelOnlyAfterLlmStream = (input, llmEventsRef) => Stream.unwrap(Effect.gen(function* () {
|
|
441
|
+
const llmEvents = yield* Ref.get(llmEventsRef);
|
|
442
|
+
const completion = yield* validateTurnCompletion(llmEvents);
|
|
443
|
+
const assistantMessage = accumulateAssistantMessage(llmEvents);
|
|
444
|
+
yield* Ref.update(input.createdMessages, (messages) => [...messages, assistantMessage]);
|
|
445
|
+
return Stream.fromIterable([
|
|
446
|
+
LLMStreamEnd.make({ turn: input.turn }),
|
|
447
|
+
AssistantMessageEvent.make({ message: assistantMessage }),
|
|
448
|
+
TurnEnd.make({
|
|
449
|
+
turn: input.turn,
|
|
450
|
+
reason: completion.stopReason
|
|
451
|
+
})
|
|
452
|
+
]);
|
|
453
|
+
}));
|
|
454
|
+
const makeLlmStream = (input, llmEvents, result) => {
|
|
455
|
+
const makeStream = () => input.provider.stream({
|
|
456
|
+
messages: result.messages,
|
|
457
|
+
tools: input.config.tools,
|
|
458
|
+
model: input.config.model,
|
|
459
|
+
reasoningEffort: input.config.reasoningEffort,
|
|
460
|
+
systemPrompt: input.config.systemPrompt
|
|
461
|
+
});
|
|
462
|
+
return Stream.fromIterable(result.events).pipe(Stream.concat(withProviderRetries(makeStream(), input.loopConfig, makeStream, 1))).pipe(Stream.tap((event) => {
|
|
463
|
+
if (!isLlmEvent(event)) return Effect.void;
|
|
464
|
+
const appendEvent = Ref.update(llmEvents, (events) => [...events, event]);
|
|
465
|
+
if (event._tag !== "Usage") return appendEvent;
|
|
466
|
+
return Ref.update(input.usage, (usage) => addAgentUsage(usage, event.usage)).pipe(Effect.flatMap(() => appendEvent));
|
|
467
|
+
}), Stream.flatMap((event) => event._tag === "AgentRetry" ? Stream.make(event) : isLlmEvent(event) ? Stream.fromIterable(toLlmEvent(event)) : Stream.make(event)), Stream.concat(makeAfterLlmStream(input, llmEvents)));
|
|
468
|
+
};
|
|
469
|
+
const makeModelOnlyLlmStream = (input, llmEvents, result) => {
|
|
470
|
+
const makeStream = () => input.provider.stream({
|
|
471
|
+
messages: result.messages,
|
|
472
|
+
tools: input.config.tools,
|
|
473
|
+
model: input.config.model,
|
|
474
|
+
reasoningEffort: input.config.reasoningEffort,
|
|
475
|
+
systemPrompt: input.config.systemPrompt
|
|
476
|
+
});
|
|
477
|
+
return Stream.fromIterable(result.events).pipe(Stream.concat(withProviderRetries(makeStream(), input.loopConfig, makeStream, 1))).pipe(Stream.tap((event) => {
|
|
478
|
+
if (!isLlmEvent(event)) return Effect.void;
|
|
479
|
+
const appendEvent = Ref.update(llmEvents, (events) => [...events, event]);
|
|
480
|
+
if (event._tag !== "Usage") return appendEvent;
|
|
481
|
+
return Ref.update(input.usage, (usage) => addAgentUsage(usage, event.usage)).pipe(Effect.flatMap(() => appendEvent));
|
|
482
|
+
}), Stream.flatMap((event) => event._tag === "AgentRetry" ? Stream.make(event) : isLlmEvent(event) ? Stream.fromIterable(toLlmEvent(event)) : Stream.make(event)), Stream.concat(makeModelOnlyAfterLlmStream(input, llmEvents)));
|
|
483
|
+
};
|
|
484
|
+
const makeTurnStream = (input) => Stream.suspend(() => {
|
|
485
|
+
if (input.turn > input.loopConfig.maxTurns) return Stream.fail(new AbortError({ reason: "max_turns" }));
|
|
486
|
+
const llmStream = Stream.unwrap(Effect.gen(function* () {
|
|
487
|
+
const llmEvents = yield* Ref.make([]);
|
|
488
|
+
const result = yield* input.contextTransformer.transform(input.currentMessages);
|
|
489
|
+
yield* validateCapabilities(input.config, result.messages);
|
|
490
|
+
return makeLlmStream(input, llmEvents, result);
|
|
491
|
+
}));
|
|
492
|
+
return Stream.fromIterable([TurnStart.make({ turn: input.turn }), LLMStreamStart.make({ turn: input.turn })]).pipe(Stream.concat(llmStream));
|
|
493
|
+
});
|
|
494
|
+
const makePendingToolResumeStream = (input) => Stream.unwrap(Effect.gen(function* () {
|
|
495
|
+
const pendingCalls = pendingHostToolCalls(input.currentMessages);
|
|
496
|
+
if (pendingCalls.length === 0 || (input.config.hitlResponses ?? []).length === 0) return makeTurnStream(input);
|
|
497
|
+
const toolResultMessages = yield* Ref.make([]);
|
|
498
|
+
const prepared = yield* prepareToolBatch({
|
|
499
|
+
tools: input.config.tools,
|
|
500
|
+
responses: input.config.hitlResponses ?? [],
|
|
501
|
+
calls: pendingCalls
|
|
502
|
+
});
|
|
503
|
+
if (prepared.resultMessages.length > 0) yield* Ref.update(toolResultMessages, (results) => [...results, ...prepared.resultMessages]);
|
|
504
|
+
if (prepared.pendingRequests.length > 0) {
|
|
505
|
+
const pendingRequests = nonEmptyHitlRequests(prepared.pendingRequests);
|
|
506
|
+
if (pendingRequests === void 0) return Stream.empty;
|
|
507
|
+
const readyResults = orderedToolResultMessages(yield* Ref.get(toolResultMessages));
|
|
508
|
+
if (readyResults.length > 0) yield* Ref.update(input.createdMessages, (messages) => [...messages, ...readyResults]);
|
|
509
|
+
const messages = yield* Ref.get(input.createdMessages);
|
|
510
|
+
const usage = yield* Ref.get(input.usage);
|
|
511
|
+
return Stream.fromIterable([
|
|
512
|
+
...prepared.events,
|
|
513
|
+
...prepared.pendingEvents,
|
|
514
|
+
AgentAwaitingInput.make({
|
|
515
|
+
requests: pendingRequests,
|
|
516
|
+
messages,
|
|
517
|
+
turns: Math.max(0, input.turn - 1),
|
|
518
|
+
usage
|
|
519
|
+
})
|
|
520
|
+
]);
|
|
521
|
+
}
|
|
522
|
+
const toolExecutionStream = parallelToolExecutionStream({
|
|
523
|
+
calls: prepared.callsToExecute,
|
|
524
|
+
executor: input.executor,
|
|
525
|
+
loopConfig: input.loopConfig,
|
|
526
|
+
model: input.config.model,
|
|
527
|
+
results: toolResultMessages
|
|
528
|
+
});
|
|
529
|
+
const nextTurnStream = Stream.unwrap(Ref.get(toolResultMessages).pipe(Effect.flatMap((results) => {
|
|
530
|
+
const orderedResults = orderedToolResultMessages(results);
|
|
531
|
+
return Ref.update(input.createdMessages, (messages) => [...messages, ...orderedResults]).pipe(Effect.as(makeTurnStream({
|
|
532
|
+
...input,
|
|
533
|
+
currentMessages: [...input.currentMessages, ...orderedResults]
|
|
534
|
+
})));
|
|
535
|
+
})));
|
|
536
|
+
return Stream.fromIterable(prepared.events).pipe(Stream.concat(toolExecutionStream), Stream.concat(nextTurnStream));
|
|
537
|
+
}));
|
|
538
|
+
const unavailableToolExecutor = { execute: (call) => Effect.fail(new ToolError({
|
|
539
|
+
tool: call.name,
|
|
540
|
+
message: "Tool execution is not available in model turn step",
|
|
541
|
+
cause: "execution"
|
|
542
|
+
})) };
|
|
543
|
+
const makeModelOnlyTurnStream = (input) => Stream.suspend(() => {
|
|
544
|
+
if (input.turn > input.loopConfig.maxTurns) return Stream.fail(new AbortError({ reason: "max_turns" }));
|
|
545
|
+
const llmStream = Stream.unwrap(Effect.gen(function* () {
|
|
546
|
+
const llmEvents = yield* Ref.make([]);
|
|
547
|
+
const result = yield* input.contextTransformer.transform(input.currentMessages);
|
|
548
|
+
yield* validateCapabilities(input.config, result.messages);
|
|
549
|
+
return makeModelOnlyLlmStream(input, llmEvents, result);
|
|
550
|
+
}));
|
|
551
|
+
return Stream.fromIterable([TurnStart.make({ turn: input.turn }), LLMStreamStart.make({ turn: input.turn })]).pipe(Stream.concat(llmStream));
|
|
552
|
+
});
|
|
553
|
+
const runModelTurn = (config) => Stream.unwrap(Effect.gen(function* () {
|
|
554
|
+
const contextTransformer = yield* ContextTransformer;
|
|
555
|
+
const loopConfig = yield* LoopConfig;
|
|
556
|
+
const provider = yield* LLMProvider;
|
|
557
|
+
const createdMessages = yield* Ref.make([]);
|
|
558
|
+
const usage = yield* Ref.make(zeroAgentUsage);
|
|
559
|
+
return makeModelOnlyTurnStream({
|
|
560
|
+
config,
|
|
561
|
+
contextTransformer,
|
|
562
|
+
loopConfig,
|
|
563
|
+
provider,
|
|
564
|
+
executor: unavailableToolExecutor,
|
|
565
|
+
currentMessages: config.messages,
|
|
566
|
+
createdMessages,
|
|
567
|
+
usage,
|
|
568
|
+
turn: config.turn
|
|
569
|
+
});
|
|
570
|
+
}));
|
|
571
|
+
const runToolBatch = (config) => Stream.unwrap(Effect.gen(function* () {
|
|
572
|
+
const executor = yield* ToolExecutor;
|
|
573
|
+
const loopConfig = yield* LoopConfig;
|
|
574
|
+
const toolResultMessages = yield* Ref.make([]);
|
|
575
|
+
const prepared = yield* prepareToolBatch({
|
|
576
|
+
tools: config.tools ?? [],
|
|
577
|
+
responses: config.hitlResponses ?? [],
|
|
578
|
+
calls: config.calls
|
|
579
|
+
});
|
|
580
|
+
const hasPendingRequests = prepared.pendingRequests.length > 0;
|
|
581
|
+
const resultEvents = hasPendingRequests ? [] : prepared.resultEvents;
|
|
582
|
+
const pendingRequests = nonEmptyHitlRequests(prepared.pendingRequests);
|
|
583
|
+
const awaitingEvents = pendingRequests === void 0 ? [] : [AgentAwaitingInput.make({
|
|
584
|
+
requests: pendingRequests,
|
|
585
|
+
messages: config.createdMessages ?? [],
|
|
586
|
+
turns: config.turn ?? 0,
|
|
587
|
+
usage: config.usage ?? zeroAgentUsage
|
|
588
|
+
})];
|
|
589
|
+
return Stream.fromIterable([
|
|
590
|
+
...prepared.events,
|
|
591
|
+
...resultEvents,
|
|
592
|
+
...prepared.pendingEvents,
|
|
593
|
+
...awaitingEvents
|
|
594
|
+
]).pipe(Stream.concat(parallelToolExecutionStream({
|
|
595
|
+
calls: hasPendingRequests ? [] : prepared.callsToExecute,
|
|
596
|
+
executor,
|
|
597
|
+
loopConfig,
|
|
598
|
+
model: config.model ?? "",
|
|
599
|
+
results: toolResultMessages
|
|
600
|
+
})));
|
|
601
|
+
}));
|
|
602
|
+
const run = (config) => Stream.unwrap(Effect.gen(function* () {
|
|
603
|
+
const contextTransformer = yield* ContextTransformer;
|
|
604
|
+
const loopConfig = yield* LoopConfig;
|
|
605
|
+
const provider = yield* LLMProvider;
|
|
606
|
+
const executor = yield* ToolExecutor;
|
|
607
|
+
const createdMessages = yield* Ref.make([]);
|
|
608
|
+
const usage = yield* Ref.make(zeroAgentUsage);
|
|
609
|
+
return Stream.make(AgentStart.make({})).pipe(Stream.concat(makePendingToolResumeStream({
|
|
610
|
+
config,
|
|
611
|
+
contextTransformer,
|
|
612
|
+
loopConfig,
|
|
613
|
+
provider,
|
|
614
|
+
executor,
|
|
615
|
+
currentMessages: config.messages,
|
|
616
|
+
createdMessages,
|
|
617
|
+
usage,
|
|
618
|
+
turn: 1
|
|
619
|
+
})));
|
|
620
|
+
}));
|
|
621
|
+
//#endregion
|
|
622
|
+
export { run, runModelTurn, runToolBatch };
|
|
623
|
+
|
|
624
|
+
//# sourceMappingURL=run.mjs.map
|