ai-structured-tooling
v1.0.3
Published
A flexible library for using AI providers with structured outputs. Supports OpenAI, Anthropic, Google, and custom providers.
Maintainers
Readme
AI Structured Tooling
A flexible, type-safe library for using AI providers with structured outputs. Supports OpenAI, Anthropic, Google Gemini, and custom providers.
Features
- 🔌 Multiple Built-in Providers: OpenAI, Anthropic (Claude), Google Gemini
- 🛠️ Custom Provider Support: Register your own AI providers
- 📦 Structured Outputs: Type-safe responses using Zod schemas
- ⚙️ Config-Driven Agents: Create agents from serializable JSON config files
- 🔑 Simple API Key Management: Register and manage API keys for multiple providers
- 📘 Full TypeScript Support: Complete type definitions included
- 🎯 Minimal Dependencies: Only requires Zod, provider SDKs are optional
Installation
npm install ai-structured-tooling zodProvider SDKs (Optional)
Install only the providers you need:
# For OpenAI
npm install openai
# For Anthropic (Claude)
npm install @anthropic-ai/sdk
# For Google (Gemini)
npm install @google/genaiQuick Start
1. Register API Keys
import { registerAPIKeys } from "ai-structured-tooling";
registerAPIKeys({
openai: process.env.OPENAI_API_KEY,
anthropic: process.env.ANTHROPIC_API_KEY,
google: process.env.GEMINI_API_KEY,
});2. Use Built-in Providers
import { useAI } from "ai-structured-tooling";
import { z } from "zod";
// Simple text response
const response = await useAI({
system: "You are a helpful assistant.",
content: "What is the capital of France?",
provider: "anthropic",
});
const answer = await response.chat();
console.log(answer);
// Structured output with Zod schema
const WeatherSchema = z.object({
location: z.string(),
temperature: z.number(),
condition: z.string(),
});
const weatherResponse = await useAI(
{
system: "You are a weather assistant.",
content: "What is the weather in New York?",
provider: "openai",
},
WeatherSchema,
);
const weather = weatherResponse.parsed();
console.log(`Temperature: ${weather.temperature}°F`);3. Config-Driven Agents
Create an agent from a JSON-serializable config that can target either provider mode or framework mode.
import {
createAgentFromConfig,
registerAgentSchema,
registerAgentTool,
} from "ai-structured-tooling";
import { z } from "zod";
const TicketSchema = z.object({
title: z.string(),
priority: z.enum(["low", "medium", "high"]),
});
registerAgentSchema("ticket", TicketSchema);
registerAgentTool("searchDocs", {
name: "searchDocs",
description: "Search docs",
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => [{ title: "result", query }],
});
const agent = createAgentFromConfig({
version: 1,
runtime: { kind: "provider" },
io: { mode: "structured", schemaRef: "ticket" },
defaults: { provider: "openai", model: "gpt-4o-mini" },
apiKeys: { openai: "${OPENAI_API_KEY}" },
});
const output = await agent.runStructured("Summarize this issue");Config shape
runtime.kind:"provider"or"framework"runtime.framework: required whenkindis"framework"io.mode:"simple"or"structured"io.schemaRef: required in structured modeio.toolRefs: optional tool names to resolve from registrydefaults.provider: required in provider modeapiKeys: optional provider-to-key map, supports${ENV_VAR}token syntax
Important behavior notes
apiKeysin config are applied per request for configured agents (no global key registration side effects).- If no per-request key is supplied for a provider, built-in providers fall back to globally registered keys (
registerAPIKey/registerAPIKeys). - Schema and tool refs must be registered before running an agent via
registerAgentSchemaandregisterAgentTool.
See examples/config-agent-demo.js for complete provider/framework/tooling examples.
Custom Provider Registration
Create and register your own AI providers:
import {
registerProvider,
registerAPIKey,
getRequiredAPIKey,
type ProviderFactory,
} from "ai-structured-tooling";
// Define your custom provider
const myCustomProvider: ProviderFactory = async (request, zodSchema) => {
const apiKey = getRequiredAPIKey("mycustom");
// Your custom API implementation
const response = await fetch("https://api.myai.com/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: request.content,
system: request.system,
}),
});
const data = await response.json();
// Parse and validate with the provided schema
const parsed = zodSchema.parse(data);
return {
input: request,
output: parsed,
};
};
// Register your provider
registerProvider("mycustom", myCustomProvider);
registerAPIKey("mycustom", "your-api-key");
// Use it like any built-in provider
const result = await useAI({
system: "You are helpful.",
content: "Hello!",
provider: "mycustom",
});API Reference
Provider Management
registerAPIKey(provider: string, apiKey: string): void
Register an API key for a provider.
registerAPIKeys(keys: Record<string, string>): void
Register multiple API keys at once.
getAPIKey(provider: string): string | undefined
Get a registered API key.
hasAPIKey(provider: string): boolean
Check if an API key is registered.
clearAPIKey(provider: string): void
Remove an API key.
getRegisteredProviders(): string[]
Get list of providers with registered API keys.
getAvailableProviders(): string[]
Get list of all available providers (built-in + custom).
Custom Providers
registerProvider(name: string, factory: ProviderFactory): void
Register a custom AI provider.
Parameters:
name: Unique provider name (cannot conflict with built-in providers)factory: Function implementing the provider logic
Example:
registerProvider("myai", async (request, zodSchema) => {
// Your implementation
return {
input: request,
output: parsedResponse,
};
});unregisterProvider(name: string): boolean
Unregister a custom provider.
isProviderAvailable(name: string): boolean
Check if a provider is available (built-in or custom).
AI Requests
useAI<T>(request: UseAIRequest, zodSchema?: ZodType): Promise<AIResponse<T>>
Make an AI request with optional structured output.
Parameters:
request.system: System prompt (default: "You are a helpful assistant.")request.content: User message/promptrequest.provider: Provider name (default: "anthropic")request.model: Optional model overridezodSchema: Optional Zod schema for structured output
Returns:
chat(): Get response as stringparsed(): Get typed, validated response
Built-in Provider Functions
You can also use provider-specific functions directly:
import { useOpenAi, useAnthropic, useGoogle } from "ai-structured-tooling";
const response = await useOpenAi(request, schema);Types
import type {
UseAIRequest,
ProviderResponse,
ProviderFactory,
LLMProvider,
LLMMessage,
LLMConfig,
} from "ai-structured-tooling";Examples
Multi-Provider Example
const providers = ["openai", "anthropic", "google"];
for (const provider of providers) {
if (hasAPIKey(provider)) {
const response = await useAI({
system: "You are a comedian.",
content: "Tell me a joke.",
provider,
});
console.log(`${provider}:`, await response.chat());
}
}Complex Structured Output
const AnalysisSchema = z.object({
sentiment: z.enum(["positive", "negative", "neutral"]),
confidence: z.number().min(0).max(1),
keywords: z.array(z.string()),
summary: z.string(),
});
const response = await useAI(
{
system: "You are a text analysis expert.",
content: 'Analyze this review: "Great product, highly recommend!"',
provider: "anthropic",
},
AnalysisSchema,
);
const analysis = response.parsed();
console.log(`Sentiment: ${analysis.sentiment} (${analysis.confidence})`);License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
