Appearance
Deep Dive: Hidden Gems
You know about Recipes and Adapters. But llm-core has some "hidden gems"—advanced features and safeguards that solve hard problems so you don't have to.
1. Introspection & Control
Runtime Explainability ("Why did it act like this?")
When you compose complex recipes with plugins overriding other plugins, it's hard to know what the final configuration looks like. llm-core builds an Explain Snapshot for every runtime.
Why you care: You can debug exactly which plugin replaced which, without guessing.
ts
const runtime = agent.build();
// Programmatic access to the "resolved" state
console.log(runtime.explain());This snapshot tells you:
- Overrides: Which plugins replaced which (e.g., "MockLLM overrides OpenAI").
- Unused: Plugins you added but nothing used (dead code detection!).
- Missing: Capabilities that are required but not provided.
Lifecycle Safety Nets
Plugin authors often make mistakes—like hooking into a lifecycle event that doesn't exist. In default mode, llm-core logs a warning. In strict mode, it crashes the build.
Why you care: Prevents the "Why didn't my analytics plugin fire?" bug that plagues other frameworks.
ts
// Extensions.ts internals
if (!isLifecycleScheduled(lifecycleSet, plugin.lifecycle)) {
// We know BEFORE running that this plugin will never fire
diagnostics.push(createLifecycleDiagnostic(...));
}Universal Telemetry Normalisation
Every provider (OpenAI, Anthropic, Ollama) returns token usage, timestamps, and model IDs differently. llm-core normalises this into a single, reliable ModelTelemetry object:
- Timestamps: Always converted to milliseconds.
- Usage: Always
inputTokens/outputTokens(normalisingprompt_evalandusage_metadata). - IDs: Reliable
modelIdextraction from nested metadata.
Plugin Override Mechanics
The getEffectivePlugins algorithm isn't just a list merge. It implements a strict Key-Based Override System. If you register a plugin with mode: "override" and overrideKey: "base", it surgically replaces the base plugin in the execution graph.
2. State & Long-Running Flows
Pausing Safely (Hot-State & Serialization)
Most "pause/resume" systems require you to serialize state to a database and reload it. llm-core supports Hot-State Resumption.
Why you care: You can pause a workflow for human approval without inventing your own state machine.
If a workflow pauses (e.g., waiting for a tool), the runtime keeps the actual iterator in memory efficiently.
ts
// 1. Run until paused
const result = await workflow.run(input);
const token = result.token;
// 2. Resume instantly (in-memory, no DB roundtrip needed if same process)
const nextResult = await workflow.resume({ token, input: "tool output" });The runtime's snapshotRecorder also handles Circular References and BigInts automatically during serialization, so you don't crash when saving complex state.
3. Inputs & Content
Universal Content Normalisation
LLM inputs are messy: strings, arrays, buffers, Base64, data URLs. The Adapter layer includes a universal content normaliser.
Why you care: You don't rewrite your code every time your front-end team changes how it sends images.
ts
import { toMessageContent } from "@geekist/llm-core/adapters";
// All of these become the SAME standard structure
toMessageContent("hello");
toMessageContent({ parts: [{ type: "text", text: "hello" }] });
toMessageContent(Buffer.from("hello"));Reasoning & Chain-of-Thought Support
The MessageContent primitives natively support a reasoning part type, future-proofing your apps for "Thinking Models" (like o1/DeepSeek) even if the current adapter doesn't emit them yet.
Stream Polyfilling
Not all models support streaming output for structured data (JSON). llm-core's AI SDK adapter detects when a provider refuses to stream objects and gracefully falls back to generateObject (non-streaming) or emulates the stream where possible.
4. Foundations (Under the Hood)
Core Utilities & Primitives
The library is built on strong functional foundations:
- Monadic Async Support:
MaybePromise<T>(viasrc/maybe.ts) allows utilities to work seamlessly with both sync and async values, avoiding "function coloring" issues. - Centralized Validation:
src/adapters/input-validation.tsprovides uniform diagnostic warning generation. - Universal Query Normalisation:
src/adapters/retrieval-query.tshandles converting multi-modal inputs into searchable strings.
Async Extension Coordination
If a plugin needs to initialize a database connection before the workflow starts, llm-core supports Async Registration. The runtime automatically waits for all register promises to settle before executing the first step.
Why you care: Eliminates race conditions during agent startup.
ts
// Plugin definition
const dbPlugin = {
key: "database",
register: async () => {
await db.connect(); // Runtime waits for this!
return { ... };
}
};The Adapter Polyglot
Adapters function as a Universal Translator for the entire AI ecosystem.
- Bidirectional Tool Interop: You can use a LangChain Tool inside an AI SDK Agent. The adapters normalise
ZodvsJSON Schemaon the fly. - Vector Store Normalisation: A unified
upsert/deleteinterface that handles both raw documents and pre-computed vectors. - Async Agnosticism: Wrap a synchronous local embedding model (e.g., ONNX) without forcing an unnecessary
awaittick.
5. Putting it Together: The "Doc Review" Story
How do these gems combine in real life? Consider a Legal Doc Review Agent.
The User: Uploads a PDF contract screenshot and asks, "Is the indemnity clause standard?"
- Unified Input: You pass the image Buffer and user text to
workflow.run(). The Content Normaliser handles the multi-modal structure automatically. - Execution: The agent identifies a suspicious clause but needs a Partner's approval.
- Pause: The agent returns
status: "paused"with atoken. You save this token to your database. The runtime Hot-State Serializer ensures the entire analysis state is preserved safely. - Tracing: While paused, your dev team inspects the
result.trace. They see exactly why the agent flagged the clause (Chain-of-Thought reasoning), even though the process is halted. - Resume: The Partner clicks "Approve" in your UI. You call
workflow.resume({ token, input: "Approved" }). The agent wakes up instantly (in-memory if possible) and finishes the job.
This is the llm-core promise: Complex, long-running, multi-modal workflows made to feel like simple function calls.