lekana-gemini
v1.0.0
Published
A shared TypeScript library for Lekana microservices that provides AI-powered document processing, validation, and workflow automation using Google's Gemini API
Maintainers
Readme
hw# @lekana/gemini
A shared TypeScript library for Lekana microservices that provides AI-powered document processing, validation, and workflow automation using Google's Gemini API.
Features
✅ Clean API – Simple, typed functions for extraction, validation, and workflows
✅ Secure – No API keys stored; inject at runtime from environment variables
✅ Reusable Prompts – Centralized prompt templates shared across services
✅ TypeScript Native – Full type definitions and strict mode enabled
✅ Production Ready – Error handling, JSON sanitization, and CommonJS output
Table of Contents
Installation
npm install @lekana/geminiRequirements:
- Node.js 20+
- Google Gemini API key (get one here)
Setup environment variable:
export GEMINI_API_KEY="your-api-key-here"Or in your .env file:
GEMINI_API_KEY=your-api-key-hereQuick Start
import { extractFields } from "@lekana/gemini";
async function processInvoice() {
const result = await extractFields(
{ documentText: "Invoice #12345\nTotal: $1,500.00\nDate: Dec 10, 2025" },
process.env.GEMINI_API_KEY!
);
console.log(result.fields);
// Output: { invoiceNumber: "12345", total: 1500, date: "2025-12-10" }
}
processInvoice();API Reference
1. GeminiClient
Low-level wrapper for direct Gemini API access. Use this when you need custom prompts beyond the helper functions.
Constructor
import { GeminiClient } from "@lekana/gemini";
const client = new GeminiClient({
apiKey: process.env.GEMINI_API_KEY!,
model: "gemini-1.5-pro", // optional, defaults to "gemini-1.5-pro"
});Options:
apiKey(required): Your Gemini API keymodel(optional): Model name (default:"gemini-1.5-pro")
Methods
generateText(prompt: string): Promise<string>
Sends a prompt to Gemini and returns the AI-generated response.
const response = await client.generateText(
"Summarize the following invoice in one sentence: Invoice #789, Total $2,300"
);
console.log(response);
// "Invoice #789 has a total amount of $2,300."Throws:
- If API key is missing
- If prompt is empty
- If Gemini API request fails
2. extractFields
Extracts structured data from unstructured document text using AI. Returns parsed JSON.
Function Signature
extractFields(
input: { documentText: string },
apiKey: string
): Promise<{ fields: any }>Example
import { extractFields } from "@lekana/gemini";
const result = await extractFields(
{
documentText: `
INVOICE
Invoice Number: INV-2025-001
Customer: Acme Corp
Total Amount: $5,000.00
Due Date: January 15, 2026
Items:
- Widget A (qty: 10) - $3,000
- Service B - $2,000
`,
},
process.env.GEMINI_API_KEY!
);
console.log(result.fields);Output:
{
"invoiceNumber": "INV-2025-001",
"customer": "Acme Corp",
"totalAmount": 5000,
"dueDate": "2026-01-15",
"items": [
{ "name": "Widget A", "quantity": 10, "price": 3000 },
{ "name": "Service B", "price": 2000 }
]
}Uses Prompt: src/prompts/extract.txt
Throws:
- If API key is missing
- If
documentTextis empty - If JSON parsing fails
3. validateDocument
Validates a JSON object and returns validation status with error messages.
Function Signature
validateDocument(
input: { json: any },
apiKey: string
): Promise<{ isValid: boolean; errors: string[] }>Example
import { validateDocument } from "@lekana/gemini";
const validation = await validateDocument(
{
json: {
invoiceNumber: "INV-001",
customerEmail: "invalid-email", // Invalid format
totalAmount: -500, // Invalid: negative
items: [], // Invalid: empty array
},
},
process.env.GEMINI_API_KEY!
);
console.log(validation);Output:
{
"isValid": false,
"errors": [
"customerEmail must be a valid email address",
"totalAmount cannot be negative",
"items array cannot be empty"
]
}Valid example:
const validation = await validateDocument(
{
json: {
invoiceNumber: "INV-001",
customerEmail: "[email protected]",
totalAmount: 500,
},
},
process.env.GEMINI_API_KEY!
);
console.log(validation);
// { isValid: true, errors: [] }Uses Prompt: src/prompts/validate.txt
Throws:
- If API key is missing
- If
jsonis undefined - If JSON parsing fails
4. runWorkflowStep
Executes a generic workflow step with custom logic defined by the step metadata.
Function Signature
runWorkflowStep(
step: { id: string; prompt?: string },
data: Record<string, unknown>,
apiKey: string
): Promise<{ stepId: string; output: any }>Example
import { runWorkflowStep } from "@lekana/gemini";
const result = await runWorkflowStep(
{
id: "calculate-tax",
prompt: "Calculate 10% tax on the total amount and return the result",
},
{
invoiceNumber: "INV-001",
totalAmount: 1000,
items: ["Widget A", "Widget B"],
},
process.env.GEMINI_API_KEY!
);
console.log(result);Output:
{
"stepId": "calculate-tax",
"output": {
"totalAmount": 1000,
"taxRate": 0.1,
"taxAmount": 100,
"totalWithTax": 1100
}
}Uses Prompt: src/prompts/workflow.txt
Throws:
- If API key is missing
- If step doesn't have an
id - If JSON parsing fails
Complete Examples
Example 1: Invoice Processing Pipeline
import {
extractFields,
validateDocument,
runWorkflowStep,
} from "@lekana/gemini";
async function processInvoice(rawText: string) {
const apiKey = process.env.GEMINI_API_KEY!;
try {
// Step 1: Extract structured data
console.log("📄 Extracting fields...");
const extraction = await extractFields({ documentText: rawText }, apiKey);
console.log("Extracted:", extraction.fields);
// Step 2: Validate extracted data
console.log("✅ Validating data...");
const validation = await validateDocument(
{ json: extraction.fields },
apiKey
);
if (!validation.isValid) {
console.error("❌ Validation failed:", validation.errors);
throw new Error(`Invalid data: ${validation.errors.join(", ")}`);
}
console.log("✅ Data is valid");
// Step 3: Calculate totals with tax
console.log("🧮 Calculating tax...");
const taxCalculation = await runWorkflowStep(
{
id: "calculate-tax",
prompt: "Calculate 15% tax and grand total",
},
extraction.fields,
apiKey
);
console.log("Tax calculation:", taxCalculation.output);
// Step 4: Generate summary
console.log("📝 Generating summary...");
const summary = await runWorkflowStep(
{
id: "generate-summary",
prompt: "Create a one-sentence summary of this invoice",
},
{ ...extraction.fields, ...taxCalculation.output },
apiKey
);
return {
extractedData: extraction.fields,
taxDetails: taxCalculation.output,
summary: summary.output,
};
} catch (error) {
console.error("Error processing invoice:", error);
throw error;
}
}
// Usage
const invoiceText = `
INVOICE #INV-2025-123
Bill To: XYZ Company
Amount: $10,000
Date: December 10, 2025
`;
processInvoice(invoiceText)
.then((result) => console.log("✅ Processing complete:", result))
.catch((err) => console.error("❌ Failed:", err.message));Example 2: Express.js Microservice
import express from "express";
import { extractFields, validateDocument } from "@lekana/gemini";
const app = express();
app.use(express.json());
// Extract endpoint
app.post("/api/extract", async (req, res) => {
try {
const { documentText } = req.body;
if (!documentText) {
return res.status(400).json({ error: "documentText is required" });
}
const result = await extractFields(
{ documentText },
process.env.GEMINI_API_KEY!
);
res.json({ success: true, data: result.fields });
} catch (error) {
console.error("Extraction error:", error);
res.status(500).json({
error: "Extraction failed",
message: error.message,
});
}
});
// Validate endpoint
app.post("/api/validate", async (req, res) => {
try {
const { json } = req.body;
if (!json) {
return res.status(400).json({ error: "json payload is required" });
}
const validation = await validateDocument(
{ json },
process.env.GEMINI_API_KEY!
);
res.json({
success: true,
isValid: validation.isValid,
errors: validation.errors,
});
} catch (error) {
console.error("Validation error:", error);
res.status(500).json({
error: "Validation failed",
message: error.message,
});
}
});
app.listen(3000, () => {
console.log("🚀 Microservice running on http://localhost:3000");
});Usage:
# Extract fields
curl -X POST http://localhost:3000/api/extract \
-H "Content-Type: application/json" \
-d '{"documentText": "Invoice #123, Total: $500"}'
# Validate data
curl -X POST http://localhost:3000/api/validate \
-H "Content-Type: application/json" \
-d '{"json": {"invoiceNumber": "123", "total": 500}}'Example 3: Custom Gemini Client Usage
import { GeminiClient } from "@lekana/gemini";
async function customAITask() {
const client = new GeminiClient({
apiKey: process.env.GEMINI_API_KEY!,
model: "gemini-1.5-pro",
});
// Custom classification
const classification = await client.generateText(`
Classify this document type and return only JSON:
{ "type": "invoice" | "receipt" | "contract" | "other" }
Document: "RECEIPT - Coffee Shop - $4.50"
`);
console.log(classification);
// Custom entity extraction
const entities = await client.generateText(`
Extract all company names from this text as JSON array:
"Acme Corp signed a contract with XYZ Ltd and Beta Industries"
`);
console.log(entities);
}
customAITask();Error Handling
All library functions throw errors for:
- Missing/empty API keys
- Missing required parameters
- Gemini API failures
- JSON parsing failures
Best Practice: Always use try/catch
import { extractFields } from "@lekana/gemini";
async function safeExtraction(text: string) {
try {
const result = await extractFields(
{ documentText: text },
process.env.GEMINI_API_KEY!
);
return { success: true, data: result.fields };
} catch (error) {
console.error("Extraction failed:", error.message);
return {
success: false,
error: error.message,
fallback: {},
};
}
}Common Error Messages:
"Gemini API key is required to initialize the client.""API key is required for extractFields.""extractFields requires non-empty documentText.""Failed to parse JSON from Gemini response: ...""Gemini generateText failed: ..."
TypeScript Types
Full type definitions for better IDE support and type safety:
import type {
// Client options
GeminiClientOptions,
// Extract function types
ExtractInput,
ExtractResult,
// Validate function types
ValidateInput,
ValidateResult,
// Workflow function types
WorkflowStep,
WorkflowData,
WorkflowResult,
} from "@lekana/gemini";
// Example usage
const extractInput: ExtractInput = {
documentText: "Invoice...",
};
const validateInput: ValidateInput = {
json: { amount: 100 },
};
const workflowStep: WorkflowStep = {
id: "step-1",
prompt: "Process this data",
};Development
Project Structure
zybbera-gemini/
├── package.json
├── tsconfig.json
├── README.md
├── src/
│ ├── index.ts # Public API exports
│ ├── types.ts # TypeScript type definitions
│ ├── geminiClient.ts # GeminiClient class
│ ├── extract.ts # extractFields function
│ ├── validate.ts # validateDocument function
│ ├── workflow.ts # runWorkflowStep function
│ └── prompts/
│ ├── extract.txt # Extraction prompt template
│ ├── validate.txt # Validation prompt template
│ └── workflow.txt # Workflow prompt template
└── dist/ # Compiled output (generated)Build from Source
# Install dependencies
npm install
# Compile TypeScript
npm run build
# Output will be in dist/Customizing Prompts
Edit the prompt files in src/prompts/ to customize AI behavior:
extract.txt– Controls how data is extracted from documentsvalidate.txt– Defines validation rules and error messagesworkflow.txt– Sets behavior for workflow step execution
After editing, rebuild:
npm run buildConfiguration
Package Details
- Name:
@lekana/gemini - Version:
1.0.0 - Target: ES2020, CommonJS
- Main:
dist/index.js - Types:
dist/index.d.ts - Access: Restricted (private NPM package)
Dependencies
@google/generative-ai– Official Google Gemini SDK@types/node(dev) – Node.js type definitionstypescript(dev) – TypeScript compiler
Security Notes
⚠️ Never commit API keys to version control
- Always inject
GEMINI_API_KEYfrom environment variables - Use
.envfiles (and add to.gitignore) - For production: use secret management (AWS Secrets Manager, Azure Key Vault, etc.)
✅ This library does not store or log API keys
Support
For issues or questions:
- Check the examples above
- Review error handling patterns
- Verify your
GEMINI_API_KEYis set correctly
License
Private package for Lekana microservices. #� �l�e�k�a�n�a�-�g�e�m�i�n�i� � �#� �l�e�k�a�n�a�-�g�e�m�i�n�i� � �
