kernl 0.1.1
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/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +53 -0
- package/LICENSE +201 -0
- package/dist/agent.d.ts +43 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +130 -0
- package/dist/context.d.ts +70 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +111 -0
- package/dist/env.d.ts +45 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +31 -0
- package/dist/error.d.ts +1 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +1 -0
- package/dist/guardrail.d.ts +178 -0
- package/dist/guardrail.d.ts.map +1 -0
- package/dist/guardrail.js +34 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/kernel.d.ts +7 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +7 -0
- package/dist/kernl.d.ts +18 -0
- package/dist/kernl.d.ts.map +1 -0
- package/dist/kernl.js +16 -0
- package/dist/lib/env.d.ts +43 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +29 -0
- package/dist/lib/error.d.ts +88 -0
- package/dist/lib/error.d.ts.map +1 -0
- package/dist/lib/error.js +117 -0
- package/dist/lib/logger.d.ts +36 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +43 -0
- package/dist/lib/serde/__tests__/codec.test.d.ts +2 -0
- package/dist/lib/serde/__tests__/codec.test.d.ts.map +1 -0
- package/dist/lib/serde/__tests__/codec.test.js +75 -0
- package/dist/lib/serde/codec.d.ts +12 -0
- package/dist/lib/serde/codec.d.ts.map +1 -0
- package/dist/lib/serde/codec.js +54 -0
- package/dist/lib/serde/json.d.ts +8 -0
- package/dist/lib/serde/json.d.ts.map +1 -0
- package/dist/lib/serde/json.js +13 -0
- package/dist/lib/serde/thread.d.ts +1 -0
- package/dist/lib/serde/thread.d.ts.map +1 -0
- package/dist/lib/serde/thread.js +172 -0
- package/dist/lib/serde/tool.d.ts +36 -0
- package/dist/lib/serde/tool.d.ts.map +1 -0
- package/dist/lib/serde/tool.js +1 -0
- package/dist/lib/utils.d.ts +19 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +41 -0
- package/dist/lifecycle.d.ts +133 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +29 -0
- package/dist/logger.d.ts +36 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +43 -0
- package/dist/mcp/__tests__/base.test.d.ts +2 -0
- package/dist/mcp/__tests__/base.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/base.test.js +268 -0
- package/dist/mcp/__tests__/fixtures/echo-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/echo-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/echo-server.js +92 -0
- package/dist/mcp/__tests__/fixtures/math-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/math-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/math-server.js +98 -0
- package/dist/mcp/__tests__/fixtures/server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/server.js +162 -0
- package/dist/mcp/__tests__/fixtures/test-server.d.ts +3 -0
- package/dist/mcp/__tests__/fixtures/test-server.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/test-server.js +163 -0
- package/dist/mcp/__tests__/fixtures/utils.d.ts +17 -0
- package/dist/mcp/__tests__/fixtures/utils.d.ts.map +1 -0
- package/dist/mcp/__tests__/fixtures/utils.js +42 -0
- package/dist/mcp/__tests__/integration.test.d.ts +2 -0
- package/dist/mcp/__tests__/integration.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/integration.test.js +360 -0
- package/dist/mcp/__tests__/stdio.test.d.ts +2 -0
- package/dist/mcp/__tests__/stdio.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/stdio.test.js +180 -0
- package/dist/mcp/__tests__/test-utils.d.ts +17 -0
- package/dist/mcp/__tests__/test-utils.d.ts.map +1 -0
- package/dist/mcp/__tests__/test-utils.js +42 -0
- package/dist/mcp/__tests__/utils.test.d.ts +2 -0
- package/dist/mcp/__tests__/utils.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/utils.test.js +300 -0
- package/dist/mcp/base.d.ts +88 -0
- package/dist/mcp/base.d.ts.map +1 -0
- package/dist/mcp/base.js +68 -0
- package/dist/mcp/http.d.ts +34 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.js +100 -0
- package/dist/mcp/node.d.ts +60 -0
- package/dist/mcp/node.d.ts.map +1 -0
- package/dist/mcp/node.js +297 -0
- package/dist/mcp/sse.d.ts +34 -0
- package/dist/mcp/sse.d.ts.map +1 -0
- package/dist/mcp/sse.js +97 -0
- package/dist/mcp/stdio.d.ts +32 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +96 -0
- package/dist/mcp/types.d.ts +172 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +16 -0
- package/dist/mcp/utils.d.ts +23 -0
- package/dist/mcp/utils.d.ts.map +1 -0
- package/dist/mcp/utils.js +44 -0
- package/dist/model.d.ts +175 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +1 -0
- package/dist/providers/ai.d.ts +1 -0
- package/dist/providers/ai.d.ts.map +1 -0
- package/dist/providers/ai.js +1 -0
- package/dist/providers/default.d.ts +16 -0
- package/dist/providers/default.d.ts.map +1 -0
- package/dist/providers/default.js +17 -0
- package/dist/providers/registry.d.ts +1 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +1 -0
- package/dist/sched/scheduler.d.ts +20 -0
- package/dist/sched/scheduler.d.ts.map +1 -0
- package/dist/sched/scheduler.js +1 -0
- package/dist/sched/task.d.ts +92 -0
- package/dist/sched/task.d.ts.map +1 -0
- package/dist/sched/task.js +102 -0
- package/dist/serde/__tests__/codec.test.d.ts +2 -0
- package/dist/serde/__tests__/codec.test.d.ts.map +1 -0
- package/dist/serde/__tests__/codec.test.js +75 -0
- package/dist/serde/codec.d.ts +12 -0
- package/dist/serde/codec.d.ts.map +1 -0
- package/dist/serde/codec.js +54 -0
- package/dist/serde/json.d.ts +8 -0
- package/dist/serde/json.d.ts.map +1 -0
- package/dist/serde/json.js +13 -0
- package/dist/serde/thread.d.ts +687 -0
- package/dist/serde/thread.d.ts.map +1 -0
- package/dist/serde/thread.js +158 -0
- package/dist/serde/tool.d.ts +36 -0
- package/dist/serde/tool.d.ts.map +1 -0
- package/dist/serde/tool.js +1 -0
- package/dist/session.d.ts +1 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +1 -0
- package/dist/task.d.ts +87 -0
- package/dist/task.d.ts.map +1 -0
- package/dist/task.js +97 -0
- package/dist/thread/__tests__/mock.d.ts +28 -0
- package/dist/thread/__tests__/mock.d.ts.map +1 -0
- package/dist/thread/__tests__/mock.js +74 -0
- package/dist/thread/__tests__/thread.test.d.ts +2 -0
- package/dist/thread/__tests__/thread.test.d.ts.map +1 -0
- package/dist/thread/__tests__/thread.test.js +1412 -0
- package/dist/thread/index.d.ts +2 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/thread/index.js +1 -0
- package/dist/thread/thread.d.ts +66 -0
- package/dist/thread/thread.d.ts.map +1 -0
- package/dist/thread/thread.js +472 -0
- package/dist/thread/utils.d.ts +19 -0
- package/dist/thread/utils.d.ts.map +1 -0
- package/dist/thread/utils.js +50 -0
- package/dist/tool/__tests__/fixtures.d.ts +45 -0
- package/dist/tool/__tests__/fixtures.d.ts.map +1 -0
- package/dist/tool/__tests__/fixtures.js +97 -0
- package/dist/tool/__tests__/tool.test.d.ts +2 -0
- package/dist/tool/__tests__/tool.test.d.ts.map +1 -0
- package/dist/tool/__tests__/tool.test.js +172 -0
- package/dist/tool/__tests__/toolkit.test.d.ts +2 -0
- package/dist/tool/__tests__/toolkit.test.d.ts.map +1 -0
- package/dist/tool/__tests__/toolkit.test.js +134 -0
- package/dist/tool/index.d.ts +4 -0
- package/dist/tool/index.d.ts.map +1 -0
- package/dist/tool/index.js +2 -0
- package/dist/tool/mcp.d.ts +75 -0
- package/dist/tool/mcp.d.ts.map +1 -0
- package/dist/tool/mcp.js +111 -0
- package/dist/tool/tool.d.ts +95 -0
- package/dist/tool/tool.d.ts.map +1 -0
- package/dist/tool/tool.js +176 -0
- package/dist/tool/toolkit.d.ts +121 -0
- package/dist/tool/toolkit.d.ts.map +1 -0
- package/dist/tool/toolkit.js +180 -0
- package/dist/tool/types.d.ts +187 -0
- package/dist/tool/types.d.ts.map +1 -0
- package/dist/tool/types.js +1 -0
- package/dist/tools.d.ts +362 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +220 -0
- package/dist/trace/processor.d.ts +1 -0
- package/dist/trace/processor.d.ts.map +1 -0
- package/dist/trace/processor.js +1 -0
- package/dist/trace/traces.d.ts +1 -0
- package/dist/trace/traces.d.ts.map +1 -0
- package/dist/trace/traces.js +73 -0
- package/dist/trace/utils.d.ts +22 -0
- package/dist/trace/utils.d.ts.map +1 -0
- package/dist/trace/utils.js +30 -0
- package/dist/types/agent.d.ts +91 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +1 -0
- package/dist/types/proto.d.ts +1551 -0
- package/dist/types/proto.d.ts.map +1 -0
- package/dist/types/proto.js +531 -0
- package/dist/types/thread.d.ts +71 -0
- package/dist/types/thread.d.ts.map +1 -0
- package/dist/types/thread.js +5 -0
- package/dist/usage.d.ts +43 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +61 -0
- package/package.json +52 -0
- package/src/agent.ts +203 -0
- package/src/context.ts +265 -0
- package/src/guardrail.ts +277 -0
- package/src/index.ts +3 -0
- package/src/kernl.ts +22 -0
- package/src/lib/env.ts +36 -0
- package/src/lib/error.ts +158 -0
- package/src/lib/logger.ts +78 -0
- package/src/lib/serde/json.ts +18 -0
- package/src/lib/serde/thread.ts +188 -0
- package/src/lifecycle.ts +181 -0
- package/src/mcp/__tests__/base.test.ts +344 -0
- package/src/mcp/__tests__/fixtures/server.ts +179 -0
- package/src/mcp/__tests__/fixtures/utils.ts +58 -0
- package/src/mcp/__tests__/integration.test.ts +447 -0
- package/src/mcp/__tests__/stdio.test.ts +236 -0
- package/src/mcp/__tests__/utils.test.ts +360 -0
- package/src/mcp/base.ts +162 -0
- package/src/mcp/http.ts +147 -0
- package/src/mcp/sse.ts +137 -0
- package/src/mcp/stdio.ts +136 -0
- package/src/mcp/types.ts +202 -0
- package/src/mcp/utils.ts +62 -0
- package/src/task.ts +119 -0
- package/src/thread/__tests__/mock.ts +95 -0
- package/src/thread/__tests__/thread.test.ts +1574 -0
- package/src/thread/index.ts +1 -0
- package/src/thread/thread.ts +611 -0
- package/src/thread/utils.ts +67 -0
- package/src/tool/__tests__/fixtures.ts +106 -0
- package/src/tool/__tests__/tool.test.ts +235 -0
- package/src/tool/__tests__/toolkit.test.ts +174 -0
- package/src/tool/index.ts +10 -0
- package/src/tool/tool.ts +264 -0
- package/src/tool/toolkit.ts +234 -0
- package/src/tool/types.ts +243 -0
- package/src/trace/processor.ts +0 -0
- package/src/trace/traces.ts +86 -0
- package/src/trace/utils.ts +38 -0
- package/src/types/agent.ts +145 -0
- package/src/types/thread.ts +86 -0
- package/tsconfig.json +13 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/thread/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Thread } from "./thread";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Kernl } from "../kernl";
|
|
2
|
+
import { Agent } from "../agent";
|
|
3
|
+
import { Context } from "../context";
|
|
4
|
+
import type { Task } from "../task";
|
|
5
|
+
import { LanguageModel, LanguageModelResponse } from "@kernl/protocol";
|
|
6
|
+
import type { ThreadEvent, ThreadOptions, ThreadExecuteResult } from "../types/thread";
|
|
7
|
+
import type { AgentResponseType } from "../types/agent";
|
|
8
|
+
import type { ResolvedAgentResponse } from "../guardrail";
|
|
9
|
+
/**
|
|
10
|
+
* A thread drives the execution loop for an agent.
|
|
11
|
+
*/
|
|
12
|
+
export declare class Thread<TContext = unknown, TResponse extends AgentResponseType = "text"> {
|
|
13
|
+
private kernl;
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly agent: Agent<TContext, TResponse>;
|
|
16
|
+
readonly context: Context<TContext>;
|
|
17
|
+
readonly model: LanguageModel;
|
|
18
|
+
readonly parent: Task<TContext> | null;
|
|
19
|
+
readonly mode: "blocking" | "stream";
|
|
20
|
+
readonly state: ThreadState;
|
|
21
|
+
readonly input: ThreadEvent[] | string;
|
|
22
|
+
private history;
|
|
23
|
+
constructor(kernl: Kernl, agent: Agent<TContext, TResponse>, input: ThreadEvent[] | string, options?: ThreadOptions<TContext>);
|
|
24
|
+
/**
|
|
25
|
+
* Main thread execution loop - runs until terminal state or interruption
|
|
26
|
+
*/
|
|
27
|
+
execute(): Promise<ThreadExecuteResult<ResolvedAgentResponse<TResponse>>>;
|
|
28
|
+
/**
|
|
29
|
+
* A single tick of the thread's execution.
|
|
30
|
+
*
|
|
31
|
+
* Prepares the input for the model, gets the response, and then parses into a TickResult
|
|
32
|
+
* with the events generated and the model's intentions (actions).
|
|
33
|
+
*/
|
|
34
|
+
private tick;
|
|
35
|
+
/**
|
|
36
|
+
* Perform the actions returned by the model
|
|
37
|
+
*/
|
|
38
|
+
private performActions;
|
|
39
|
+
/**
|
|
40
|
+
* Execute function calls requested by the model
|
|
41
|
+
*
|
|
42
|
+
* TODO: refactor into actions system
|
|
43
|
+
*/
|
|
44
|
+
private executeTools;
|
|
45
|
+
/**
|
|
46
|
+
* Applies call-level filters and prepares the model request for the language model
|
|
47
|
+
*/
|
|
48
|
+
private prepareModelRequest;
|
|
49
|
+
/**
|
|
50
|
+
* @internal
|
|
51
|
+
* Parses the model's response into events (for history) and actions (for execution).
|
|
52
|
+
*/
|
|
53
|
+
private parseModelResponse;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* ThreadState tracks the execution state of a single thread.
|
|
57
|
+
*
|
|
58
|
+
* A thread is created each time a task is scheduled and executes
|
|
59
|
+
* the main tick() loop until terminal state.
|
|
60
|
+
*/
|
|
61
|
+
export declare class ThreadState {
|
|
62
|
+
tick: number;
|
|
63
|
+
modelResponses: LanguageModelResponse[];
|
|
64
|
+
constructor();
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=thread.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/thread/thread.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,EAEL,aAAa,EAEb,qBAAqB,EAGtB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAEV,WAAW,EACX,aAAa,EACb,mBAAmB,EAGpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAIzD;;GAEG;AACH,qBAAa,MAAM,CACjB,QAAQ,GAAG,OAAO,EAClB,SAAS,SAAS,iBAAiB,GAAG,MAAM;IAE5C,OAAO,CAAC,KAAK,CAAQ;IAErB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAGrC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACvC,OAAO,CAAC,OAAO,CAAsE;gBAGnF,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,EACjC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,EAC7B,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC;IAgCnC;;OAEG;IACG,OAAO,IAAI,OAAO,CACtB,mBAAmB,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,CACtD;IAmDD;;;;;OAKG;YACW,IAAI;IAmClB;;OAEG;YACW,cAAc;IAmC5B;;;;OAIG;YACW,YAAY;IA4C1B;;OAEG;YACW,mBAAmB;IA4CjC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAqB3B;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IACtB,IAAI,EAAE,MAAM,CAAwE;IACpF,cAAc,EAAE,qBAAqB,EAAE,CAAmE;;CAiB3G"}
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { Context } from "../context";
|
|
3
|
+
import { FAILED, } from "@kernl/protocol";
|
|
4
|
+
import { randomID, filter } from "@kernl/shared/lib";
|
|
5
|
+
import { getFinalResponse, parseFinalResponse } from "./utils";
|
|
6
|
+
/**
|
|
7
|
+
* A thread drives the execution loop for an agent.
|
|
8
|
+
*/
|
|
9
|
+
export class Thread {
|
|
10
|
+
kernl;
|
|
11
|
+
id;
|
|
12
|
+
agent;
|
|
13
|
+
context;
|
|
14
|
+
model; /* inherited from the agent unless specified */
|
|
15
|
+
parent; /* parent task which spawned this thread */
|
|
16
|
+
mode; /* TODO */
|
|
17
|
+
/* state */
|
|
18
|
+
state;
|
|
19
|
+
input; /* the initial input for the thread */
|
|
20
|
+
history;
|
|
21
|
+
constructor(kernl, agent, input, options) {
|
|
22
|
+
this.id = `tid_${randomID()}`;
|
|
23
|
+
this.agent = agent;
|
|
24
|
+
this.context = options?.context ?? new Context();
|
|
25
|
+
this.kernl = kernl;
|
|
26
|
+
this.parent = options?.task ?? null;
|
|
27
|
+
this.model = options?.model ?? agent.model;
|
|
28
|
+
this.state = new ThreadState(); // (TODO): checkpoint ?? new ThreadState()
|
|
29
|
+
this.mode = "blocking"; // (TODO): add streaming
|
|
30
|
+
this.input = input;
|
|
31
|
+
// Convert string input to user message and initialize history
|
|
32
|
+
if (typeof input === "string") {
|
|
33
|
+
this.history = [
|
|
34
|
+
{
|
|
35
|
+
kind: "message",
|
|
36
|
+
id: `msg_${randomID()}`,
|
|
37
|
+
role: "user",
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
kind: "text",
|
|
41
|
+
text: input,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.history = input;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Main thread execution loop - runs until terminal state or interruption
|
|
53
|
+
*/
|
|
54
|
+
async execute() {
|
|
55
|
+
while (true) {
|
|
56
|
+
const { events, intentions } = await this.tick(); // actions: { syscalls, functions, mcpApprovalRequests }
|
|
57
|
+
this.history.push(...events);
|
|
58
|
+
// // priority 1: syscalls first - these override all other actions
|
|
59
|
+
// if (actions.syscalls.length > 0) {
|
|
60
|
+
// switch (actions.syscalls.kind) { // is it possible to have more than one?
|
|
61
|
+
// case SYS_WAIT:
|
|
62
|
+
// return this.state;
|
|
63
|
+
// case SYS_EXIT:
|
|
64
|
+
// return { state: this.state, output: this.output }
|
|
65
|
+
// default:
|
|
66
|
+
// }
|
|
67
|
+
// }
|
|
68
|
+
// if model returns a message with no actions intentions -> terminal state
|
|
69
|
+
if (!intentions) {
|
|
70
|
+
const text = getFinalResponse(events);
|
|
71
|
+
if (!text)
|
|
72
|
+
continue; // run again, policy-dependent?
|
|
73
|
+
const parsed = parseFinalResponse(text, this.agent.responseType);
|
|
74
|
+
// await this.agent.runOutputGuardails(context, state);
|
|
75
|
+
// this.kernl.emit("thread.terminated", context, output);
|
|
76
|
+
return { response: parsed, state: this.state };
|
|
77
|
+
}
|
|
78
|
+
// perform the actions intended by the model
|
|
79
|
+
const { actions, pendingApprovals } = await this.performActions(intentions);
|
|
80
|
+
this.history.push(...actions);
|
|
81
|
+
if (pendingApprovals.length > 0) {
|
|
82
|
+
// publish a batch approval request containing all of them
|
|
83
|
+
//
|
|
84
|
+
// const reqid = randomID();
|
|
85
|
+
// this.kernl.publish(channel, approvalRequest);
|
|
86
|
+
//
|
|
87
|
+
// const filter = { reqid }
|
|
88
|
+
// await wait_event(Action.ApprovalResponse, filter);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ----------------------
|
|
93
|
+
// Internal helpers
|
|
94
|
+
// ----------------------
|
|
95
|
+
/**
|
|
96
|
+
* A single tick of the thread's execution.
|
|
97
|
+
*
|
|
98
|
+
* Prepares the input for the model, gets the response, and then parses into a TickResult
|
|
99
|
+
* with the events generated and the model's intentions (actions).
|
|
100
|
+
*/
|
|
101
|
+
async tick() {
|
|
102
|
+
this.state.tick++;
|
|
103
|
+
// // check limits
|
|
104
|
+
// if (this.state.tick > this.limits.maxTicks) {
|
|
105
|
+
// throw new RuntimeError("resource_limit:max_ticks_exceeded");
|
|
106
|
+
// }
|
|
107
|
+
// run guardrails on the first tick
|
|
108
|
+
if (this.state.tick === 1) {
|
|
109
|
+
// await this.agent.runInputGuardrails(this.context, ...?);
|
|
110
|
+
}
|
|
111
|
+
const req = await this.prepareModelRequest(this.history); // (TODO): how to get input for this tick?
|
|
112
|
+
// if (this.mode === "stream") {
|
|
113
|
+
// const stream = this.model.stream(input, {
|
|
114
|
+
// system: systemPrompt,
|
|
115
|
+
// tools: this.agent.tools /* [systools, tools] */,
|
|
116
|
+
// settings: this.agent.modelSettings,
|
|
117
|
+
// responseSchema: this.agent.responseType,
|
|
118
|
+
// });
|
|
119
|
+
// for await (const event of stream) {
|
|
120
|
+
// // handle streaming events
|
|
121
|
+
// }
|
|
122
|
+
// response = stream.collect(); // something like this
|
|
123
|
+
// } else {
|
|
124
|
+
const res = await this.model.generate(req);
|
|
125
|
+
this.state.modelResponses.push(res);
|
|
126
|
+
// this.stats.usage.add(response.usage);
|
|
127
|
+
return this.parseModelResponse(res);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Perform the actions returned by the model
|
|
131
|
+
*/
|
|
132
|
+
async performActions(intentions) {
|
|
133
|
+
// (TODO): refactor into a general actions system - probably shouldn't be handled by Thread
|
|
134
|
+
const toolEvents = await this.executeTools(intentions.toolCalls);
|
|
135
|
+
// const mcpEvents = await this.executeMCPRequests(actions.mcpRequests);
|
|
136
|
+
// Separate events and pending approvals
|
|
137
|
+
const actions = [];
|
|
138
|
+
const pendingApprovals = [];
|
|
139
|
+
// (TODO): clean this - approval tracking should be handled differently
|
|
140
|
+
for (const e of toolEvents) {
|
|
141
|
+
if (e.kind === "tool-result" &&
|
|
142
|
+
e.state === "requires_approval") {
|
|
143
|
+
// Find the original tool call for this pending approval
|
|
144
|
+
const originalCall = intentions.toolCalls.find((call) => call.callId === e.callId);
|
|
145
|
+
if (originalCall) {
|
|
146
|
+
pendingApprovals.push(originalCall);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
actions.push(e);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
actions: actions,
|
|
155
|
+
pendingApprovals,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Execute function calls requested by the model
|
|
160
|
+
*
|
|
161
|
+
* TODO: refactor into actions system
|
|
162
|
+
*/
|
|
163
|
+
async executeTools(calls) {
|
|
164
|
+
return await Promise.all(calls.map(async (call) => {
|
|
165
|
+
try {
|
|
166
|
+
const tool = this.agent.tool(call.toolId);
|
|
167
|
+
if (!tool) {
|
|
168
|
+
throw new Error(`Tool ${call.toolId} not found`);
|
|
169
|
+
}
|
|
170
|
+
// hosted tools are executed server-side by the provider, not locally
|
|
171
|
+
assert(tool.type === "function", `Tool ${call.id} is a hosted tool and should not be executed locally`);
|
|
172
|
+
// (TMP) - passing the approval status through the context until actions system
|
|
173
|
+
// is refined
|
|
174
|
+
const ctx = new Context(this.context.context);
|
|
175
|
+
ctx.approve(call.callId); // mark this call as approved
|
|
176
|
+
const res = await tool.invoke(ctx, call.arguments, call.callId);
|
|
177
|
+
return {
|
|
178
|
+
kind: "tool-result",
|
|
179
|
+
callId: call.callId,
|
|
180
|
+
toolId: call.toolId,
|
|
181
|
+
state: res.state,
|
|
182
|
+
result: res.result,
|
|
183
|
+
error: res.error,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
// Handles both tool not found AND any execution errors
|
|
188
|
+
return {
|
|
189
|
+
kind: "tool-result",
|
|
190
|
+
callId: call.callId,
|
|
191
|
+
toolId: call.toolId,
|
|
192
|
+
state: FAILED,
|
|
193
|
+
result: undefined,
|
|
194
|
+
error: error instanceof Error ? error.message : String(error),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Applies call-level filters and prepares the model request for the language model
|
|
201
|
+
*/
|
|
202
|
+
async prepareModelRequest(history) {
|
|
203
|
+
let settings = {
|
|
204
|
+
...this.agent.modelSettings,
|
|
205
|
+
};
|
|
206
|
+
// // TODO: what do we want to do with this?
|
|
207
|
+
// settings = maybeResetToolChoice(this.agent, this.state.toolUse, settings);
|
|
208
|
+
const system = await this.agent.instructions(this.context);
|
|
209
|
+
const input = system
|
|
210
|
+
? [
|
|
211
|
+
// (TODO): add message(role, text) helper
|
|
212
|
+
{
|
|
213
|
+
kind: "message",
|
|
214
|
+
id: randomID(),
|
|
215
|
+
role: "system",
|
|
216
|
+
content: [{ kind: "text", text: system }],
|
|
217
|
+
},
|
|
218
|
+
...history, // (TODO): filter for LanguageModelItem specifically - there may be other thread events
|
|
219
|
+
]
|
|
220
|
+
: history;
|
|
221
|
+
// TODO: apply custom input filters - arguably want global + agent-scoped -> apply in a middleware-like chain
|
|
222
|
+
// const filtered = await applyInputFilters(inputWithSystem, context);
|
|
223
|
+
const filtered = input;
|
|
224
|
+
// serialize action repertoire
|
|
225
|
+
const allTools = await this.agent.tools(this.context);
|
|
226
|
+
const enabled = await filter(allTools, async (tool) => await tool.isEnabled(this.context, this.agent));
|
|
227
|
+
const tools = enabled.map((tool) => tool.serialize());
|
|
228
|
+
return {
|
|
229
|
+
input: filtered,
|
|
230
|
+
settings,
|
|
231
|
+
tools,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* @internal
|
|
236
|
+
* Parses the model's response into events (for history) and actions (for execution).
|
|
237
|
+
*/
|
|
238
|
+
parseModelResponse(res) {
|
|
239
|
+
const events = [];
|
|
240
|
+
const toolCalls = [];
|
|
241
|
+
for (const event of res.content) {
|
|
242
|
+
switch (event.kind) {
|
|
243
|
+
case "tool-call":
|
|
244
|
+
// Add to both actions (for execution) and events (for history)
|
|
245
|
+
toolCalls.push(event);
|
|
246
|
+
// fallthrough
|
|
247
|
+
default:
|
|
248
|
+
events.push(event);
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
events,
|
|
254
|
+
intentions: toolCalls.length > 0 ? { toolCalls } : null,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* ThreadState tracks the execution state of a single thread.
|
|
260
|
+
*
|
|
261
|
+
* A thread is created each time a task is scheduled and executes
|
|
262
|
+
* the main tick() loop until terminal state.
|
|
263
|
+
*/
|
|
264
|
+
export class ThreadState {
|
|
265
|
+
tick;
|
|
266
|
+
modelResponses;
|
|
267
|
+
constructor() {
|
|
268
|
+
this.tick = 0;
|
|
269
|
+
this.modelResponses = [];
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// /**
|
|
273
|
+
// * The result of an agent run in streaming mode.
|
|
274
|
+
// */
|
|
275
|
+
// export class StreamedRunResult<
|
|
276
|
+
// TContext,
|
|
277
|
+
// TAgent extends Agent<TContext, AgentResponseType>,
|
|
278
|
+
// >
|
|
279
|
+
// extends RunResultBase<TContext, TAgent>
|
|
280
|
+
// implements AsyncIterable<ThreadStreamEvent>
|
|
281
|
+
// {
|
|
282
|
+
// /**
|
|
283
|
+
// * The current agent that is running
|
|
284
|
+
// */
|
|
285
|
+
// public get currentAgent(): TAgent | undefined {
|
|
286
|
+
// return this.lastAgent;
|
|
287
|
+
// }
|
|
288
|
+
// /**
|
|
289
|
+
// * The current turn number
|
|
290
|
+
// */
|
|
291
|
+
// public currentTurn: number = 0;
|
|
292
|
+
// /**
|
|
293
|
+
// * The maximum number of turns that can be run
|
|
294
|
+
// */
|
|
295
|
+
// public maxTurns: number | undefined;
|
|
296
|
+
// #error: unknown = null;
|
|
297
|
+
// #signal?: AbortSignal;
|
|
298
|
+
// #readableController:
|
|
299
|
+
// | ReadableStreamDefaultController<ThreadStreamEvent>
|
|
300
|
+
// | undefined;
|
|
301
|
+
// #readableStream: ReadableStream<ThreadStreamEvent>;
|
|
302
|
+
// #completedPromise: Promise<void>;
|
|
303
|
+
// #completedPromiseResolve: (() => void) | undefined;
|
|
304
|
+
// #completedPromiseReject: ((err: unknown) => void) | undefined;
|
|
305
|
+
// #cancelled: boolean = false;
|
|
306
|
+
// #streamLoopPromise: Promise<void> | undefined;
|
|
307
|
+
// constructor(
|
|
308
|
+
// result: {
|
|
309
|
+
// state: ThreadState<TContext, TAgent>;
|
|
310
|
+
// signal?: AbortSignal;
|
|
311
|
+
// } = {} as any,
|
|
312
|
+
// ) {
|
|
313
|
+
// super(result.state);
|
|
314
|
+
// this.#signal = result.signal;
|
|
315
|
+
// this.#readableStream = new ReadableStream<ThreadStreamEvent>({
|
|
316
|
+
// start: (controller) => {
|
|
317
|
+
// this.#readableController = controller;
|
|
318
|
+
// },
|
|
319
|
+
// cancel: () => {
|
|
320
|
+
// this.#cancelled = true;
|
|
321
|
+
// },
|
|
322
|
+
// });
|
|
323
|
+
// this.#completedPromise = new Promise((resolve, reject) => {
|
|
324
|
+
// this.#completedPromiseResolve = resolve;
|
|
325
|
+
// this.#completedPromiseReject = reject;
|
|
326
|
+
// });
|
|
327
|
+
// if (this.#signal) {
|
|
328
|
+
// const handleAbort = () => {
|
|
329
|
+
// if (this.#cancelled) {
|
|
330
|
+
// return;
|
|
331
|
+
// }
|
|
332
|
+
// this.#cancelled = true;
|
|
333
|
+
// const controller = this.#readableController;
|
|
334
|
+
// this.#readableController = undefined;
|
|
335
|
+
// if (this.#readableStream.locked) {
|
|
336
|
+
// if (controller) {
|
|
337
|
+
// try {
|
|
338
|
+
// controller.close();
|
|
339
|
+
// } catch (err) {
|
|
340
|
+
// logger.debug(`Failed to close readable stream on abort: ${err}`);
|
|
341
|
+
// }
|
|
342
|
+
// }
|
|
343
|
+
// } else {
|
|
344
|
+
// void this.#readableStream
|
|
345
|
+
// .cancel(this.#signal?.reason)
|
|
346
|
+
// .catch((err) => {
|
|
347
|
+
// logger.debug(`Failed to cancel readable stream on abort: ${err}`);
|
|
348
|
+
// });
|
|
349
|
+
// }
|
|
350
|
+
// this.#completedPromiseResolve?.();
|
|
351
|
+
// };
|
|
352
|
+
// if (this.#signal.aborted) {
|
|
353
|
+
// handleAbort();
|
|
354
|
+
// } else {
|
|
355
|
+
// this.#signal.addEventListener("abort", handleAbort, { once: true });
|
|
356
|
+
// }
|
|
357
|
+
// }
|
|
358
|
+
// }
|
|
359
|
+
// /**
|
|
360
|
+
// * @internal
|
|
361
|
+
// * Adds an item to the stream of output items
|
|
362
|
+
// */
|
|
363
|
+
// _addItem(item: ThreadStreamEvent) {
|
|
364
|
+
// if (!this.cancelled) {
|
|
365
|
+
// this.#readableController?.enqueue(item);
|
|
366
|
+
// }
|
|
367
|
+
// }
|
|
368
|
+
// /**
|
|
369
|
+
// * @internal
|
|
370
|
+
// * Indicates that the stream has been completed
|
|
371
|
+
// */
|
|
372
|
+
// _done() {
|
|
373
|
+
// if (!this.cancelled && this.#readableController) {
|
|
374
|
+
// this.#readableController.close();
|
|
375
|
+
// this.#readableController = undefined;
|
|
376
|
+
// this.#completedPromiseResolve?.();
|
|
377
|
+
// }
|
|
378
|
+
// }
|
|
379
|
+
// /**
|
|
380
|
+
// * @internal
|
|
381
|
+
// * Handles an error in the stream loop.
|
|
382
|
+
// */
|
|
383
|
+
// _raiseError(err: unknown) {
|
|
384
|
+
// if (!this.cancelled && this.#readableController) {
|
|
385
|
+
// this.#readableController.error(err);
|
|
386
|
+
// this.#readableController = undefined;
|
|
387
|
+
// }
|
|
388
|
+
// this.#error = err;
|
|
389
|
+
// this.#completedPromiseReject?.(err);
|
|
390
|
+
// this.#completedPromise.catch((e) => {
|
|
391
|
+
// logger.debug(`Resulted in an error: ${e}`);
|
|
392
|
+
// });
|
|
393
|
+
// }
|
|
394
|
+
// /**
|
|
395
|
+
// * Returns true if the stream has been cancelled.
|
|
396
|
+
// */
|
|
397
|
+
// get cancelled(): boolean {
|
|
398
|
+
// return this.#cancelled;
|
|
399
|
+
// }
|
|
400
|
+
// /**
|
|
401
|
+
// * Returns the underlying readable stream.
|
|
402
|
+
// * @returns A readable stream of the agent run.
|
|
403
|
+
// */
|
|
404
|
+
// toStream(): ReadableStream<ThreadStreamEvent> {
|
|
405
|
+
// return this.#readableStream as ReadableStream<ThreadStreamEvent>;
|
|
406
|
+
// }
|
|
407
|
+
// /**
|
|
408
|
+
// * Await this promise to ensure that the stream has been completed if you are not consuming the
|
|
409
|
+
// * stream directly.
|
|
410
|
+
// */
|
|
411
|
+
// get completed() {
|
|
412
|
+
// return this.#completedPromise;
|
|
413
|
+
// }
|
|
414
|
+
// /**
|
|
415
|
+
// * Error thrown during the run, if any.
|
|
416
|
+
// */
|
|
417
|
+
// get error() {
|
|
418
|
+
// return this.#error;
|
|
419
|
+
// }
|
|
420
|
+
// /**
|
|
421
|
+
// * Returns a readable stream of the final text output of the agent run.
|
|
422
|
+
// *
|
|
423
|
+
// * @returns A readable stream of the final output of the agent run.
|
|
424
|
+
// * @remarks Pass `{ compatibleWithNodeStreams: true }` to receive a Node.js compatible stream
|
|
425
|
+
// * instance.
|
|
426
|
+
// */
|
|
427
|
+
// toTextStream(): ReadableStream<string>;
|
|
428
|
+
// toTextStream(options?: { compatibleWithNodeStreams: true }): Readable;
|
|
429
|
+
// toTextStream(options?: {
|
|
430
|
+
// compatibleWithNodeStreams?: false;
|
|
431
|
+
// }): ReadableStream<string>;
|
|
432
|
+
// toTextStream(
|
|
433
|
+
// options: { compatibleWithNodeStreams?: boolean } = {},
|
|
434
|
+
// ): Readable | ReadableStream<string> {
|
|
435
|
+
// const stream = this.#readableStream.pipeThrough(
|
|
436
|
+
// new TransformStream<ThreadStreamEvent, string>({
|
|
437
|
+
// transform(event, controller) {
|
|
438
|
+
// if (
|
|
439
|
+
// event.kind === "raw_model_stream_event" && // (TODO): what to do here?
|
|
440
|
+
// event.data.kind === "text-delta"
|
|
441
|
+
// ) {
|
|
442
|
+
// const item = TextDeltaEvent.parse(event); // ??
|
|
443
|
+
// controller.enqueue(item.text); // (TODO): is it just the text that we want to return here?
|
|
444
|
+
// }
|
|
445
|
+
// },
|
|
446
|
+
// }),
|
|
447
|
+
// );
|
|
448
|
+
// if (options.compatibleWithNodeStreams) {
|
|
449
|
+
// return Readable.fromWeb(stream);
|
|
450
|
+
// }
|
|
451
|
+
// return stream as ReadableStream<string>;
|
|
452
|
+
// }
|
|
453
|
+
// [Symbol.asyncIterator](): AsyncIterator<ThreadStreamEvent> {
|
|
454
|
+
// return this.#readableStream[Symbol.asyncIterator]();
|
|
455
|
+
// }
|
|
456
|
+
// /**
|
|
457
|
+
// * @internal
|
|
458
|
+
// * Sets the stream loop promise that completes when the internal stream loop finishes.
|
|
459
|
+
// * This is used to defer trace end until all agent work is complete.
|
|
460
|
+
// */
|
|
461
|
+
// _setStreamLoopPromise(promise: Promise<void>) {
|
|
462
|
+
// this.#streamLoopPromise = promise;
|
|
463
|
+
// }
|
|
464
|
+
// /**
|
|
465
|
+
// * @internal
|
|
466
|
+
// * Returns a promise that resolves when the stream loop completes.
|
|
467
|
+
// * This is used by the tracing system to wait for all agent work before ending the trace.
|
|
468
|
+
// */
|
|
469
|
+
// _getStreamLoopPromise(): Promise<void> | undefined {
|
|
470
|
+
// return this.#streamLoopPromise;
|
|
471
|
+
// }
|
|
472
|
+
// }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ResolvedAgentResponse } from "../guardrail";
|
|
2
|
+
import type { AgentResponseType } from "../types/agent";
|
|
3
|
+
import type { ThreadEvent } from "../types/thread";
|
|
4
|
+
/**
|
|
5
|
+
* Extract the final text response from a list of events.
|
|
6
|
+
* Returns null if no assistant message with text content is found.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getFinalResponse(events: ThreadEvent[]): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* (TODO): This should run through the language model's native structured output (if avail)
|
|
11
|
+
*
|
|
12
|
+
* Parse the final response according to the response type schema.
|
|
13
|
+
* - If responseType is "text", returns the text as-is
|
|
14
|
+
* - If responseType is a ZodType, parses and validates the text as JSON
|
|
15
|
+
*
|
|
16
|
+
* @throws {ModelBehaviorError} if structured output validation fails
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseFinalResponse<TResponse extends AgentResponseType>(text: string, responseType: TResponse): ResolvedAgentResponse<TResponse>;
|
|
19
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/thread/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAOzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,IAAI,CAcrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,SAAS,iBAAiB,EACpE,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,SAAS,GACtB,qBAAqB,CAAC,SAAS,CAAC,CAsBlC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* lib */
|
|
2
|
+
import { json } from "@kernl/shared/lib";
|
|
3
|
+
import { ModelBehaviorError } from "../lib/error";
|
|
4
|
+
/**
|
|
5
|
+
* Extract the final text response from a list of events.
|
|
6
|
+
* Returns null if no assistant message with text content is found.
|
|
7
|
+
*/
|
|
8
|
+
export function getFinalResponse(events) {
|
|
9
|
+
// Scan backwards for the last assistant message
|
|
10
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
11
|
+
const event = events[i];
|
|
12
|
+
if (event.kind === "message" && event.role === "assistant") {
|
|
13
|
+
// Extract text from content parts
|
|
14
|
+
for (const part of event.content) {
|
|
15
|
+
if (part.kind === "text") {
|
|
16
|
+
return part.text;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* (TODO): This should run through the language model's native structured output (if avail)
|
|
25
|
+
*
|
|
26
|
+
* Parse the final response according to the response type schema.
|
|
27
|
+
* - If responseType is "text", returns the text as-is
|
|
28
|
+
* - If responseType is a ZodType, parses and validates the text as JSON
|
|
29
|
+
*
|
|
30
|
+
* @throws {ModelBehaviorError} if structured output validation fails
|
|
31
|
+
*/
|
|
32
|
+
export function parseFinalResponse(text, responseType) {
|
|
33
|
+
if (responseType === "text") {
|
|
34
|
+
return text; // text output - return as-is
|
|
35
|
+
}
|
|
36
|
+
// structured output - decode JSON and validate with schema
|
|
37
|
+
if (responseType && typeof responseType === "object") {
|
|
38
|
+
// (TODO): prob better way of checking this here
|
|
39
|
+
const schema = responseType;
|
|
40
|
+
try {
|
|
41
|
+
const validated = json(schema).decode(text); // (TODO): it would be nice if we could use `decodeSafe` here
|
|
42
|
+
return validated;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new ModelBehaviorError(`Failed to parse structured output: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Fallback - should not reach here
|
|
49
|
+
return text;
|
|
50
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Context } from "../../context";
|
|
3
|
+
import { HostedTool } from "../tool";
|
|
4
|
+
/**
|
|
5
|
+
* Create a minimal mock context for testing
|
|
6
|
+
*/
|
|
7
|
+
export declare const mockContext: <T = any>(data?: T) => Context<T>;
|
|
8
|
+
/**
|
|
9
|
+
* Simple string tool with no parameters
|
|
10
|
+
*/
|
|
11
|
+
export declare const simpleStringTool: import("..").FunctionTool<unknown, undefined, string>;
|
|
12
|
+
/**
|
|
13
|
+
* Tool with Zod schema validation
|
|
14
|
+
*/
|
|
15
|
+
export declare const zodTool: import("..").FunctionTool<unknown, z.ZodObject<{
|
|
16
|
+
name: z.ZodString;
|
|
17
|
+
age: z.ZodNumber;
|
|
18
|
+
}, z.core.$strip>, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Tool that always throws an error
|
|
21
|
+
*/
|
|
22
|
+
export declare const errorTool: import("..").FunctionTool<unknown, undefined, never>;
|
|
23
|
+
/**
|
|
24
|
+
* Tool with custom error handler
|
|
25
|
+
*/
|
|
26
|
+
export declare const customErrorTool: import("..").FunctionTool<unknown, undefined, never>;
|
|
27
|
+
/**
|
|
28
|
+
* Tool requiring approval (boolean)
|
|
29
|
+
*/
|
|
30
|
+
export declare const approvalRequiredTool: import("..").FunctionTool<unknown, undefined, string>;
|
|
31
|
+
/**
|
|
32
|
+
* Tool with conditional approval (function)
|
|
33
|
+
*/
|
|
34
|
+
export declare const conditionalApprovalTool: import("..").FunctionTool<unknown, z.ZodObject<{
|
|
35
|
+
dangerous: z.ZodBoolean;
|
|
36
|
+
}, z.core.$strip>, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Mock hosted tool (e.g., web_search)
|
|
39
|
+
*/
|
|
40
|
+
export declare const mockHostedTool: HostedTool;
|
|
41
|
+
/**
|
|
42
|
+
* Another mock hosted tool
|
|
43
|
+
*/
|
|
44
|
+
export declare const anotherHostedTool: HostedTool;
|
|
45
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../../src/tool/__tests__/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAQ,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3C;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,GAAG,GAAG,EAAE,OAAO,CAAC,KAAG,OAAO,CAAC,CAAC,CAExD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,uDAK3B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,OAAO;;;0BAQlB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,sDAOpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe,sDAU1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,oBAAoB,uDAM/B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,uBAAuB;;0BAUlC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,YAOzB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,YAG5B,CAAC"}
|