@aseity/lyra
v0.14.0
Published
Lyra agent library
Downloads
3,065
Readme
@aseity/lyra
Lyra is a TypeScript library for running tool-using agents across OpenAI, Anthropic, and compatible model providers. It includes streaming, sessions, handoffs, structured outputs, tracing, MCP clients, and sandbox capabilities.
Changelog
See the release history
for changes in each version. CHANGELOG.md is also included in the npm package.
Install
pnpm add @aseity/lyra zodQuick start
import { Agent, OpenAIProvider, run } from '@aseity/lyra';
const provider = new OpenAIProvider({
apiKey: process.env.OPENAI_API_KEY,
});
const model = await provider.getModel('gpt-5.4-mini');
const agent = new Agent({
name: 'Assistant',
instructions: 'Answer clearly and concisely.',
model,
});
const result = await run(agent, 'Hello!');
console.log(result.finalOutput);Pass concrete Model instances when model selection is owned by your
application. Use Runner when you need reusable workflow, tracing, or session
configuration.
Xiaomi MiMo
import { MiMoProvider, MIMO_MODEL_NAMES } from '@aseity/lyra';
const provider = new MiMoProvider(); // reads MIMO_API_KEY
const pro = await provider.getModel(); // mimo-v2.6-pro
const flash = await provider.getModel('mimo-v2.6-flash');
// MIMO_MODEL_NAMES contains the two built-in model names.MiMo uses the Anthropic Messages API
at https://api.xiaomimimo.com/anthropic. Pass apiKey, baseURL, or
defaultModel to override the defaults; MIMO_DEFAULT_MODEL also sets the
default model. Token Plan users can supply their plan's Anthropic base URL and key.
Both built-in models have a 1M-token context window and a 131,072-token output
limit, with image input, streaming, tool calls, and thinking replay.
Thinking follows the service default. Use modelSettings.reasoning.effort: 'none'
to disable it, or modelSettings.providerData.thinking: { type: 'enabled' }
to enable it explicitly. MiMo has no reasoning effort levels. In thinking mode,
the service fixes temperature and top-p to its own defaults.
See the model list
and thinking guide.
DeepSeek Responses JSON output
DeepSeekProvider continues to use Anthropic Messages. For JSON Schema output,
pass the separately exported DeepSeekResponsesModel directly to an agent:
import { Agent, DeepSeekResponsesModel, run } from '@aseity/lyra';
import { z } from 'zod';
const agent = new Agent({
name: 'Extractor',
model: new DeepSeekResponsesModel({
model: 'deepseek-v4-pro',
apiKey: process.env.DEEPSEEK_API_KEY,
}),
instructions: 'Extract the name and age from the input.',
outputType: z.object({ name: z.string(), age: z.number() }),
});
const result = await run(agent, 'Alice is 30 years old.');
console.log(result.finalOutput);The model sends outputType as text.format.type: 'json_schema'. It defaults to
https://api.deepseek.com and uses the configured DeepSeek key when apiKey is
omitted. Its baseURL is independent of DEEPSEEK_BASE_URL, which belongs to the
Anthropic provider. You can supply fetch, maxRetries, and metadata, or inject
an openAIClient instead of client configuration.
Streaming is supported. modelSettings.maxTokens overrides the default 32,768
output tokens; reasoning effort supports low, high, max, and none/null
to disable thinking. Omission preserves the server's thinking default.
Responses are stateless: send full history, not conversation or previous-response
IDs. See the DeepSeek Responses reference.
To run the non-streaming and streaming live checks with DEEPSEEK_API_KEY set:
bun packages/lyra/test/manual/deepseek-responses.ts (makes two paid API calls).
OpenRouter
OpenRouterProvider uses OpenRouter's Chat Completions endpoint while
preserving OpenRouter routing metadata, cost data, structured reasoning, and
in-band stream errors:
import { OpenRouterProvider } from '@aseity/lyra';
const provider = new OpenRouterProvider({
apiKey: process.env.OPENROUTER_API_KEY,
httpReferer: 'https://example.com',
appTitle: 'Example Agent',
defaultRequest: {
provider: { require_parameters: true },
},
});
const model = await provider.getModel('anthropic/claude-sonnet-4.6');Per-request OpenRouter extensions such as provider, models, plugins,
reasoning, and session_id can also be passed through
modelSettings.providerData.
License
MIT
Model parameter compatibility
See model parameter transport for modern effort support, legacy thinking budgets, explicit output validation, and migration from aliased effort values. Provider/model support is distinct from transport support.
MCP
Connect stdio, Streamable HTTP or SSE MCP servers and expose their tools to any Lyra tool-capable model provider. See MCP clients for connection management, authentication, filtering, resources and upstream compatibility.
Image validation
view_image validates inline PNG/JPEG/WebP/GIF bytes before native image results
or vision fallback requests, even when compression is disabled. It performs a
strict Sharp decode, including all animation frames; GIF block boundaries are
also checked because the decoder tolerates some truncated GIFs. Invalid images
return toolError text without image content, so the model can regenerate the
file and retry within the same turn. Source files are never repaired or rewritten.
A provider's later HTTP 400 still ends the turn; local validation does not guarantee
provider acceptance and does not add model retries. Remote image references from
custom sandboxes cannot be locally validated and return a tool error.
Byte and dimension limits are checked before full decoding. Host ceilings are 32 MiB per input, 8000 pixels per side and 64 million pixels summed across all frames; stricter model limits also apply. At most two decodes run concurrently per process. Validation uses streaming pixel statistics instead of returning a full raw pixel buffer. Queued calls retain their bounded input bytes, so callers must still bound tool-call concurrency. Sharp loads lazily on the first image. Static compression doubles as validation instead of decoding the image twice.
Offline regression: pnpm --filter @aseity/lyra exec vitest run test/view-image-validation.test.ts.
Set LYRA_TEST_DOCKER_IMAGE to an existing image with bash to include real Docker
Runner recovery. bun packages/lyra/test/manual/image-validation.ts measures four
concurrent calls at roughly 1, 16 and 64 million pixels; optional file arguments
replay local failures without contacting a model. Local 64-million-pixel runs took
6.8 seconds for four calls with sampled process RSS peaking near 232 MiB; these
measurements depend on codecs, pixels, runtime, and machine, and are not a memory guarantee.
Optional view_image compression
view_image preserves original image bytes by default. To reduce inline image payloads:
filesystem({ viewImageCompression: true })This applies to both native vision and viewImageCaptioner requests. Static inline
images are encoded as JPEG at quality 90 with 4:4:4 chroma, without downscaling;
EXIF orientation is applied and transparent backgrounds become white. If encoding
would increase the payload, the original is retained. Animated images retain their original bytes after validation. Source files are never overwritten; returned
MIME type, byte size and dimensions describe the actual transmitted image.
Validation and compression share the installed sharp dependency.
Existing input and model image limits still apply. This reduces individual image
payloads but does not limit accumulated conversation size or guarantee avoidance
of HTTP 413 responses. Lossy compression can affect fine text; retain original
files for detailed crops and verification.
Docker lifecycle
Docker startup failures are cleaned up before the error returns. If cleanup also fails, catch DockerSandboxStartupError, retain the execution-resource reservation, and retry error.cleanup() after Docker recovers. See Docker startup and cleanup for the error contract, cancellation, and shutdown behavior.
See explicit skill input for manual selection, discovery flags, original message display and frozen model history.
