|
|
@@ -1,11 +1,12 @@
|
|
|
-import { describe, test, expect } from "bun:test"
|
|
|
-import { Effect, Layer, ManagedRuntime, Schema } from "effect"
|
|
|
+import { describe, expect } from "bun:test"
|
|
|
+import { Effect, Layer, Schema } from "effect"
|
|
|
import { Agent } from "../../src/agent/agent"
|
|
|
import { MessageID, SessionID } from "../../src/session/schema"
|
|
|
import { Tool } from "@/tool/tool"
|
|
|
import { Truncate } from "@/tool/truncate"
|
|
|
+import { testEffect } from "../lib/effect"
|
|
|
|
|
|
-const runtime = ManagedRuntime.make(Layer.mergeAll(Truncate.defaultLayer, Agent.defaultLayer))
|
|
|
+const it = testEffect(Layer.mergeAll(Truncate.defaultLayer, Agent.defaultLayer))
|
|
|
|
|
|
const params = Schema.Struct({ input: Schema.String })
|
|
|
|
|
|
@@ -21,49 +22,53 @@ function makeTool(id: string, executeFn?: () => void) {
|
|
|
}
|
|
|
|
|
|
describe("Tool.define", () => {
|
|
|
- test("object-defined tool does not mutate the original init object", async () => {
|
|
|
- const original = makeTool("test")
|
|
|
- const originalExecute = original.execute
|
|
|
+ it.effect("object-defined tool does not mutate the original init object", () =>
|
|
|
+ Effect.gen(function* () {
|
|
|
+ const original = makeTool("test")
|
|
|
+ const originalExecute = original.execute
|
|
|
|
|
|
- const info = await runtime.runPromise(Tool.define("test-tool", Effect.succeed(original)))
|
|
|
+ const info = yield* Tool.define("test-tool", Effect.succeed(original))
|
|
|
|
|
|
- await Effect.runPromise(info.init())
|
|
|
- await Effect.runPromise(info.init())
|
|
|
- await Effect.runPromise(info.init())
|
|
|
+ yield* info.init()
|
|
|
+ yield* info.init()
|
|
|
+ yield* info.init()
|
|
|
|
|
|
- expect(original.execute).toBe(originalExecute)
|
|
|
- })
|
|
|
+ expect(original.execute).toBe(originalExecute)
|
|
|
+ }),
|
|
|
+ )
|
|
|
|
|
|
- test("effect-defined tool returns fresh objects and is unaffected", async () => {
|
|
|
- const info = await runtime.runPromise(
|
|
|
- Tool.define(
|
|
|
+ it.effect("effect-defined tool returns fresh objects and is unaffected", () =>
|
|
|
+ Effect.gen(function* () {
|
|
|
+ const info = yield* Tool.define(
|
|
|
"test-fn-tool",
|
|
|
Effect.succeed(() => Effect.succeed(makeTool("test"))),
|
|
|
- ),
|
|
|
- )
|
|
|
+ )
|
|
|
|
|
|
- const first = await Effect.runPromise(info.init())
|
|
|
- const second = await Effect.runPromise(info.init())
|
|
|
+ const first = yield* info.init()
|
|
|
+ const second = yield* info.init()
|
|
|
|
|
|
- expect(first).not.toBe(second)
|
|
|
- })
|
|
|
+ expect(first).not.toBe(second)
|
|
|
+ }),
|
|
|
+ )
|
|
|
|
|
|
- test("object-defined tool returns distinct objects per init() call", async () => {
|
|
|
- const info = await runtime.runPromise(Tool.define("test-copy", Effect.succeed(makeTool("test"))))
|
|
|
+ it.effect("object-defined tool returns distinct objects per init() call", () =>
|
|
|
+ Effect.gen(function* () {
|
|
|
+ const info = yield* Tool.define("test-copy", Effect.succeed(makeTool("test")))
|
|
|
|
|
|
- const first = await Effect.runPromise(info.init())
|
|
|
- const second = await Effect.runPromise(info.init())
|
|
|
+ const first = yield* info.init()
|
|
|
+ const second = yield* info.init()
|
|
|
|
|
|
- expect(first).not.toBe(second)
|
|
|
- })
|
|
|
+ expect(first).not.toBe(second)
|
|
|
+ }),
|
|
|
+ )
|
|
|
|
|
|
- test("execute receives decoded parameters", async () => {
|
|
|
- const parameters = Schema.Struct({
|
|
|
- count: Schema.NumberFromString.pipe(Schema.optional, Schema.withDecodingDefaultType(Effect.succeed(5))),
|
|
|
- })
|
|
|
- const calls: Array<Schema.Schema.Type<typeof parameters>> = []
|
|
|
- const info = await runtime.runPromise(
|
|
|
- Tool.define(
|
|
|
+ it.effect("execute receives decoded parameters", () =>
|
|
|
+ Effect.gen(function* () {
|
|
|
+ const parameters = Schema.Struct({
|
|
|
+ count: Schema.NumberFromString.pipe(Schema.optional, Schema.withDecodingDefaultType(Effect.succeed(5))),
|
|
|
+ })
|
|
|
+ const calls: Array<Schema.Schema.Type<typeof parameters>> = []
|
|
|
+ const info = yield* Tool.define(
|
|
|
"test-decoded",
|
|
|
Effect.succeed({
|
|
|
description: "test tool",
|
|
|
@@ -73,27 +78,27 @@ describe("Tool.define", () => {
|
|
|
return Effect.succeed({ title: "test", output: "ok", metadata: { truncated: false } })
|
|
|
},
|
|
|
}),
|
|
|
- ),
|
|
|
- )
|
|
|
- const ctx: Tool.Context = {
|
|
|
- sessionID: SessionID.descending(),
|
|
|
- messageID: MessageID.ascending(),
|
|
|
- agent: "build",
|
|
|
- abort: new AbortController().signal,
|
|
|
- messages: [],
|
|
|
- metadata() {
|
|
|
- return Effect.void
|
|
|
- },
|
|
|
- ask() {
|
|
|
- return Effect.void
|
|
|
- },
|
|
|
- }
|
|
|
- const tool = await Effect.runPromise(info.init())
|
|
|
- const execute = tool.execute as unknown as (args: unknown, ctx: Tool.Context) => ReturnType<typeof tool.execute>
|
|
|
-
|
|
|
- await Effect.runPromise(execute({}, ctx))
|
|
|
- await Effect.runPromise(execute({ count: "7" }, ctx))
|
|
|
-
|
|
|
- expect(calls).toEqual([{ count: 5 }, { count: 7 }])
|
|
|
- })
|
|
|
+ )
|
|
|
+ const ctx: Tool.Context = {
|
|
|
+ sessionID: SessionID.descending(),
|
|
|
+ messageID: MessageID.ascending(),
|
|
|
+ agent: "build",
|
|
|
+ abort: new AbortController().signal,
|
|
|
+ messages: [],
|
|
|
+ metadata() {
|
|
|
+ return Effect.void
|
|
|
+ },
|
|
|
+ ask() {
|
|
|
+ return Effect.void
|
|
|
+ },
|
|
|
+ }
|
|
|
+ const tool = yield* info.init()
|
|
|
+ const execute = tool.execute as unknown as (args: unknown, ctx: Tool.Context) => ReturnType<typeof tool.execute>
|
|
|
+
|
|
|
+ yield* execute({}, ctx)
|
|
|
+ yield* execute({ count: "7" }, ctx)
|
|
|
+
|
|
|
+ expect(calls).toEqual([{ count: 5 }, { count: 7 }])
|
|
|
+ }),
|
|
|
+ )
|
|
|
})
|