ai-lambda-service
v1.3.0
Published
Run a local REST server from a declarative JSON config with AI or JS handlers.
Downloads
514
Maintainers
Readme
ai-lambda-service
Run a local REST server from a declarative JSON config. Each endpoint can be backed by an OpenAI prompt, a JavaScript handler, a Microsoft 365 Copilot query via WorkIQ, or a chain of multiple endpoints for advanced workflows.
Installation
Install globally via npm:
npm install -g ai-lambda-serviceOr add it as a project dependency:
npm install ai-lambda-serviceQuick start
- Add your OpenAI key to
.env:
OPENAI_API_KEY=sk-...Create a config file (e.g.,
config.json) — see Configuration below.Start the server:
ai-lambda-service start -c config.jsonOr if installed locally, use npx:
npx ai-lambda-service start -c config.json- Try the sample endpoints:
- AI prompt:
POST http://localhost:4000/ai-greetingwith{ "name": "Ada" } - JS handler:
POST http://localhost:4000/sumwith{ "a": 1, "b": 2 } - AI prompt (GET):
GET http://localhost:4000/countries?continent=Europe - WorkIQ query:
GET http://localhost:4000/meetings?day=today&timeOfDay=morning - Local LLM:
GET http://localhost:4000/local-joke?topic=programming(requires LM Studio)
- Or use the interactive dashboard — open
http://localhost:4000in your browser!
Interactive Dashboard
When you visit the root URL of your running service, you'll see an interactive dashboard that lets you explore and test all endpoints without writing any code or using curl.

