Skip to content

Getting Started

In this getting started guide, we will demonstrate how to generate a simple text completion using an LLM provider (OpenAi) using the Effect AI integration packages.

We’ll walk through:

  • Writing provider-agnostic logic to interact with an LLM
  • Declaring the specific LLM model to use for the interaction
  • Using a provider integration to make the program executable

First, we will need to install the base @effect/ai package to gain access to the core AI abstractions. In addition, we will need to install at least one provider integration package (in this case @effect/ai-openai):

Terminal window
# Install the base package for the core abstractions (always required)
npm install @effect/ai
# Install one (or more) provider integrations
npm install @effect/ai-openai
# Also add the core Effect package (if not already installed)
npm install effect

First let’s define a simple interaction with an LLM:

Example (Using the Completions Service to Generate a Dad Joke)

import {
import Completions
Completions
} from "@effect/ai"
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
// Using `Completions` will add it to your program's requirements
//
// ┌─── Effect<AiResponse, AiError, Completions>
// ▼
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
// Extract the `Completions` service from the Effect environment
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
// Use the `Completions` service to generate some text
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
// Log the generated text to the console
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
// Return the response
return
const response: AiResponse
response
})

Next, we need to select which provider we want to use to satisfy the Completions requirement of our generateDadJoke program:

Example (Providing a Concrete Model to Satisfy the Completions Requirement)

import {
import OpenAiCompletions
OpenAiCompletions
} from "@effect/ai-openai"
import {
import Completions
Completions
} from "@effect/ai"
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
6 collapsed lines
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
return
const response: AiResponse
response
})
// Create an `AiModel` which provides a concrete implementation of
// `Completions` and requires an `OpenAiClient`
//
// ┌─── AiModel<Completions, OpenAiClient>
// ▼
const
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
=
import OpenAiCompletions
OpenAiCompletions
.
const model: (model: (string & {}) | OpenAiCompletions.Model, config?: Omit<OpenAiCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, OpenAiClient>

@since1.0.0

model
("gpt-4o")
// Building an `AiModel` adds its requirements to the program
//
// ┌─── Effect<void, AiError, OpenAiClient>
// ▼
const
const main: Effect.Effect<void, AiError, OpenAiClient | AiModels>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiResponse, AiError, never>> | YieldWrap<Effect.Effect<AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions.Completions | Tokenizer>, never, OpenAiClient | AiModels>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
// Build the `AiModel` into a `Provider`
const
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
= yield*
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
// Provide the implementation of `Completions` to `generateDadJoke`
const
const response: AiResponse
response
= yield*
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
})

Before moving on, it is important that we understand the purpose of the AiModel data type.

The AiModel data type represents a provider-specific implementation of one or more services, such as Completions or Embeddings. It is the primary way that you can plug a real LLM (like OpenAI’s GPT-4o) into your program.

export interface AiModel<Provides, Requires> {}

An AiModel has two generic type parameters:

  • Provides - the services this model will provide when built
  • Requires - the services this model will require to be built

This allows Effect to track which services should be added to the requirements of the program that builds the AiModel, as well as which services the built AiModel can provide.

To create an AiModel, you can use the model-specific factory from one of Effect’s provider integration packages.

Example (Defining an AiModel to Interact with OpenAi)

import { OpenAiCompletions } from "@effect/ai-openai"
// ┌─── AiModel<Completions, OpenAiClient>
// ▼
const Gpt4o = OpenAiCompletions.model("gpt-4o")

This creates an AiModel that:

  • Provides an OpenAi-specific implementation of the Completions service using "gpt-4o"
  • Requires an OpenAiClient to be built

When you build an AiModel<Provides, Requires>, you get back a Provider<Provides>:

Effect.gen(function*() {
// ┌─── Provider<Completions>
// ▼
const gpt4o = yield* Gpt4o
})

A Provider has a single .provide(...) method:

gpt4o.provide(myAiProgram)

The .provide(...) method injects the services built by the AiModel into an Effect program, thereby removing those services from that program’s requirements.

There are several benefits to this approach:

Reusability

You can call .provide(...) with the same built model as many times as you like.

For example, we can re-use the same built model to provide Completions to multiple calls to generateDadJoke:

import {
import OpenAiCompletions
OpenAiCompletions
} from "@effect/ai-openai"
import {
import Completions
Completions
} from "@effect/ai"
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
6 collapsed lines
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
return
const response: AiResponse
response
})
const
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
=
import OpenAiCompletions
OpenAiCompletions
.
const model: (model: (string & {}) | OpenAiCompletions.Model, config?: Omit<OpenAiCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, OpenAiClient>

@since1.0.0

model
("gpt-4o")
const
const main: Effect.Effect<void, AiError, OpenAiClient | AiModels>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiResponse, AiError, never>> | YieldWrap<Effect.Effect<AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions.Completions | Tokenizer>, never, OpenAiClient | AiModels>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
= yield*
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
const
const res1: AiResponse
res1
= yield*
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
const
const res2: AiResponse
res2
= yield*
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
const
const res3: AiResponse
res3
= yield*
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
})

Flexibility

If we know that one model or provider performs better at a given task than another, we can freely mix and match models and providers together.

For example, if we know Anthropic’s Claude generates some really great dad jokes, we can mix it into our existing program with just a few lines of code:

Example (Mixing Multiple Providers and Models)

import {
import AnthropicCompletions
AnthropicCompletions
} from "@effect/ai-anthropic"
import {
import OpenAiCompletions
OpenAiCompletions
} from "@effect/ai-openai"
import {
import Completions
Completions
} from "@effect/ai"
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
6 collapsed lines
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
return
const response: AiResponse
response
})
const
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
=
import OpenAiCompletions
OpenAiCompletions
.
const model: (model: (string & {}) | OpenAiCompletions.Model, config?: Omit<OpenAiCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, OpenAiClient>

@since1.0.0

model
("gpt-4o")
const
const Claude37: AiModel<Completions.Completions | Tokenizer, AnthropicClient>
Claude37
=
import AnthropicCompletions
AnthropicCompletions
.
const model: (model: (string & {}) | AnthropicCompletions.Model, config?: Omit<AnthropicCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, AnthropicClient>

@since1.0.0

model
("claude-3-7-sonnet-latest")
// ┌─── Effect<void, AiError, AnthropicClient | OpenAiClient>
// ▼
const
const main: Effect.Effect<void, AiError, OpenAiClient | AnthropicClient | AiModels>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiResponse, AiError, never>> | YieldWrap<Effect.Effect<AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions.Completions | Tokenizer>, never, OpenAiClient | AiModels>> | YieldWrap<...>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
= yield*
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
const
const claude: AiPlan.Provider<Completions.Completions | Tokenizer>
claude
= yield*
const Claude37: AiModel<Completions.Completions | Tokenizer, AnthropicClient>
Claude37
const
const res1: AiResponse
res1
= yield*
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
const
const res2: AiResponse
res2
= yield*
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
const
const res3: AiResponse
res3
= yield*
const claude: AiPlan.Provider<Completions.Completions | Tokenizer>
claude
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
})

Because Effect performs type-level dependency tracking, we can see that an AnthropicClient must now also be provided to make our program runnable.

Abstractability

Because there is separation between building an AiModel and providing its services, we can very nicely support the service constructor pattern.

For example, in the code below the main program is only dependent upon the DadJokes service. All AI requirements are abstracted away into Layer composition.

Example (Abstracting LLM Interactions into a Service)

import {
import AnthropicCompletions
AnthropicCompletions
} from "@effect/ai-anthropic"
import {
import OpenAiCompletions
OpenAiCompletions
} from "@effect/ai-openai"
import {
import Completions
Completions
} from "@effect/ai"
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
const
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
=
import OpenAiCompletions
OpenAiCompletions
.
const model: (model: (string & {}) | OpenAiCompletions.Model, config?: Omit<OpenAiCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, OpenAiClient>

@since1.0.0

model
("gpt-4o")
const
const Claude37: AiModel<Completions.Completions | Tokenizer, AnthropicClient>
Claude37
=
import AnthropicCompletions
AnthropicCompletions
.
const model: (model: (string & {}) | AnthropicCompletions.Model, config?: Omit<AnthropicCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, AnthropicClient>

@since1.0.0

model
("claude-3-7-sonnet-latest")
class
class DadJokes
DadJokes
extends
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const Service: <DadJokes>() => {
<Key, Make>(key: Key, make: Make): Effect.Service.Class<DadJokes, Key, Make>;
<Key, Make>(key: Key, make: Make): Effect.Service.Class<...>;
<Key, Make>(key: Key, make: Make): Effect.Service.Class<...>;
<Key, Make>(key: Key, make: Make): Effect.Service.Class<...>;
<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]
}) {}

@since3.9.0

Service
<
class DadJokes
DadJokes
>()("app/DadJokes", {
effect: Effect.Effect<{
generateDadJoke: Effect.Effect<AiResponse, AiError, never>;
generateReallyGroanInducingDadJoke: Effect.Effect<AiResponse, AiError, never>;
}, never, OpenAiClient | ... 1 more ... | AiModels>
effect
:
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions.Completions | Tokenizer>, never, OpenAiClient | AiModels>> | YieldWrap<...>, {
...;
}>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
= yield*
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
const
const claude: AiPlan.Provider<Completions.Completions | Tokenizer>
claude
= yield*
const Claude37: AiModel<Completions.Completions | Tokenizer, AnthropicClient>
Claude37
6 collapsed lines
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
return
const response: AiResponse
response
})
return {
generateDadJoke: Effect.Effect<AiResponse, AiError, never>
generateDadJoke
:
const gpt: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
),
generateReallyGroanInducingDadJoke: Effect.Effect<AiResponse, AiError, never>
generateReallyGroanInducingDadJoke
:
const claude: AiPlan.Provider<Completions.Completions | Tokenizer>
claude
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
}
})
}) {}
// Programs which utilize the `DadJokes` service have no knowledge of
// any AI requirements
//
// ┌─── Effect<void, AiError, DadJokes>
// ▼
const
const main: Effect.Effect<void, AiError, DadJokes>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiResponse, AiError, never>> | YieldWrap<Tag<DadJokes, DadJokes>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const dadJokes: DadJokes
dadJokes
= yield*
class DadJokes
DadJokes
const
const res1: AiResponse
res1
= yield*
const dadJokes: DadJokes
dadJokes
.
generateDadJoke: Effect.Effect<AiResponse, AiError, never>
generateDadJoke
const
const res2: AiResponse
res2
= yield*
const dadJokes: DadJokes
dadJokes
.
generateReallyGroanInducingDadJoke: Effect.Effect<AiResponse, AiError, never>
generateReallyGroanInducingDadJoke
})
// The AI requirements are abstracted away into `Layer` composition
//
// ┌─── Layer<DadJokes, never, AnthropicClient | OpenAiClient>
// ▼
class DadJokes
DadJokes
.
type Default: Layer<DadJokes, never, OpenAiClient | AnthropicClient | AiModels>
Default

To make our code executable, we must finish satisfying our program’s requirements.

Let’s take another look at our program from earlier:

import {
import OpenAiCompletions
OpenAiCompletions
} from "@effect/ai-openai"
import {
import Completions
Completions
} from "@effect/ai"
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
8 collapsed lines
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
return
const response: AiResponse
response
})
const
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
=
import OpenAiCompletions
OpenAiCompletions
.
const model: (model: (string & {}) | OpenAiCompletions.Model, config?: Omit<OpenAiCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, OpenAiClient>

@since1.0.0

model
("gpt-4o")
// ┌─── Effect<void, AiError, OpenAiClient>
// ▼
const
const main: Effect.Effect<void, AiError, OpenAiClient | AiModels>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiResponse, AiError, never>> | YieldWrap<Effect.Effect<AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions.Completions | Tokenizer>, never, OpenAiClient | AiModels>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
= yield*
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient>
Gpt4o
const
const response: AiResponse
response
= yield*
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
})

We can see that our main program still requires us to provide an OpenAiClient.

Each of our provider integration packages exports a client module that can be used to construct a client for that provider.

Example (Creating a Client Layer for an LLM Provider)

import {
import OpenAiClient
OpenAiClient
,
import OpenAiCompletions
OpenAiCompletions
} from "@effect/ai-openai"
import {
import Completions
Completions
} from "@effect/ai"
import {
import Config
Config
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
13 collapsed lines
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
return
const response: AiResponse
response
})
const
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>
Gpt4o
=
import OpenAiCompletions
OpenAiCompletions
.
const model: (model: (string & {}) | OpenAiCompletions.Model, config?: Omit<OpenAiCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>

@since1.0.0

model
("gpt-4o")
const
const main: Effect.Effect<void, AiError, OpenAiClient.OpenAiClient | AiModels>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiResponse, AiError, never>> | YieldWrap<Effect.Effect<AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions.Completions | Tokenizer>, never, OpenAiClient.OpenAiClient | AiModels>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
= yield*
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>
Gpt4o
const
const response: AiResponse
response
= yield*
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
})
// Create a `Layer` which produces an `OpenAiClient` and requires
// an `HttpClient`
//
// ┌─── Layer<OpenAiClient, never, HttpClient>
// ▼
const
const OpenAi: Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, HttpClient>
OpenAi
=
import OpenAiClient
OpenAiClient
.
const layerConfig: (options: Config.Config.Wrap<{
readonly apiKey?: Redacted | undefined;
readonly apiUrl?: string | undefined;
readonly organizationId?: Redacted | undefined;
readonly projectId?: Redacted | undefined;
readonly transformClient?: (client: HttpClient) => HttpClient;
}>) => Layer<AiModels | OpenAiClient.OpenAiClient, ConfigError, HttpClient>

