Skip to content
schemaloomPublic

About

The main TypeScript API service that transforms unstructured text into schema-validated structured data. Exposes a minimal REST interface for LLM-powered extraction with Zod validation, packaged as a single Docker image for deploy-anywhere simplicity.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

NPM Version

SchemaLoom

A TypeScript library for AI-powered data extraction and schema validation with integrated web server capabilities.

Overview

SchemaLoom provides a robust framework for extracting structured data from unstructured text using AI models. The library combines the power of LangChain and Google Gemini with flexible schema validation through Zod, offering both programmatic and web-based interfaces.

Features

  • AI-Powered Extraction: Leverage Google Gemini models for intelligent data extraction
  • Schema Validation: Comprehensive schema definition and validation using Zod
  • Flexible Chunking: Configurable text chunking with overlap control
  • Multiple Interfaces: Use as a library or deploy as a web service
  • Type Safety: Full TypeScript support with comprehensive type definitions
  • Extensible Architecture: Support for custom schemas and extraction logic
  • Validation & Error Handling: Per-chunk error reporting and post-extraction graph validation

Installation

npm install schemaloom

Quick Start

Library Usage

import { SchemaLoomExtractor, GeminiProvider, EventListSchema } from 'schemaloom';

// Initialize extractor with custom configuration
const extractor = new SchemaLoomExtractor({
  chunkSize: 100000,
  chunkOverlap: 0,
  temperature: 0
});

// Configure AI provider
const provider = new GeminiProvider({
  model: "gemini-3-flash-preview",
  temperature: 0
});

// Extract structured data
const result = await extractor.extract(
  "Your text content here...",
  EventListSchema,
  provider.getLLM()
);

console.log(result.data);        // Extracted data
console.log(result.metadata);    // Processing information

Web Server Deployment

import { startServer } from 'schemaloom/server';

// Start server with custom configuration
startServer({
  port: 3000,
  host: 'localhost'
});

Core Components

SchemaLoomExtractor

The primary extraction engine that handles text processing, chunking, and AI model interaction.

Configuration Options

interface ExtractionOptions {
  chunkSize?: number;        // Default: 100000
  chunkOverlap?: number;     // Default: 0
  temperature?: number;       // Default: 0
  model?: string;            // Default: "gemini-3-flash-preview"
}

Methods

  • extract<T>(text: string, schema: z.ZodSchema<T>, provider: any): Promise<ExtractionResult<T>>
  • extractBatch<T>(chunks: TextChunk[], schema: z.ZodSchema<T>, provider: any): Promise<ExtractionResult<T[]>>
  • getOptions(): ExtractionOptions
  • updateOptions(newOptions: Partial<ExtractionOptions>): void

GeminiProvider

Manages Google Gemini AI model interactions with configurable parameters.

Configuration

interface GeminiOptions {
  model?: string;            // Default: "gemini-3-flash-preview"
  temperature?: number;       // Default: 0
}

Methods

  • getLLM(): ChatGoogleGenerativeAI
  • updateConfig(options: Partial<GeminiOptions>): void

Predefined Schemas

The library includes several pre-built schemas for common use cases:

Event Schema

const EventSchema = z.object({
  name: z.string(),
  date: z.string(),
  place: z.string()
});

Product Schema

const ProductSchema = z.object({
  name: z.string(),
  price: z.number(),
  category: z.string(),
  description: z.string().optional(),
  brand: z.string().optional(),
  sku: z.string().optional()
});

Article Schema

const ArticleSchema = z.object({
  title: z.string(),
  author: z.string().optional(),
  publishDate: z.string().optional(),
  content: z.string().optional(),
  tags: z.array(z.string()).optional(),
  summary: z.string().optional()
});

Contact Schema

const ContactSchema = z.object({
  name: z.string(),
  email: z.string().optional(),
  phone: z.string().optional(),
  company: z.string().optional(),
  position: z.string().optional(),
  address: z.string().optional()
});

Invoice Schema

const InvoiceSchema = z.object({
  invoiceNumber: z.string(),
  date: z.string(),
  dueDate: z.string().optional(),
  customer: z.string(),
  items: z.array(z.object({
    description: z.string(),
    quantity: z.number(),
    unitPrice: z.number(),
    total: z.number()
  })),
  subtotal: z.number(),
  tax: z.number().optional(),
  total: z.number()
});

Web Server API

Endpoints

  • GET /health - Health check endpoint
  • GET /extract - Schema and parameter documentation
  • POST /extract - Data extraction using predefined schemas
  • POST /extract/custom - Data extraction using custom schema definitions

Predefined Schema Extraction

POST /extract?schema=product&chunkSize=50000&chunkOverlap=1000&temperature=0.1
Content-Type: multipart/form-data

file: [your-file]

Custom Schema Extraction

POST /extract/custom
Content-Type: application/json

{
  "content": "Your text content here...",
  "schemaDefinition": {
    "type": "object",
    "properties": {
      "name": {"type": "string"},
      "value": {"type": "number"},
      "active": {"type": "boolean"}
    }
  },
  "chunkSize": 50000,
  "temperature": 0
}

Advanced Usage

Custom Pipeline Creation

import { createPipeline } from 'schemaloom/server';
import { Hono } from 'hono';

// Create base pipeline
const baseApp = createPipeline();

// Extend with custom functionality
const customApp = new Hono();