Features:
- Lists all configured endpoints with method, path, and description
- Shows handler type (AI Prompt, JS Handler, or WorkIQ Query) for each endpoint
- Auto-generates input fields based on each endpoint's
inputSchema - Send requests with one click and see formatted JSON responses
- Displays response status and timing information
This makes it easy to:
- Explore available endpoints during development
- Test endpoints with different inputs
- Debug responses without leaving the browser
- Share API documentation with your team
Configuration
- See CONFIG.md for the JSON schema and field descriptions.
- Sample config: examples/basic.json
- Sample JS handler: examples/handlers/sum.js
- Includes endpoints:
hello-ai,sum-js, andcountries(GET withcontinentquery)
- Includes endpoints:
Minimal config example
Save this as config.json (paths in jsHandler.file are relative to this file):
{
"port": 4000,
"endpoints": [
{
"name": "hello-ai",
"description": "Return a friendly greeting using an OpenAI prompt.",
"path": "/ai-greeting",
"method": "POST",
"inputSchema": {
"type": "object",
"required": ["name"],
"properties": { "name": { "type": "string" } },
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"required": ["greeting"],
"properties": { "greeting": { "type": "string" } },
"additionalProperties": false
},
"aiPrompt": {
"prompt": "Write a JSON object with a key 'greeting' that greets the provided name in one short sentence.",
"model": "gpt-5-mini",
"temperature": 1
}
},
{
"name": "sum-js",
"description": "Sum two numbers using a JS handler.",
"path": "/sum",
"method": "POST",
"inputSchema": {
"type": "object",
"required": ["a", "b"],
"properties": { "a": { "type": "number" }, "b": { "type": "number" } },
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"required": ["sum"],
"properties": { "sum": { "type": "number" } },
"additionalProperties": false
},
"jsHandler": {
"file": "handlers/sum.js"
}
}
]
}Create handlers/sum.js next to the config:
module.exports = async (input) => {
return { sum: Number(input.a) + Number(input.b) };
};Environment
.envis loaded automatically.OPENAI_API_KEYis required for any endpoint usingaiPrompt(unless using a local LLM server).
Local LLM Support (LM Studio, Ollama, etc.)
You can use any OpenAI-compatible LLM server instead of OpenAI. This works great with:
- LM Studio - Run local models with a GUI
- Ollama - Run local models from the command line
- vLLM - High-throughput LLM serving
- Any other OpenAI-compatible server
Configuration Options
| Field | Level | Description |
|-------|-------|-------------|
| defaultBaseUrl | Top-level | Default LLM server URL for all endpoints |
| defaultModel | Top-level | Default model name for all endpoints |
| defaultApiKey | Top-level | Default API key (falls back to OPENAI_API_KEY env var) |
| baseUrl | Per-endpoint | Override server URL for this endpoint |
| model | Per-endpoint | Override model for this endpoint |
| apiKey | Per-endpoint | Override API key for this endpoint |
Example: Using LM Studio
- Start LM Studio and load a model (e.g.,
llama-3.2-3b-instruct) - Enable the local server (default:
http://localhost:1234/v1) - Configure your endpoint:
{
"defaultBaseUrl": "http://localhost:1234/v1",
"defaultModel": "llama-3.2-3b-instruct",
"endpoints": [
{
"name": "local-greeting",
"description": "Generate a greeting using local LLM",
"path": "/greeting",
"method": "POST",
"inputSchema": {
"type": "object",
"required": ["name"],
"properties": { "name": { "type": "string" } }
},
"aiPrompt": {
"prompt": "Generate a friendly greeting for the provided name."
}
}
]
}Tips for Local LLMs
- Plain text responses: Local models are more reliable without
outputSchema. Omit it to get plain text responses instead of JSON. - Explicit prompts: Local models may need more explicit instructions than OpenAI models.
- Model names: Use the exact model name shown in LM Studio or Ollama.
- No API key required: Local servers typically don't require authentication.
Mixed Configuration (Local + Cloud)
You can use both local and cloud LLMs in the same config:
{
"endpoints": [
{
"name": "cloud-endpoint",
"description": "Uses OpenAI",
"path": "/cloud",
"method": "POST",
"aiPrompt": {
"prompt": "...",
"model": "gpt-4o"
}
},
{
"name": "local-endpoint",
"description": "Uses local LM Studio",
"path": "/local",
"method": "POST",
"aiPrompt": {
"prompt": "...",
"baseUrl": "http://localhost:1234/v1",
"model": "llama-3.2-3b-instruct"
}
}
]
}See CONFIG.md for full documentation on all aiPrompt options.
WorkIQ Integration
Endpoints can query Microsoft 365 Copilot using WorkIQ. This enables natural language queries about your emails, meetings, files, and other M365 data.
Prerequisites
- Install WorkIQ CLI globally:
npm install -g @microsoft/workiq- Authenticate with your Microsoft 365 account:
workiq authWorkIQ Endpoint Example
{
"name": "meetings",
"description": "Get meetings for a specific day",
"path": "/meetings",
"method": "GET",
"inputSchema": {
"type": "object",
"required": ["day"],
"properties": {
"day": { "type": "string", "description": "Date like 'today', 'tomorrow', or '2/4/2026'" },
"timeOfDay": { "type": "string", "description": "morning, afternoon, or evening" }
}
},
"outputSchema": {
"type": "object",
"required": ["meetings"],
"properties": {
"meetings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": { "type": "string" },
"time": { "type": "string" }
}
}
}
}
},
"workiqQuery": {
"query": "What meetings do I have on {{day}} {{timeOfDay}}? Return as JSON with a 'meetings' array containing objects with 'title' and 'time' fields."
}
}How It Works
- The
queryfield supports{{variable}}placeholders that are replaced with input values - The service connects to WorkIQ's MCP (Model Context Protocol) server for efficient communication
- Falls back to CLI execution if MCP is unavailable
- EULA is automatically accepted on first connection
CLI
Implemented in bin/ai-lambda-service.js.
ai-lambda-service start -c <config.json> -p <port> -v <level>
ai-lambda-service stop-c, --config: path to JSON config (default./config.json)-p, --port: port override (else uses config.port or 3000)-v, --verbose:debug|info|warn|error(defaultinfo)
How it works
- Config is validated with AJV in src/config.js.
- Routes are bound in src/server.js using Express.
- Each endpoint uses either an OpenAI chat completion, a JS handler, a WorkIQ query, or a chain of other endpoints via src/engine.js.
- Input/output validation uses JSON Schema per-endpoint.
- Output format: When
outputSchemais defined, responses are JSON. Without it, AI endpoints return plain text directly—useful for translations, summaries, etc.
Chaining Endpoints
Create advanced workflows by chaining multiple endpoints together. The output of one endpoint becomes the input to the next:
{
"name": "greeting-analyzed",
"path": "/greeting-analyzed",
"method": "POST",
"chainHandler": {
"steps": [
{
"name": "greet",
"endpoint": "greeting",
"input": { "name": "{{input.name}}" }
},
{
"name": "analyze",
"endpoint": "sentiment",
"input": { "text": "{{greet.greeting}}" }
}
],
"output": {
"greeting": "{{greet.greeting}}",
"sentiment": "{{analyze.sentiment}}"
}
}
}This chain:
- Generates a greeting for the provided name
- Analyzes the sentiment of the greeting
- Returns both the greeting and sentiment
Template syntax: Use {{input.field}}, {{stepName.field}}, or {{previousStep.field}} to reference data between steps.
Features:
- Sequential execution with data flow between steps
- Full validation at each step
- Circular dependency detection at startup
- Clear error messages with step context
See examples/chain.json for complete examples and CONFIG.md for full documentation.
Testing
npm testMocha + Supertest cover config loading and JS handler endpoints. Test fixtures live in test/fixtures.
License
ISC