@since1.0.0

layerConfig
({
apiKey?: Config.Config<Redacted<string> | undefined>
apiKey
:
import Config
Config
.
const redacted: (name?: string) => Config.Config<Redacted> (+1 overload)

Constructs a config for a redacted value.

@since2.0.0

redacted
("OPENAI_API_KEY")
})

In the code above, we use the layerConfig constructor from the OpenAiClient module to create a Layer which will produce an OpenAiClient. The layerConfig constructor allows us to read in configuration variables using Effect’s configuration system.

The provider clients also have a dependency on an HttpClient implementation to avoid any platform dependencies. This way, you can provide whichever HttpClient implementation is most appropriate for the platform your code is running upon.

For example, if we know we are going to run this code in NodeJS, we can utilize the NodeHttpClient module from @effect/platform-node to provide an HttpClient implementation:

import {
import OpenAiClient
OpenAiClient
,
import OpenAiCompletions
OpenAiCompletions
} from "@effect/ai-openai"
import {
import Completions
Completions
} from "@effect/ai"
import {
import NodeHttpClient
NodeHttpClient
} from "@effect/platform-node"
import {
import Config
Config
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Layer
Layer
} from "effect"
13 collapsed lines
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
return
const response: AiResponse
response
})
const
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>
Gpt4o
=
import OpenAiCompletions
OpenAiCompletions
.
const model: (model: (string & {}) | OpenAiCompletions.Model, config?: Omit<OpenAiCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>

@since1.0.0

model
("gpt-4o")
const
const main: Effect.Effect<void, AiError, OpenAiClient.OpenAiClient | AiModels>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiResponse, AiError, never>> | YieldWrap<Effect.Effect<AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions.Completions | Tokenizer>, never, OpenAiClient.OpenAiClient | AiModels>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
= yield*
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>
Gpt4o
const
const response: AiResponse
response
= yield*
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
})
// Create a `Layer` which produces an `OpenAiClient` and requires
// an `HttpClient`
//
// ┌─── Layer<OpenAiClient, never, HttpClient>
// ▼
const
const OpenAi: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, HttpClient>
OpenAi
=
import OpenAiClient
OpenAiClient
.
const layerConfig: (options: Config.Config.Wrap<{
readonly apiKey?: Redacted | undefined;
readonly apiUrl?: string | undefined;
readonly organizationId?: Redacted | undefined;
readonly projectId?: Redacted | undefined;
readonly transformClient?: (client: HttpClient) => HttpClient;
}>) => Layer.Layer<AiModels | OpenAiClient.OpenAiClient, ConfigError, HttpClient>

@since1.0.0

layerConfig
({
apiKey?: Config.Config<Redacted<string> | undefined>
apiKey
:
import Config
Config
.
const redacted: (name?: string) => Config.Config<Redacted> (+1 overload)

Constructs a config for a redacted value.

@since2.0.0

redacted
("OPENAI_API_KEY")
})
// Provide a platform-specific implementation of `HttpClient` to our
// OpenAi layer
//
// ┌─── Layer<OpenAiClient, never, never>
// ▼
const
const OpenAiWithHttp: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, never>
OpenAiWithHttp
=
import Layer
Layer
.
const provide: <HttpClient, ConfigError, OpenAiClient.OpenAiClient | AiModels, never, never, HttpClient>(self: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, HttpClient>, that: Layer.Layer<...>) => Layer.Layer<...> (+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.

@since2.0.0

provide
(
const OpenAi: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, HttpClient>
OpenAi
,
import NodeHttpClient
NodeHttpClient
.
const layerUndici: Layer.Layer<HttpClient, never, never>

@since1.0.0

layerUndici
)

Now that we have a Layer which provides us with an OpenAiClient, we’re ready to make our main program runnable.

Our final program looks like the following:

import {
import OpenAiClient
OpenAiClient
,
import OpenAiCompletions
OpenAiCompletions
} from "@effect/ai-openai"
import {
import Completions
Completions
} from "@effect/ai"
import {
import NodeHttpClient
NodeHttpClient
} from "@effect/platform-node"
import {
import Config
Config
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Layer
Layer
} from "effect"
const
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Tag<Completions.Completions, Completions.Completions.Service>> | YieldWrap<Effect.Effect<AiResponse, AiError, never>>, AiResponse>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const completions: Completions.Completions.Service
completions
= yield*
import Completions
Completions
.
class Completions

@since1.0.0

@since1.0.0

Completions
const
const response: AiResponse
response
= yield*
const completions: Completions.Completions.Service
completions
.
Completions.Service.create: (input: Input) => Effect.Effect<AiResponse, AiError>
create
("Generate a dad joke")
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(
const response: AiResponse
response
.
AiResponse.text: string

@since1.0.0

text
)
return
const response: AiResponse
response
})
const
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>
Gpt4o
=
import OpenAiCompletions
OpenAiCompletions
.
const model: (model: (string & {}) | OpenAiCompletions.Model, config?: Omit<OpenAiCompletions.Config.Service, "model">) => AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>

