Liminal Quickstart beta
Installation
Install liminal with your JavaScript package manager of choice.
npm i liminalbun i liminaldeno add npm:liminalpnpm i liminalyarn install liminalNOTE
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-openaibun install @effect/ai-openaideno add @effect/ai-openaipnpm @effect/ai-openaiyarn add @effect/ai-openaiConversation 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.