Liminal Quickstart beta
Installation
Install liminal
with your JavaScript package manager of choice.
npm i liminal
bun i liminal
deno add npm:liminal
pnpm i liminal
yarn install liminal
NOTE
Depending on whether you package manager auto-installs peer dependencies, you may need to also install effect
, @effect/platform
and @effect/ai
.
Additionally, install an Effect AI model provider, which we'll use to execute our conversations.
npm install @effect/ai-openai
bun install @effect/ai-openai
deno add @effect/ai-openai
pnpm @effect/ai-openai
yarn add @effect/ai-openai
Conversation Effects
Liminal's effects and effect factories are accessible from the L
namespace.
import L from "liminal"
L.
We yield Liminal Effects within an Effect.gen
body to control the underlying conversation state without manually managing structures the list of messages.
const conversation = Effect.gen(function*() {
// Append some initial messages.
yield* L.user`...`
yield* L.assistant
yield* L.user`...`
yield* L.assistant
// Ask for a summary.
yield* L.user`Summarize our conversation.`
const summary = yield* L.assistant
// Clear the current conversation.
yield* L.clear
// Append the summary.
yield* L.user`Conversation summary: ${summary}`
})
Example Use Case
Let's consider a function that validates an email address and returns either a validation error message or undefined if the supplies address is valid.
const validateEmail = (email: string): string | undefined => {
// If valid, return undefined.
if (EMAIL_REGEX.test(email)) return
// If invalid, return the error message.
return "Invalid email format."
}
The error message we return is opaque; the caller lacks information about why validation failed.
Let's use Liminal to infer a helpful validation error message. Also note how we pipe
the effect into L.thread
to mark the boundary of the conversation.
export const validateEmail = (email: string) =>
Effect.gen(function*() {
// If valid, return undefined.
if (EMAIL_REGEX.test(email)) return
// Otherwise, set the system prompt.
yield* L.system`You are an email-validating assistant.`
// If invalid, ask why.
yield* L.user`Why is the following email is invalid?: "${email}".`
// Infer and return the message.
return yield* L.assistant
}).pipe(
L.thread,
)
Configuring Model Layer
Some Liminal effects require a language model to specified. This is provided using the Effect AI AiLanguageModel
tag.
Let's create a layer to provide an OpenAI AiLanguageModel
.
ModelLive.ts
import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai"
import { FetchHttpClient } from "@effect/platform"
import { Config, Layer } from "effect"
export const ModelLive = OpenAiLanguageModel.model("gpt-4o-mini").pipe(
Layer.provide(
OpenAiClient.layerConfig({
apiKey: Config.redacted("OPENAI_API_KEY"),
}).pipe(
Layer.provide(FetchHttpClient.layer),
),
),
)
Running the Conversation
We can now provide the model
layer to any effect's we're ready to execute. You may want to satisfy requirements once at the root of your effect program. Alternatively, you can use it in the leaves of your program.
import { ModelLive } from "./ModelLive.ts"
import { validateEmail } from "./validateEmail.ts"
const errorMessage = await validateEmail("≽^•⩊•^≼").pipe(
Effect.provide(ModelLive),
Effect.runPromise,
)
If the supplied email address is invalid, we may get error messages similar to the following.
john..doe@example
: Your email is missing the top-level domain (like .com or .org) after 'example'.user@domain
: Your email address is incomplete and missing the domain extension.john@example..com
: Your email contains consecutive dots which aren't allowed in a valid address.
In the next chapter, we touch on key concepts surrounding Liminal's implementation and usage.