@since1.0.0

model
("gpt-4o")
const
const main: Effect.Effect<void, AiError, OpenAiClient.OpenAiClient | AiModels>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<AiResponse, AiError, never>> | YieldWrap<Effect.Effect<AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions.Completions | Tokenizer>, never, OpenAiClient.OpenAiClient | AiModels>>, void>(f: (resume: Effect.Adapter) => Generator<...>) => Effect.Effect<...> (+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}`
})

@since2.0.0

gen
(function*() {
const
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
= yield*
const Gpt4o: AiModel<Completions.Completions | Tokenizer, OpenAiClient.OpenAiClient>
Gpt4o
const
const response: AiResponse
response
= yield*
const gpt4o: AiPlan.Provider<Completions.Completions | Tokenizer>
gpt4o
.
AiPlan<in Error, in out Provides, in out Requires>.Provider<Completions | Tokenizer>.provide: <AiResponse, AiError, Completions.Completions>(effect: Effect.Effect<AiResponse, AiError, Completions.Completions>) => Effect.Effect<...>
provide
(
const generateDadJoke: Effect.Effect<AiResponse, AiError, Completions.Completions>
generateDadJoke
)
})
const
const OpenAi: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, HttpClient>
OpenAi
=
import OpenAiClient
OpenAiClient
.
const layerConfig: (options: Config.Config.Wrap<{
readonly apiKey?: Redacted | undefined;
readonly apiUrl?: string | undefined;
readonly organizationId?: Redacted | undefined;
readonly projectId?: Redacted | undefined;
readonly transformClient?: (client: HttpClient) => HttpClient;
}>) => Layer.Layer<AiModels | OpenAiClient.OpenAiClient, ConfigError, HttpClient>

@since1.0.0

layerConfig
({
apiKey?: Config.Config<Redacted<string> | undefined>
apiKey
:
import Config
Config
.
const redacted: (name?: string) => Config.Config<Redacted> (+1 overload)

Constructs a config for a redacted value.

@since2.0.0

redacted
("OPENAI_API_KEY")
})
const
const OpenAiWithHttp: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, never>
OpenAiWithHttp
=
import Layer
Layer
.
const provide: <HttpClient, ConfigError, OpenAiClient.OpenAiClient | AiModels, never, never, HttpClient>(self: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, HttpClient>, that: Layer.Layer<...>) => Layer.Layer<...> (+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.

@since2.0.0

provide
(
const OpenAi: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, HttpClient>
OpenAi
,
import NodeHttpClient
NodeHttpClient
.
const layerUndici: Layer.Layer<HttpClient, never, never>

@since1.0.0

layerUndici
)
const main: Effect.Effect<void, AiError, OpenAiClient.OpenAiClient | AiModels>
main
.
Pipeable.pipe<Effect.Effect<void, AiError, OpenAiClient.OpenAiClient | AiModels>, Effect.Effect<void, AiError | ConfigError, never>, Promise<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Promise<...>): Promise<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provide: <OpenAiClient.OpenAiClient | AiModels, ConfigError, never>(layer: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, never>) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+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 Layers, 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.

@seeprovideService for providing a service to an effect.

@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"
// []

@since2.0.0

provide
(
const OpenAiWithHttp: Layer.Layer<OpenAiClient.OpenAiClient | AiModels, ConfigError, never>
OpenAiWithHttp
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <A, E>(effect: Effect.Effect<A, E, never>, options?: {
readonly signal?: AbortSignal;
} | 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.

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@example

// Title: Running a Successful Effect as a Promise
import { Effect } from "effect"
// Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

@example

//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

@since2.0.0

runPromise
)