Tool Use
Language models are great at generating text, but often we need them to take real-world actions, such as querying an API, accessing a database, or calling a service. Most LLM providers support this through tool use (also known as function calling), where you expose specific operations in your application that the model can invoke.
Based on the input it receives, a model may choose to invoke (or call) one or more tools to augment its response. Your application then runs the corresponding logic for the tool using the parameters provided by the model. You then return the result to the model, allowing it to include the output in its final response.
The Toolkit
simplifies tool integration by offering a structured, type-safe approach to defining tools. It takes care of all the wiring between the model and your application - all you have to do is define the tool and implement its behavior.
Let’s walk through a complete example of how to define, implement, and use a tool that fetches a dad joke from the icanhazdadjoke.com API.
We start by defining a tool that the language model will have access to using the Tool.make
constructor.
This constructor accepts several parameters that allow us to fully describe the tool to the language model:
description
: Provides an optional description of the toolsuccess
: The type of value the tool will return if it succeedsfailure
: The type of value the tool will return if it failsparameters
: The parameters that the tool should be called with
Example (Defining a Tool)
import { import Tool
Tool } from "@effect/ai"import { import Schema
Schema } from "effect"
const const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke = import Tool
Tool.const make: <"GetDadJoke", { searchTerm: Schema.SchemaClass<string, string, never>;}, typeof Schema.String, typeof Schema.Never, []>(name: "GetDadJoke", options?: { readonly description?: string | undefined; readonly parameters?: { searchTerm: Schema.SchemaClass<string, string, never>; } | undefined; readonly success?: typeof Schema.String | undefined; readonly failure?: typeof Schema.Never | undefined; readonly dependencies?: [] | undefined;} | undefined) => Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
Creates a user-defined tool with the specified name and configuration.
This is the primary constructor for creating custom tools that AI models
can call. The tool definition includes parameter validation, success/failure
schemas, and optional service dependencies.
make("GetDadJoke", { description?: string | undefined
An optional description explaining what the tool does.
description: "Get a hilarious dad joke from the ICanHazDadJoke API", success?: typeof Schema.String | undefined
Schema for successful tool execution results.
success: import Schema
Schema.class Stringexport String
String, failure?: typeof Schema.Never | undefined
Schema for tool execution failures.
failure: import Schema
Schema.class Never
Never, parameters?: { searchTerm: Schema.SchemaClass<string, string, never>;} | undefined
Schema defining the parameters this tool accepts.
parameters: { searchTerm: Schema.SchemaClass<string, string, never>
searchTerm: import Schema
Schema.class Stringexport String
String.Annotable<SchemaClass<string, string, never>, string, string, never>.annotations(annotations: Schema.Annotations.GenericSchema<string>): Schema.SchemaClass<string, string, never>
Merges a set of new annotations with existing ones, potentially overwriting
any duplicates.
annotations({ Annotations.Doc<string>.description?: string
description: "The search term to use to find dad jokes" }) }})
Based on the above, a request to call the GetDadJoke
tool:
- Takes a single
searchTerm
parameter - Will return a string if it succeeds (i.e. the joke)
- Does not have any expected failure scenarios
Once we have a tool request defined, we can create a Toolkit
, which is a collection of tools that the model will have access to.
Example (Creating a Toolkit
)
import { import Tool
Tool, import Toolkit
Toolkit } from "@effect/ai"import { import Schema
Schema } from "effect"
const const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke = import Tool
Tool.const make: <"GetDadJoke", { searchTerm: Schema.SchemaClass<string, string, never>;}, typeof Schema.String, typeof Schema.Never, []>(name: "GetDadJoke", options?: { readonly description?: string | undefined; readonly parameters?: { searchTerm: Schema.SchemaClass<string, string, never>; } | undefined; readonly success?: typeof Schema.String | undefined; readonly failure?: typeof Schema.Never | undefined; readonly dependencies?: [] | undefined;} | undefined) => Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
Creates a user-defined tool with the specified name and configuration.
This is the primary constructor for creating custom tools that AI models
can call. The tool definition includes parameter validation, success/failure
schemas, and optional service dependencies.
make("GetDadJoke", { description?: string | undefined
An optional description explaining what the tool does.
description: "Get a hilarious dad joke from the ICanHazDadJoke API", success?: typeof Schema.String | undefined
Schema for successful tool execution results.
success: import Schema
Schema.class Stringexport String
String, failure?: typeof Schema.Never | undefined
Schema for tool execution failures.
failure: import Schema
Schema.class Never
Never, parameters?: { searchTerm: Schema.SchemaClass<string, string, never>;} | undefined
Schema defining the parameters this tool accepts.
parameters: { searchTerm: Schema.SchemaClass<string, string, never>
searchTerm: import Schema
Schema.class Stringexport String
String.Annotable<SchemaClass<string, string, never>, string, string, never>.annotations(annotations: Schema.Annotations.GenericSchema<string>): Schema.SchemaClass<string, string, never>
Merges a set of new annotations with existing ones, potentially overwriting
any duplicates.
annotations({ Annotations.Doc<string>.description?: string
description: "The search term to use to find dad jokes" }) }})
const const DadJokeTools: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
DadJokeTools = import Toolkit
Toolkit.const make: <[Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>]>(tools_0: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>) => Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
Creates a new toolkit from the specified tools.
This is the primary constructor for creating toolkits. It accepts multiple tools
and organizes them into a toolkit that can be provided to AI language models.
Tools can be either Tool instances or TaggedRequest schemas.
make(const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke)
The .toLayer(...)
method on a Toolkit
allows you to define the handlers for each tool in the toolkit. Because .toLayer(...)
takes an Effect
, we can access services from our application to implement the tool call handlers.
Example (Implementing a Toolkit
)
import { import Tool
Tool, import Toolkit
Toolkit } from "@effect/ai"import { import HttpClient
HttpClient, import HttpClientRequest
HttpClientRequest, import HttpClientResponse
HttpClientResponse} from "@effect/platform"import { import NodeHttpClient
NodeHttpClient } from "@effect/platform-node"import { import Array
Array, import Effect
Effect, import Schema
Schema } from "effect"
37 collapsed lines
class class DadJoke
DadJoke extends import Schema
Schema.const Class: <DadJoke>(identifier: string) => <Fields>(fieldsOr: Fields | HasFields<Fields>, annotations?: ClassAnnotations<DadJoke, { [K in keyof Schema.Struct<Fields extends Schema.Struct.Fields>.Type<Fields>]: Schema.Struct.Type<Fields>[K]; }> | undefined) => Schema.Class<DadJoke, Fields, Schema.Struct.Encoded<Fields>, Schema.Schema<in out A, in out I = A, out R = never>.Context<Fields[keyof Fields]>, Schema.Struct.Constructor<...>, {}, {}>
Class<class DadJoke
DadJoke>("DadJoke")({ id: typeof Schema.String
id: import Schema
Schema.class Stringexport String
String, joke: typeof Schema.String
joke: import Schema
Schema.class Stringexport String
String}) {}
class class SearchResponse
SearchResponse extends import Schema
Schema.const Class: <SearchResponse>(identifier: string) => <Fields>(fieldsOr: Fields | HasFields<Fields>, annotations?: ClassAnnotations<SearchResponse, { [K in keyof Schema.Struct<Fields extends Schema.Struct.Fields>.Type<Fields>]: Schema.Struct.Type<Fields>[K]; }> | undefined) => Schema.Class<SearchResponse, Fields, Schema.Struct.Encoded<Fields>, Schema.Schema<in out A, in out I = A, out R = never>.Context<Fields[keyof Fields]>, Schema.Struct.Constructor<...>, {}, {}>
Class<class SearchResponse
SearchResponse>("SearchResponse")({ results: Schema.Array$<typeof DadJoke>
results: import Schema
Schema.Array<typeof DadJoke>(value: typeof DadJoke): Schema.Array$<typeof DadJoke>export Array
Array(class DadJoke
DadJoke)}) {}
class class ICanHazDadJoke
ICanHazDadJoke extends import Effect
Effect.const Service: <ICanHazDadJoke>() => { <Key, Make>(key: Key, make: Make): Effect.Service.Class<ICanHazDadJoke, Key, Make>; <Key, Make>(key: Key, make: Make): Effect.Service.Class<ICanHazDadJoke, Key, Make>; <Key, Make>(key: Key, make: Make): Effect.Service.Class<ICanHazDadJoke, Key, Make>; <Key, Make>(key: Key, make: Make): Effect.Service.Class<ICanHazDadJoke, Key, Make>; <Key, Make>(key: Key, make: Make): Effect.Service.Class<...>;}
Simplifies the creation and management of services in Effect by defining both
a Tag
and a Layer
.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag
and a Layer
in a single step. It supports
various ways of providing the service implementation:
- Using an
effect
to define the service dynamically.
- Using
sync
or succeed
to define the service statically.
- Using
scoped
to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
Example
import { Effect } from 'effect';
class Prefix extends Effect.Service<Prefix>()("Prefix", { sync: () => ({ prefix: "PRE" })}) {}
class Logger extends Effect.Service<Logger>()("Logger", { accessors: true, effect: Effect.gen(function* () { const { prefix } = yield* Prefix return { info: (message: string) => Effect.sync(() => { console.log(`[${prefix}][${message}]`) }) } }), dependencies: [Prefix.Default]}) {}
Service<class ICanHazDadJoke
ICanHazDadJoke>()("ICanHazDadJoke", { dependencies: readonly [Layer<HttpClient.HttpClient, never, never>]
dependencies: [import NodeHttpClient
NodeHttpClient.const layerUndici: Layer<HttpClient.HttpClient, never, never>
layerUndici], effect: Effect.Effect<{ readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}, never, HttpClient.HttpClient>
effect: import Effect
Effect.const gen: <YieldWrap<Tag<HttpClient.HttpClient, HttpClient.HttpClient>>, { readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Tag<HttpClient.HttpClient, HttpClient.HttpClient>>, { readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}, never>) => Effect.Effect<{ readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}, never, HttpClient.HttpClient> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen
allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await
but with more
explicit control over the execution of effects. You can yield*
values from
effects and return the final result at the end.
Example
import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () { const transactionAmount = yield* fetchTransactionAmount const discountRate = yield* fetchDiscountRate const discountedAmount = yield* applyDiscount( transactionAmount, discountRate ) const finalAmount = addServiceCharge(discountedAmount) return `Final amount to charge: ${finalAmount}`})
gen(function*() { const const httpClient: HttpClient.HttpClient
httpClient = yield* import HttpClient
HttpClient.const HttpClient: Tag<HttpClient.HttpClient, HttpClient.HttpClient>
HttpClient const const httpClientOk: HttpClient.HttpClient.With<HttpClientError, never>
httpClientOk = const httpClient: HttpClient.HttpClient
httpClient.Pipeable.pipe<HttpClient.HttpClient, HttpClient.HttpClient.With<HttpClientError, never>, HttpClient.HttpClient.With<HttpClientError, never>>(this: HttpClient.HttpClient, ab: (_: HttpClient.HttpClient) => HttpClient.HttpClient.With<HttpClientError, never>, bc: (_: HttpClient.HttpClient.With<HttpClientError, never>) => HttpClient.HttpClient.With<HttpClientError, never>): HttpClient.HttpClient.With<...> (+21 overloads)
pipe( import HttpClient
HttpClient.const filterStatusOk: <E, R>(self: HttpClient.HttpClient.With<E, R>) => HttpClient.HttpClient.With<E | ResponseError, R>
Filters responses that return a 2xx status code.
filterStatusOk, import HttpClient
HttpClient.const mapRequest: (f: (a: HttpClientRequest.HttpClientRequest) => HttpClientRequest.HttpClientRequest) => <E, R>(self: HttpClient.HttpClient.With<E, R>) => HttpClient.HttpClient.With<E, R> (+1 overload)
Appends a transformation of the request object before sending it.
mapRequest(import HttpClientRequest
HttpClientRequest.const prependUrl: (path: string) => (self: HttpClientRequest.HttpClientRequest) => HttpClientRequest.HttpClientRequest (+1 overload)
prependUrl("https://icanhazdadjoke.com")) )
const const search: (searchTerm: string) => Effect.Effect<string, never, never>
search = import Effect
Effect.const fn: (name: string, options?: SpanOptions) => Effect.fn.Gen & Effect.fn.NonGen (+20 overloads)
fn("ICanHazDadJoke.search")( function*(searchTerm: string
searchTerm: string) { return yield* const httpClientOk: HttpClient.HttpClient.With<HttpClientError, never>
httpClientOk.HttpClient.With<HttpClientError, never>.get: (url: string | URL, options?: HttpClientRequest.Options.NoBody) => Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError, never>
get("/search", { acceptJson?: boolean | undefined
acceptJson: true, urlParams?: Input | undefined
urlParams: { searchTerm: string
searchTerm } }).Pipeable.pipe<Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError, never>, Effect.Effect<SearchResponse, HttpClientError | ParseError, never>, Effect.Effect<DadJoke, HttpClientError | ParseError | NoSuchElementException, never>, Effect.Effect<string, HttpClientError | ParseError | NoSuchElementException, never>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>, cd: (_: Effect.Effect<...>) => Effect.Effect<...>, de: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe( import Effect
Effect.const flatMap: <HttpClientResponse.HttpClientResponse, SearchResponse, ResponseError | ParseError, never>(f: (a: HttpClientResponse.HttpClientResponse) => Effect.Effect<SearchResponse, ResponseError | ParseError, never>) => <E, R>(self: Effect.Effect<HttpClientResponse.HttpClientResponse, E, R>) => Effect.Effect<SearchResponse, ResponseError | ParseError | E, R> (+1 overload)
Chains effects to produce new Effect
instances, useful for combining
operations that depend on previous results.
Syntax
const flatMappedEffect = pipe(myEffect, Effect.flatMap(transformation))// orconst flatMappedEffect = Effect.flatMap(myEffect, transformation)// orconst flatMappedEffect = myEffect.pipe(Effect.flatMap(transformation))
Details
flatMap
lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap
used with arrays but works
specifically with Effect
instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap
always returns a new effect instead of
changing the original one.
When to Use
Use flatMap
when you need to chain multiple effects, ensuring that each
step produces a new Effect
while flattening any nested effects that may
occur.
Example
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amountconst applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from databaseconst fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`const finalAmount = pipe( fetchTransactionAmount, Effect.flatMap((amount) => applyDiscount(amount, 5)))
Effect.runPromise(finalAmount).then(console.log)// Output: 95
flatMap(import HttpClientResponse
HttpClientResponse.schemaBodyJson<SearchResponse, { readonly results: readonly { readonly id: string; readonly joke: string; }[];}, never>(schema: Schema.Schema<SearchResponse, { readonly results: readonly { readonly id: string; readonly joke: string; }[];}, never>, options?: ParseOptions | undefined): <E>(self: HttpIncomingMessage<E>) => Effect.Effect<SearchResponse, ParseError | E, never>export schemaBodyJson
schemaBodyJson(class SearchResponse
SearchResponse)), import Effect
Effect.const flatMap: <SearchResponse, DadJoke, NoSuchElementException, never>(f: (a: SearchResponse) => Effect.Effect<DadJoke, NoSuchElementException, never>) => <E, R>(self: Effect.Effect<SearchResponse, E, R>) => Effect.Effect<DadJoke, NoSuchElementException | E, R> (+1 overload)
Chains effects to produce new Effect
instances, useful for combining
operations that depend on previous results.
Syntax
const flatMappedEffect = pipe(myEffect, Effect.flatMap(transformation))// orconst flatMappedEffect = Effect.flatMap(myEffect, transformation)// orconst flatMappedEffect = myEffect.pipe(Effect.flatMap(transformation))
Details
flatMap
lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap
used with arrays but works
specifically with Effect
instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap
always returns a new effect instead of
changing the original one.
When to Use
Use flatMap
when you need to chain multiple effects, ensuring that each
step produces a new Effect
while flattening any nested effects that may
occur.
Example
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amountconst applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from databaseconst fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`const finalAmount = pipe( fetchTransactionAmount, Effect.flatMap((amount) => applyDiscount(amount, 5)))
Effect.runPromise(finalAmount).then(console.log)// Output: 95
flatMap(({ results: readonly DadJoke[]
results }) => import Array
Array.const head: <DadJoke>(self: readonly DadJoke[]) => Option<DadJoke>
Get the first element of a ReadonlyArray
, or None
if the ReadonlyArray
is empty.
head(results: readonly DadJoke[]
results)), import Effect
Effect.const map: <DadJoke, string>(f: (a: DadJoke) => string) => <E, R>(self: Effect.Effect<DadJoke, E, R>) => Effect.Effect<string, E, R> (+1 overload)
Transforms the value inside an effect by applying a function to it.
Syntax
const mappedEffect = pipe(myEffect, Effect.map(transformation))// orconst mappedEffect = Effect.map(myEffect, transformation)// orconst mappedEffect = myEffect.pipe(Effect.map(transformation))
Details
map
takes a function and applies it to the value contained within an
effect, creating a new effect with the transformed value.
It's important to note that effects are immutable, meaning that the original
effect is not modified. Instead, a new effect is returned with the updated
value.
Example (Adding a Service Charge)
import { pipe, Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const finalAmount = pipe( fetchTransactionAmount, Effect.map(addServiceCharge))
Effect.runPromise(finalAmount).then(console.log)// Output: 101
map((joke: DadJoke
joke) => joke: DadJoke
joke.joke: string
joke), import Effect
Effect.const orDie: <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, never, R>
Converts an effect's failure into a fiber termination, removing the error
from the effect's type.
Details
The orDie
function is used when you encounter errors that you do not want
to handle or recover from. It removes the error type from the effect and
ensures that any failure will terminate the fiber. This is useful for
propagating failures as defects, signaling that they should not be handled
within the effect.
*When to Use
Use orDie
when failures should be treated as unrecoverable defects and no
error handling is required.
Example (Propagating an Error as a Defect)
import { Effect } from "effect"
const divide = (a: number, b: number) => b === 0 ? Effect.fail(new Error("Cannot divide by zero")) : Effect.succeed(a / b)
// ┌─── Effect<number, never, never>// ▼const program = Effect.orDie(divide(1, 0))
Effect.runPromise(program).catch(console.error)// Output:// (FiberFailure) Error: Cannot divide by zero// ...stack trace...
orDie ) } )
return { search: (searchTerm: string) => Effect.Effect<string, never, never>
search } as type const = { readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}
const })}) {}
const const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke = import Tool
Tool.const make: <"GetDadJoke", { searchTerm: Schema.SchemaClass<string, string, never>;}, typeof Schema.String, typeof Schema.Never, []>(name: "GetDadJoke", options?: { readonly description?: string | undefined; readonly parameters?: { searchTerm: Schema.SchemaClass<string, string, never>; } | undefined; readonly success?: typeof Schema.String | undefined; readonly failure?: typeof Schema.Never | undefined; readonly dependencies?: [] | undefined;} | undefined) => Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
Creates a user-defined tool with the specified name and configuration.
This is the primary constructor for creating custom tools that AI models
can call. The tool definition includes parameter validation, success/failure
schemas, and optional service dependencies.
make("GetDadJoke", { description?: string | undefined
An optional description explaining what the tool does.
description: "Get a hilarious dad joke from the ICanHazDadJoke API", success?: typeof Schema.String | undefined
Schema for successful tool execution results.
success: import Schema
Schema.class Stringexport String
String, failure?: typeof Schema.Never | undefined
Schema for tool execution failures.
failure: import Schema
Schema.class Never
Never, parameters?: { searchTerm: Schema.SchemaClass<string, string, never>;} | undefined
Schema defining the parameters this tool accepts.
parameters: { searchTerm: Schema.SchemaClass<string, string, never>
searchTerm: import Schema
Schema.class Stringexport String
String.Annotable<SchemaClass<string, string, never>, string, string, never>.annotations(annotations: Schema.Annotations.GenericSchema<string>): Schema.SchemaClass<string, string, never>
Merges a set of new annotations with existing ones, potentially overwriting
any duplicates.
annotations({ Annotations.Doc<string>.description?: string
description: "The search term to use to find dad jokes" }) }})
const const DadJokeTools: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
DadJokeTools = import Toolkit
Toolkit.const make: <[Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>]>(tools_0: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>) => Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
Creates a new toolkit from the specified tools.
This is the primary constructor for creating toolkits. It accepts multiple tools
and organizes them into a toolkit that can be provided to AI language models.
Tools can be either Tool instances or TaggedRequest schemas.
make(const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke)
const const DadJokeToolHandlers: Layer<Tool.Handler<"GetDadJoke">, never, ICanHazDadJoke>
DadJokeToolHandlers = const DadJokeTools: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
DadJokeTools.Toolkit<{ readonly GetDadJoke: Tool<"GetDadJoke", { readonly parameters: Struct<{ searchTerm: SchemaClass<string, string, never>; }>; readonly success: typeof String$; readonly failure: typeof Never; }, never>; }>.toLayer<{ GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}, never, ICanHazDadJoke>(build: { GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;} | Effect.Effect<{ GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}, never, ICanHazDadJoke>): Layer<Tool.Handler<"GetDadJoke">, never, ICanHazDadJoke>
Converts a toolkit into a Layer containing handlers for each tool in the
toolkit.
toLayer( import Effect
Effect.const gen: <YieldWrap<Tag<ICanHazDadJoke, ICanHazDadJoke>>, { GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Tag<ICanHazDadJoke, ICanHazDadJoke>>, { GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}, never>) => Effect.Effect<{ GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}, never, ICanHazDadJoke> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen
allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await
but with more
explicit control over the execution of effects. You can yield*
values from
effects and return the final result at the end.
Example
import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () { const transactionAmount = yield* fetchTransactionAmount const discountRate = yield* fetchDiscountRate const discountedAmount = yield* applyDiscount( transactionAmount, discountRate ) const finalAmount = addServiceCharge(discountedAmount) return `Final amount to charge: ${finalAmount}`})
gen(function*() { // Access the `ICanHazDadJoke` service const const icanhazdadjoke: ICanHazDadJoke
icanhazdadjoke = yield* class ICanHazDadJoke
ICanHazDadJoke return { // Implement the handler for the `GetDadJoke` tool call request type GetDadJoke: ({ searchTerm }: { readonly searchTerm: string;}) => Effect.Effect<string, never, never>
GetDadJoke: ({ searchTerm: string
searchTerm }) => const icanhazdadjoke: ICanHazDadJoke
icanhazdadjoke.search: (searchTerm: string) => Effect.Effect<string, never, never>
search(searchTerm: string
searchTerm) } }))
In the code above:
- We access the
ICanHazDadJoke
service from our application - Register a handler for the
GetDadJoke
tool using.handle("GetDadJoke", ...)
- Use the
.search
method on ourICanHazDadJoke
service to search for a dad joke based on the tool call parameters
The result of calling .toLayer
on a Toolkit
is a Layer
that contains the handlers for all the tools in our toolkit.
Because of this, it is quite simple to test a Toolkit
by using .toLayer
to create a separate Layer
specifically for testing.
Once the tools are defined and implemented, you can pass them along to the model at request time. Behind the scenes, the model is given a structured description of each tool and can choose to call one or more of them when responding to input.
Example (Using a Toolkit
)
import { import LanguageModel
LanguageModel, import Tool
Tool, import Toolkit
Toolkit } from "@effect/ai"import { import Effect
Effect, import Schema
Schema } from "effect"
const const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke = import Tool
Tool.const make: <"GetDadJoke", { searchTerm: Schema.SchemaClass<string, string, never>;}, typeof Schema.String, typeof Schema.Never, []>(name: "GetDadJoke", options?: { readonly description?: string | undefined; readonly parameters?: { searchTerm: Schema.SchemaClass<string, string, never>; } | undefined; readonly success?: typeof Schema.String | undefined; readonly failure?: typeof Schema.Never | undefined; readonly dependencies?: [] | undefined;} | undefined) => Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
Creates a user-defined tool with the specified name and configuration.
This is the primary constructor for creating custom tools that AI models
can call. The tool definition includes parameter validation, success/failure
schemas, and optional service dependencies.
make("GetDadJoke", { description?: string | undefined
An optional description explaining what the tool does.
description: "Get a hilarious dad joke from the ICanHazDadJoke API", success?: typeof Schema.String | undefined
Schema for successful tool execution results.
success: import Schema
Schema.class Stringexport String
String, failure?: typeof Schema.Never | undefined
Schema for tool execution failures.
failure: import Schema
Schema.class Never
Never, parameters?: { searchTerm: Schema.SchemaClass<string, string, never>;} | undefined
Schema defining the parameters this tool accepts.
parameters: { searchTerm: Schema.SchemaClass<string, string, never>
searchTerm: import Schema
Schema.class Stringexport String
String.Annotable<SchemaClass<string, string, never>, string, string, never>.annotations(annotations: Schema.Annotations.GenericSchema<string>): Schema.SchemaClass<string, string, never>
Merges a set of new annotations with existing ones, potentially overwriting
any duplicates.
annotations({ Annotations.Doc<string>.description?: string
description: "The search term to use to find dad jokes" }) }})
const const DadJokeTools: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
DadJokeTools = import Toolkit
Toolkit.const make: <[Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>]>(tools_0: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>) => Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
Creates a new toolkit from the specified tools.
This is the primary constructor for creating toolkits. It accepts multiple tools
and organizes them into a toolkit that can be provided to AI language models.
Tools can be either Tool instances or TaggedRequest schemas.
make(const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke)
const const generateDadJoke: Effect.Effect<LanguageModel.GenerateTextResponse<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>, AiError, Tool.Handler<"GetDadJoke"> | LanguageModel.LanguageModel>
generateDadJoke = import LanguageModel
LanguageModel.const generateText: <{ prompt: string; toolkit: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>; }>;}, { readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>(options: { prompt: string; toolkit: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>; }>;} & LanguageModel.GenerateTextOptions<...>) => Effect.Effect<...>
Generate text using a language model.
generateText({ prompt: string & RawInput
The prompt input to use to generate text.
prompt: "Generate a dad joke about pirates", toolkit: (Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}> & Toolkit.WithHandler<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>) | (Toolkit.Toolkit<...> & Effect.Effect<...>)
A toolkit containing both the tools and the tool call handler to use to
augment text generation.
toolkit: const DadJokeTools: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
DadJokeTools})
To make the program executable, we must provide the implementation of our tool call handlers:
Example (Providing the Tool Call Handlers to a Program)
import { import LanguageModel
LanguageModel, import Tool
Tool, import Toolkit
Toolkit } from "@effect/ai"import { import OpenAiClient
OpenAiClient, import OpenAiLanguageModel
OpenAiLanguageModel } from "@effect/ai-openai"import { import HttpClient
HttpClient, import HttpClientRequest
HttpClientRequest, import HttpClientResponse
HttpClientResponse} from "@effect/platform"import { import NodeHttpClient
NodeHttpClient } from "@effect/platform-node"import { import Array
Array, import Config
Config, import Console
Console, import Effect
Effect, import Layer
Layer, import Schema
Schema } from "effect"
51 collapsed lines
class class DadJoke
DadJoke extends import Schema
Schema.const Class: <DadJoke>(identifier: string) => <Fields>(fieldsOr: Fields | HasFields<Fields>, annotations?: ClassAnnotations<DadJoke, { [K in keyof Schema.Struct<Fields extends Schema.Struct.Fields>.Type<Fields>]: Schema.Struct.Type<Fields>[K]; }> | undefined) => Schema.Class<DadJoke, Fields, Schema.Struct.Encoded<Fields>, Schema.Schema<in out A, in out I = A, out R = never>.Context<Fields[keyof Fields]>, Schema.Struct.Constructor<...>, {}, {}>
Class<class DadJoke
DadJoke>("DadJoke")({ id: typeof Schema.String
id: import Schema
Schema.class Stringexport String
String, joke: typeof Schema.String
joke: import Schema
Schema.class Stringexport String
String}) {}
class class SearchResponse
SearchResponse extends import Schema
Schema.const Class: <SearchResponse>(identifier: string) => <Fields>(fieldsOr: Fields | HasFields<Fields>, annotations?: ClassAnnotations<SearchResponse, { [K in keyof Schema.Struct<Fields extends Schema.Struct.Fields>.Type<Fields>]: Schema.Struct.Type<Fields>[K]; }> | undefined) => Schema.Class<SearchResponse, Fields, Schema.Struct.Encoded<Fields>, Schema.Schema<in out A, in out I = A, out R = never>.Context<Fields[keyof Fields]>, Schema.Struct.Constructor<...>, {}, {}>
Class<class SearchResponse
SearchResponse>("SearchResponse")({ results: Schema.Array$<typeof DadJoke>
results: import Schema
Schema.Array<typeof DadJoke>(value: typeof DadJoke): Schema.Array$<typeof DadJoke>export Array
Array(class DadJoke
DadJoke)}) {}
class class ICanHazDadJoke
ICanHazDadJoke extends import Effect
Effect.const Service: <ICanHazDadJoke>() => { <Key, Make>(key: Key, make: Make): Effect.Service.Class<ICanHazDadJoke, Key, Make>; <Key, Make>(key: Key, make: Make): Effect.Service.Class<ICanHazDadJoke, Key, Make>; <Key, Make>(key: Key, make: Make): Effect.Service.Class<ICanHazDadJoke, Key, Make>; <Key, Make>(key: Key, make: Make): Effect.Service.Class<ICanHazDadJoke, Key, Make>; <Key, Make>(key: Key, make: Make): Effect.Service.Class<...>;}
Simplifies the creation and management of services in Effect by defining both
a Tag
and a Layer
.
Details
This function allows you to streamline the creation of services by combining
the definition of a Context.Tag
and a Layer
in a single step. It supports
various ways of providing the service implementation:
- Using an
effect
to define the service dynamically.
- Using
sync
or succeed
to define the service statically.
- Using
scoped
to create services with lifecycle management.
It also allows you to specify dependencies for the service, which will be
provided automatically when the service is used. Accessors can be optionally
generated for the service, making it more convenient to use.
Example
import { Effect } from 'effect';
class Prefix extends Effect.Service<Prefix>()("Prefix", { sync: () => ({ prefix: "PRE" })}) {}
class Logger extends Effect.Service<Logger>()("Logger", { accessors: true, effect: Effect.gen(function* () { const { prefix } = yield* Prefix return { info: (message: string) => Effect.sync(() => { console.log(`[${prefix}][${message}]`) }) } }), dependencies: [Prefix.Default]}) {}
Service<class ICanHazDadJoke
ICanHazDadJoke>()("ICanHazDadJoke", { dependencies: readonly [Layer.Layer<HttpClient.HttpClient, never, never>]
dependencies: [import NodeHttpClient
NodeHttpClient.const layerUndici: Layer.Layer<HttpClient.HttpClient, never, never>
layerUndici], effect: Effect.Effect<{ readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}, never, HttpClient.HttpClient>
effect: import Effect
Effect.const gen: <YieldWrap<Tag<HttpClient.HttpClient, HttpClient.HttpClient>>, { readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Tag<HttpClient.HttpClient, HttpClient.HttpClient>>, { readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}, never>) => Effect.Effect<{ readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}, never, HttpClient.HttpClient> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen
allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await
but with more
explicit control over the execution of effects. You can yield*
values from
effects and return the final result at the end.
Example
import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () { const transactionAmount = yield* fetchTransactionAmount const discountRate = yield* fetchDiscountRate const discountedAmount = yield* applyDiscount( transactionAmount, discountRate ) const finalAmount = addServiceCharge(discountedAmount) return `Final amount to charge: ${finalAmount}`})
gen(function*() { const const httpClient: HttpClient.HttpClient
httpClient = yield* import HttpClient
HttpClient.const HttpClient: Tag<HttpClient.HttpClient, HttpClient.HttpClient>
HttpClient const const httpClientOk: HttpClient.HttpClient.With<HttpClientError, never>
httpClientOk = const httpClient: HttpClient.HttpClient
httpClient.Pipeable.pipe<HttpClient.HttpClient, HttpClient.HttpClient.With<HttpClientError, never>, HttpClient.HttpClient.With<HttpClientError, never>>(this: HttpClient.HttpClient, ab: (_: HttpClient.HttpClient) => HttpClient.HttpClient.With<HttpClientError, never>, bc: (_: HttpClient.HttpClient.With<HttpClientError, never>) => HttpClient.HttpClient.With<HttpClientError, never>): HttpClient.HttpClient.With<...> (+21 overloads)
pipe( import HttpClient
HttpClient.const filterStatusOk: <E, R>(self: HttpClient.HttpClient.With<E, R>) => HttpClient.HttpClient.With<E | ResponseError, R>
Filters responses that return a 2xx status code.
filterStatusOk, import HttpClient
HttpClient.const mapRequest: (f: (a: HttpClientRequest.HttpClientRequest) => HttpClientRequest.HttpClientRequest) => <E, R>(self: HttpClient.HttpClient.With<E, R>) => HttpClient.HttpClient.With<E, R> (+1 overload)
Appends a transformation of the request object before sending it.
mapRequest(import HttpClientRequest
HttpClientRequest.const prependUrl: (path: string) => (self: HttpClientRequest.HttpClientRequest) => HttpClientRequest.HttpClientRequest (+1 overload)
prependUrl("https://icanhazdadjoke.com")) )
const const search: (searchTerm: string) => Effect.Effect<string, never, never>
search = import Effect
Effect.const fn: (name: string, options?: SpanOptions) => Effect.fn.Gen & Effect.fn.NonGen (+20 overloads)
fn("ICanHazDadJoke.search")( function*(searchTerm: string
searchTerm: string) { return yield* const httpClientOk: HttpClient.HttpClient.With<HttpClientError, never>
httpClientOk.HttpClient.With<HttpClientError, never>.get: (url: string | URL, options?: HttpClientRequest.Options.NoBody) => Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError, never>
get("/search", { acceptJson?: boolean | undefined
acceptJson: true, urlParams?: Input | undefined
urlParams: { searchTerm: string
searchTerm } }).Pipeable.pipe<Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError, never>, Effect.Effect<SearchResponse, HttpClientError | ParseError, never>, Effect.Effect<DadJoke, HttpClientError | ParseError | NoSuchElementException, never>, Effect.Effect<string, HttpClientError | ParseError | NoSuchElementException, never>, Effect.Effect<...>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>, cd: (_: Effect.Effect<...>) => Effect.Effect<...>, de: (_: Effect.Effect<...>) => Effect.Effect<...>, ef: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe( import Effect
Effect.const flatMap: <HttpClientResponse.HttpClientResponse, SearchResponse, ResponseError | ParseError, never>(f: (a: HttpClientResponse.HttpClientResponse) => Effect.Effect<SearchResponse, ResponseError | ParseError, never>) => <E, R>(self: Effect.Effect<HttpClientResponse.HttpClientResponse, E, R>) => Effect.Effect<SearchResponse, ResponseError | ParseError | E, R> (+1 overload)
Chains effects to produce new Effect
instances, useful for combining
operations that depend on previous results.
Syntax
const flatMappedEffect = pipe(myEffect, Effect.flatMap(transformation))// orconst flatMappedEffect = Effect.flatMap(myEffect, transformation)// orconst flatMappedEffect = myEffect.pipe(Effect.flatMap(transformation))
Details
flatMap
lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap
used with arrays but works
specifically with Effect
instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap
always returns a new effect instead of
changing the original one.
When to Use
Use flatMap
when you need to chain multiple effects, ensuring that each
step produces a new Effect
while flattening any nested effects that may
occur.
Example
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amountconst applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from databaseconst fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`const finalAmount = pipe( fetchTransactionAmount, Effect.flatMap((amount) => applyDiscount(amount, 5)))
Effect.runPromise(finalAmount).then(console.log)// Output: 95
flatMap(import HttpClientResponse
HttpClientResponse.schemaBodyJson<SearchResponse, { readonly results: readonly { readonly id: string; readonly joke: string; }[];}, never>(schema: Schema.Schema<SearchResponse, { readonly results: readonly { readonly id: string; readonly joke: string; }[];}, never>, options?: ParseOptions | undefined): <E>(self: HttpIncomingMessage<E>) => Effect.Effect<SearchResponse, ParseError | E, never>export schemaBodyJson
schemaBodyJson(class SearchResponse
SearchResponse)), import Effect
Effect.const flatMap: <SearchResponse, DadJoke, NoSuchElementException, never>(f: (a: SearchResponse) => Effect.Effect<DadJoke, NoSuchElementException, never>) => <E, R>(self: Effect.Effect<SearchResponse, E, R>) => Effect.Effect<DadJoke, NoSuchElementException | E, R> (+1 overload)
Chains effects to produce new Effect
instances, useful for combining
operations that depend on previous results.
Syntax
const flatMappedEffect = pipe(myEffect, Effect.flatMap(transformation))// orconst flatMappedEffect = Effect.flatMap(myEffect, transformation)// orconst flatMappedEffect = myEffect.pipe(Effect.flatMap(transformation))
Details
flatMap
lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap
used with arrays but works
specifically with Effect
instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap
always returns a new effect instead of
changing the original one.
When to Use
Use flatMap
when you need to chain multiple effects, ensuring that each
step produces a new Effect
while flattening any nested effects that may
occur.
Example
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amountconst applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from databaseconst fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`const finalAmount = pipe( fetchTransactionAmount, Effect.flatMap((amount) => applyDiscount(amount, 5)))
Effect.runPromise(finalAmount).then(console.log)// Output: 95
flatMap(({ results: readonly DadJoke[]
results }) => import Array
Array.const head: <DadJoke>(self: readonly DadJoke[]) => Option<DadJoke>
Get the first element of a ReadonlyArray
, or None
if the ReadonlyArray
is empty.
head(results: readonly DadJoke[]
results)), import Effect
Effect.const map: <DadJoke, string>(f: (a: DadJoke) => string) => <E, R>(self: Effect.Effect<DadJoke, E, R>) => Effect.Effect<string, E, R> (+1 overload)
Transforms the value inside an effect by applying a function to it.
Syntax
const mappedEffect = pipe(myEffect, Effect.map(transformation))// orconst mappedEffect = Effect.map(myEffect, transformation)// orconst mappedEffect = myEffect.pipe(Effect.map(transformation))
Details
map
takes a function and applies it to the value contained within an
effect, creating a new effect with the transformed value.
It's important to note that effects are immutable, meaning that the original
effect is not modified. Instead, a new effect is returned with the updated
value.
Example (Adding a Service Charge)
import { pipe, Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const finalAmount = pipe( fetchTransactionAmount, Effect.map(addServiceCharge))
Effect.runPromise(finalAmount).then(console.log)// Output: 101
map((joke: DadJoke
joke) => joke: DadJoke
joke.joke: string
joke), import Effect
Effect.const scoped: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Scope>>
Scopes all resources used in an effect to the lifetime of the effect.
Details
This function ensures that all resources used within an effect are tied to
its lifetime. Finalizers for these resources are executed automatically when
the effect completes, whether through success, failure, or interruption. This
guarantees proper resource cleanup without requiring explicit management.
scoped, import Effect
Effect.const orDie: <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, never, R>
Converts an effect's failure into a fiber termination, removing the error
from the effect's type.
Details
The orDie
function is used when you encounter errors that you do not want
to handle or recover from. It removes the error type from the effect and
ensures that any failure will terminate the fiber. This is useful for
propagating failures as defects, signaling that they should not be handled
within the effect.
*When to Use
Use orDie
when failures should be treated as unrecoverable defects and no
error handling is required.
Example (Propagating an Error as a Defect)
import { Effect } from "effect"
const divide = (a: number, b: number) => b === 0 ? Effect.fail(new Error("Cannot divide by zero")) : Effect.succeed(a / b)
// ┌─── Effect<number, never, never>// ▼const program = Effect.orDie(divide(1, 0))
Effect.runPromise(program).catch(console.error)// Output:// (FiberFailure) Error: Cannot divide by zero// ...stack trace...
orDie ) } )
return { search: (searchTerm: string) => Effect.Effect<string, never, never>
search } as type const = { readonly search: (searchTerm: string) => Effect.Effect<string, never, never>;}
const })}) {}
const const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke = import Tool
Tool.const make: <"GetDadJoke", { searchTerm: Schema.SchemaClass<string, string, never>;}, typeof Schema.String, typeof Schema.Never, []>(name: "GetDadJoke", options?: { readonly description?: string | undefined; readonly parameters?: { searchTerm: Schema.SchemaClass<string, string, never>; } | undefined; readonly success?: typeof Schema.String | undefined; readonly failure?: typeof Schema.Never | undefined; readonly dependencies?: [] | undefined;} | undefined) => Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
Creates a user-defined tool with the specified name and configuration.
This is the primary constructor for creating custom tools that AI models
can call. The tool definition includes parameter validation, success/failure
schemas, and optional service dependencies.
make("GetDadJoke", { description?: string | undefined
An optional description explaining what the tool does.
description: "Get a hilarious dad joke from the ICanHazDadJoke API", success?: typeof Schema.String | undefined
Schema for successful tool execution results.
success: import Schema
Schema.class Stringexport String
String, failure?: typeof Schema.Never | undefined
Schema for tool execution failures.
failure: import Schema
Schema.class Never
Never, parameters?: { searchTerm: Schema.SchemaClass<string, string, never>;} | undefined
Schema defining the parameters this tool accepts.
parameters: { searchTerm: Schema.SchemaClass<string, string, never>
searchTerm: import Schema
Schema.class Stringexport String
String.Annotable<SchemaClass<string, string, never>, string, string, never>.annotations(annotations: Schema.Annotations.GenericSchema<string>): Schema.SchemaClass<string, string, never>
Merges a set of new annotations with existing ones, potentially overwriting
any duplicates.
annotations({ Annotations.Doc<string>.description?: string
description: "The search term to use to find dad jokes" }) }})
const const DadJokeTools: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
DadJokeTools = import Toolkit
Toolkit.const make: <[Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>]>(tools_0: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>) => Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
Creates a new toolkit from the specified tools.
This is the primary constructor for creating toolkits. It accepts multiple tools
and organizes them into a toolkit that can be provided to AI language models.
Tools can be either Tool instances or TaggedRequest schemas.
make(const GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never;}, never>
GetDadJoke)
const const DadJokeToolHandlers: Layer.Layer<Tool.Handler<"GetDadJoke">, never, never>
DadJokeToolHandlers = const DadJokeTools: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
DadJokeTools.Toolkit<{ readonly GetDadJoke: Tool<"GetDadJoke", { readonly parameters: Struct<{ searchTerm: SchemaClass<string, string, never>; }>; readonly success: typeof String$; readonly failure: typeof Never; }, never>; }>.toLayer<{ GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}, never, ICanHazDadJoke>(build: { GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;} | Effect.Effect<{ GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}, never, ICanHazDadJoke>): Layer.Layer<Tool.Handler<"GetDadJoke">, never, ICanHazDadJoke>
Converts a toolkit into a Layer containing handlers for each tool in the
toolkit.
toLayer( import Effect
Effect.const gen: <YieldWrap<Tag<ICanHazDadJoke, ICanHazDadJoke>>, { GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Tag<ICanHazDadJoke, ICanHazDadJoke>>, { GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}, never>) => Effect.Effect<{ GetDadJoke: ({ searchTerm }: { readonly searchTerm: string; }) => Effect.Effect<string, never, never>;}, never, ICanHazDadJoke> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen
allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await
but with more
explicit control over the execution of effects. You can yield*
values from
effects and return the final result at the end.
Example
import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () { const transactionAmount = yield* fetchTransactionAmount const discountRate = yield* fetchDiscountRate const discountedAmount = yield* applyDiscount( transactionAmount, discountRate ) const finalAmount = addServiceCharge(discountedAmount) return `Final amount to charge: ${finalAmount}`})
gen(function*() { const const icanhazdadjoke: ICanHazDadJoke
icanhazdadjoke = yield* class ICanHazDadJoke
ICanHazDadJoke return { type GetDadJoke: ({ searchTerm }: { readonly searchTerm: string;}) => Effect.Effect<string, never, never>
GetDadJoke: ({ searchTerm: string
searchTerm }) => const icanhazdadjoke: ICanHazDadJoke
icanhazdadjoke.search: (searchTerm: string) => Effect.Effect<string, never, never>
search(searchTerm: string
searchTerm) } })).Pipeable.pipe<Layer.Layer<Tool.Handler<"GetDadJoke">, never, ICanHazDadJoke>, Layer.Layer<Tool.Handler<"GetDadJoke">, never, never>>(this: Layer.Layer<Tool.Handler<"GetDadJoke">, never, ICanHazDadJoke>, ab: (_: Layer.Layer<Tool.Handler<"GetDadJoke">, never, ICanHazDadJoke>) => Layer.Layer<Tool.Handler<"GetDadJoke">, never, never>): Layer.Layer<Tool.Handler<"GetDadJoke">, never, never> (+21 overloads)
pipe(import Layer
Layer.const provide: <never, never, ICanHazDadJoke>(that: Layer.Layer<ICanHazDadJoke, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, E2, Exclude<RIn2, ICanHazDadJoke>> (+3 overloads)
Feeds the output services of this builder into the input of the specified
builder, resulting in a new builder with the inputs of this builder as
well as any leftover inputs, and the outputs of the specified builder.
provide(class ICanHazDadJoke
ICanHazDadJoke.type Default: Layer.Layer<ICanHazDadJoke, never, never>
Default))
const const program: Effect.Effect<void, AiError, Tool.Handler<"GetDadJoke"> | OpenAiClient.OpenAiClient>
program = import LanguageModel
LanguageModel.const generateText: <{ prompt: string; toolkit: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>; }>;}, { readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>(options: { prompt: string; toolkit: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>; }>;} & LanguageModel.GenerateTextOptions<...>) => Effect.Effect<...>
Generate text using a language model.
generateText({ prompt: string & RawInput
The prompt input to use to generate text.
prompt: "Generate a dad joke about pirates", toolkit: (Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}> & Toolkit.WithHandler<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>) | (Toolkit.Toolkit<...> & Effect.Effect<...>)
A toolkit containing both the tools and the tool call handler to use to
augment text generation.
toolkit: const DadJokeTools: Toolkit.Toolkit<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
DadJokeTools}).Pipeable.pipe<Effect.Effect<LanguageModel.GenerateTextResponse<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>, AiError, Tool.Handler<"GetDadJoke"> | LanguageModel.LanguageModel>, Effect.Effect<void, AiError, Tool.Handler<"GetDadJoke"> | LanguageModel.LanguageModel>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe( import Effect
Effect.const flatMap: <LanguageModel.GenerateTextResponse<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>, void, never, never>(f: (a: LanguageModel.GenerateTextResponse<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>) => Effect.Effect<...>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)
Chains effects to produce new Effect
instances, useful for combining
operations that depend on previous results.
Syntax
const flatMappedEffect = pipe(myEffect, Effect.flatMap(transformation))// orconst flatMappedEffect = Effect.flatMap(myEffect, transformation)// orconst flatMappedEffect = myEffect.pipe(Effect.flatMap(transformation))
Details
flatMap
lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap
used with arrays but works
specifically with Effect
instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap
always returns a new effect instead of
changing the original one.
When to Use
Use flatMap
when you need to chain multiple effects, ensuring that each
step produces a new Effect
while flattening any nested effects that may
occur.
Example
import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amountconst applyDiscount = ( total: number, discountRate: number): Effect.Effect<number, Error> => discountRate === 0 ? Effect.fail(new Error("Discount rate cannot be zero")) : Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from databaseconst fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`const finalAmount = pipe( fetchTransactionAmount, Effect.flatMap((amount) => applyDiscount(amount, 5)))
Effect.runPromise(finalAmount).then(console.log)// Output: 95
flatMap((response: LanguageModel.GenerateTextResponse<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
response) => import Console
Console.const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>
log(response: LanguageModel.GenerateTextResponse<{ readonly GetDadJoke: Tool.Tool<"GetDadJoke", { readonly parameters: Schema.Struct<{ searchTerm: Schema.SchemaClass<string, string, never>; }>; readonly success: typeof Schema.String; readonly failure: typeof Schema.Never; }, never>;}>
response.GenerateTextResponse<{ readonly GetDadJoke: Tool<"GetDadJoke", { readonly parameters: Struct<{ searchTerm: SchemaClass<string, string, never>; }>; readonly success: typeof String$; readonly failure: typeof Never; }, never>; }>.text: string
Extracts and concatenates all text parts from the response.
text)), import Effect
Effect.const provide: <LanguageModel.LanguageModel | ProviderName, never, OpenAiClient.OpenAiClient>(layer: Layer.Layer<LanguageModel.LanguageModel | ProviderName, never, OpenAiClient.OpenAiClient>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, OpenAiClient.OpenAiClient | Exclude<R, LanguageModel.LanguageModel | ProviderName>> (+9 overloads)
Provides necessary dependencies to an effect, removing its environmental
requirements.
Details
This function allows you to supply the required environment for an effect.
The environment can be provided in the form of one or more Layer
s, a
Context
, a Runtime
, or a ManagedRuntime
. Once the environment is
provided, the effect can run without requiring external dependencies.
You can compose layers to create a modular and reusable way of setting up the
environment for effects. For example, layers can be used to configure
databases, logging services, or any other required dependencies.
Example
import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")< Database, { readonly query: (sql: string) => Effect.Effect<Array<unknown>> }>() {}
const DatabaseLive = Layer.succeed( Database, { // Simulate a database query query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([])) })
// ┌─── Effect<unknown[], never, Database>// ▼const program = Effect.gen(function*() { const database = yield* Database const result = yield* database.query("SELECT * FROM users") return result})
// ┌─── Effect<unknown[], never, never>// ▼const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)// Output:// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"// []
provide(import OpenAiLanguageModel
OpenAiLanguageModel.const model: (model: (string & {}) | OpenAiLanguageModel.Model, config?: Omit<OpenAiLanguageModel.Config.Service, "model">) => Model<"openai", LanguageModel.LanguageModel, OpenAiClient.OpenAiClient>
model("gpt-4o")))
const const OpenAi: Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, never>
OpenAi = import OpenAiClient
OpenAiClient.const layerConfig: (options: { readonly apiKey?: Config.Config<Redacted | undefined> | undefined; readonly apiUrl?: Config.Config<string | undefined> | undefined; readonly organizationId?: Config.Config<Redacted | undefined> | undefined; readonly projectId?: Config.Config<Redacted | undefined> | undefined; readonly transformClient?: (client: HttpClient.HttpClient) => HttpClient.HttpClient;}) => Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, HttpClient.HttpClient>
layerConfig({ apiKey?: Config.Config<Redacted<string> | undefined> | undefined
apiKey: import Config
Config.const redacted: (name?: string) => Config.Config<Redacted> (+1 overload)
Constructs a config for a redacted value.
redacted("OPENAI_API_KEY")}).Pipeable.pipe<Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, HttpClient.HttpClient>, Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, never>>(this: Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, HttpClient.HttpClient>, ab: (_: Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, HttpClient.HttpClient>) => Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, never>): Layer.Layer<...> (+21 overloads)
pipe(import Layer
Layer.const provide: <never, never, HttpClient.HttpClient>(that: Layer.Layer<HttpClient.HttpClient, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, E2, Exclude<RIn2, HttpClient.HttpClient>> (+3 overloads)
Feeds the output services of this builder into the input of the specified
builder, resulting in a new builder with the inputs of this builder as
well as any leftover inputs, and the outputs of the specified builder.
provide(import NodeHttpClient
NodeHttpClient.const layerUndici: Layer.Layer<HttpClient.HttpClient, never, never>
layerUndici))
const program: Effect.Effect<void, AiError, Tool.Handler<"GetDadJoke"> | OpenAiClient.OpenAiClient>
program.Pipeable.pipe<Effect.Effect<void, AiError, Tool.Handler<"GetDadJoke"> | OpenAiClient.OpenAiClient>, Effect.Effect<void, AiError | ConfigError, never>, Promise<void>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, AiError, Tool.Handler<"GetDadJoke"> | OpenAiClient.OpenAiClient>) => Effect.Effect<void, AiError | ConfigError, never>, bc: (_: Effect.Effect<void, AiError | ConfigError, never>) => Promise<...>): Promise<...> (+21 overloads)
pipe( import Effect
Effect.const provide: <readonly [Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, never>, Layer.Layer<Tool.Handler<"GetDadJoke">, never, never>]>(layers: readonly [Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, never>, Layer.Layer<Tool.Handler<"GetDadJoke">, never, never>]) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, ConfigError | E, Exclude<R, Tool.Handler<"GetDadJoke"> | OpenAiClient.OpenAiClient>> (+9 overloads)
Provides necessary dependencies to an effect, removing its environmental
requirements.
Details
This function allows you to supply the required environment for an effect.
The environment can be provided in the form of one or more Layer
s, a
Context
, a Runtime
, or a ManagedRuntime
. Once the environment is
provided, the effect can run without requiring external dependencies.
You can compose layers to create a modular and reusable way of setting up the
environment for effects. For example, layers can be used to configure
databases, logging services, or any other required dependencies.
Example
import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")< Database, { readonly query: (sql: string) => Effect.Effect<Array<unknown>> }>() {}
const DatabaseLive = Layer.succeed( Database, { // Simulate a database query query: (sql: string) => Effect.log(`Executing query: ${sql}`).pipe(Effect.as([])) })
// ┌─── Effect<unknown[], never, Database>// ▼const program = Effect.gen(function*() { const database = yield* Database const result = yield* database.query("SELECT * FROM users") return result})
// ┌─── Effect<unknown[], never, never>// ▼const runnable = Effect.provide(program, DatabaseLive)
Effect.runPromise(runnable).then(console.log)// Output:// timestamp=... level=INFO fiber=#0 message="Executing query: SELECT * FROM users"// []
provide([const OpenAi: Layer.Layer<OpenAiClient.OpenAiClient, ConfigError, never>
OpenAi, const DadJokeToolHandlers: Layer.Layer<Tool.Handler<"GetDadJoke">, never, never>
DadJokeToolHandlers]), import Effect
Effect.const runPromise: <A, E>(effect: Effect.Effect<A, E, never>, options?: { readonly signal?: AbortSignal | undefined;} | undefined) => Promise<A>
Executes an effect and returns the result as a Promise
.
Details
This function runs an effect and converts its result into a Promise
. If the
effect succeeds, the Promise
will resolve with the successful result. If
the effect fails, the Promise
will reject with an error, which includes the
failure details of the effect.
The optional options
parameter allows you to pass an AbortSignal
for
cancellation, enabling more fine-grained control over asynchronous tasks.
When to Use
Use this function when you need to execute an effect and work with its result
in a promise-based system, such as when integrating with third-party
libraries that expect Promise
results.
Example (Running a Successful Effect as a Promise)
import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)// Output: 1
Example (Handling a Failing Effect as a Rejected Promise)
import { Effect } from "effect"
Effect.runPromise(Effect.fail("my error")).catch(console.error)// Output:// (FiberFailure) Error: my error
runPromise)
Type Safe
Every tool is fully described using Effect’s Schema
, including inputs, outputs, and descriptions.
Effect Native
Tool call behavior is defined using Effect, so they can leverage all the power of Effect. This is especially useful when you need to access other services to support the implementation of your tool call handlers.
Injectable
Because implementing the handlers for an Toolkit
results in a Layer
, providing alternate implementation of tool call handlers in different environments is as simple as providing a different Layer
to your program.
Separation of Concerns
The definition of a tool call request is cleanly separated from both the implementation of the tool behavior, as well as the business logic that calls the model.