// Add custom routes
customApp.get('/status', (c) => c.json({ status: 'healthy' }));

// Mount extraction routes
customApp.route('/api', baseApp);

// Add middleware
customApp.use('*', async (c, next) => {
  console.log(`${c.req.method} ${c.req.url}`);
  await next();
});

Batch Processing

const chunks = [
  { content: "Text chunk 1", index: 0 },
  { content: "Text chunk 2", index: 1 }
];

const result = await extractor.extractBatch(
  chunks,
  CustomSchema,
  provider.getLLM()
);

Schema Registry Access

import { SchemaRegistry } from 'schemaloom';

// Access predefined schemas
const productSchema = SchemaRegistry.product;
const articleSchema = SchemaRegistry.article;

// Use in extraction
const result = await extractor.extract(
  content,
  productSchema,
  provider.getLLM()
);

Configuration

Environment Variables

GOOGLE_API_KEY=your_google_api_key_here

Server Options

interface ServerOptions {
  port?: number;      // Default: 3000
  host?: string;      // Default: 'localhost'
  cors?: boolean;     // Default: false
}

Error Handling

The library provides comprehensive error handling with detailed error messages and metadata:

interface ExtractionResult<T> {
  data: T;
  chunks: number;
  processingTime: number;
  errors?: string[];
}

Performance Considerations

  • Chunk Size: Larger chunks reduce API calls but may impact accuracy
  • Chunk Overlap: Overlap preserves context between chunks
  • Temperature: Lower values (0-0.3) for factual extraction, higher (0.7-1.0) for creative tasks
  • Model Selection: Choose models based on accuracy vs. speed requirements

Development

# Install dependencies
npm install

# Build library
npm run build

# Development mode with watch
npm run dev

# Start server
npm start

TypeScript Support

Full TypeScript support with comprehensive type definitions:

import type { 
  ExtractionOptions, 
  ExtractionResult, 
  TextChunk, 
  ExtractionProvider, 
  ServerOptions 
} from 'schemaloom';

Evaluation

eval/ measures how well the conceptGraph extraction works on a hand-labelled dataset. It is split the way Inspect AI / OpenAI Evals split things: dataset (eval/cases, src/dataset.ts), task (src/targets.ts, src/runner.ts) and scorers (src/scorers/, pure functions, no API calls).

npm run eval -- --split dev --repeats 3 --target library     # live run (needs GOOGLE_API_KEY)
npm run eval -- --split test --repeats 3                     # final number; run sparingly
npm run eval -- --target api                                 # through the Hono app in-process
npm run eval -- --replay eval/results/<run>.json             # rescore stored outputs, no API calls
npm run eval:compare -- eval/results/a.json eval/results/b.json

Dataset. 18 cases from Wikipedia intro sections (tags cover short/long, technical/general, plus harder list-heavy, formula-heavy and cross-reference-heavy texts): 12 dev cases to iterate on and 6 test cases reserved for the final number. Each case lists about 5-6 must-have concepts (with aliases), about 3 must-have edges, and a plausible node-count range. Expectations were written from the article text before any model output was seen, and they are deliberately minimal: they check that the important things appear, not that the graph equals one person's graph. Cases are validated with Zod at load time, including that every edge endpoint is a listed concept, so a typo fails loudly.

Pass definition. A trial passes if every gate scorer passes:

Scorer Gate Passes when
extracted yes output exists and matches the schema
structure yes checkGraph reports no error-severity issue (excluding grounding)
grounding yes at least 80% of nodes have a verbatim sourceQuote
conceptRecall yes at least 80% of expected concepts are found
edgeRecall no tracked; also counts reversed and wrong-relation edges
sizeInRange, latency no tracked

The 0.8 thresholds are starting points, not tuned values. Matching is deterministic: normalized label equals the concept name or an alias (exact matches are claimed first), or contains it on word boundaries. A node can satisfy only one expected concept, and a bare generic label such as "network" does not satisfy "neural network". The matching rules are covered by unit tests in test/evalScorers.test.ts.

Repeats. Each case runs several times (default 3) because output varies even at temperature 0. Reports give the trial pass rate next to consistency (cases that passed every repeat).

Run records. Every run writes eval/results/<runId>.json (config, git commit, dataset hash, prompt version, every raw output and score) and a Markdown report in eval/reports/. Model responses are cached in eval/.cache/ keyed by input, trial, config and PROMPT_VERSION; bump PROMPT_VERSION in src/extraction/schemaLoomExtractor.ts whenever the prompt changes. Only eval/results/baseline*.json is committed.

Results. No baseline has been recorded yet: the harness was built without API access. Numbers will be added here from committed baseline*.json runs; each is on at most 12 dev or 6 test cases, so one case moves the pass rate by 8 or 17 points, and small differences are noise.

Known limitations. The dataset is small and written by one person. Very short articles (relational-database, 4 concepts) are strict under the 80% recall gate. retries is recorded in the config but not used until the extractor has a retry loop (attempts is always 1 until then). There is no LLM-as-judge by design; one would need to be validated against human labels first.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

License

MIT License - see LICENSE file for details.

Support

For issues and questions, please use the GitHub issue tracker or refer to the documentation.

About

The main TypeScript API service that transforms unstructured text into schema-validated structured data. Exposes a minimal REST interface for LLM-powered extraction with Zod validation, packaged as a single Docker image for deploy-anywhere simplicity.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages