@begryp/blog-automation-core
v0.2.9
Published
Core orchestrator, Zod contracts, and hexagonal ports for the Begryp blog automation engine.
Readme
@begryp/blog-automation-core
Framework-agnostic domain engine for autonomous blog generation, quality gate evaluation, deduplication, and stage checkpointing.
Features
- Zod Domain Contracts: Pure schema definitions for Tenants, Topics, Drafts, QA Reports, and Runs.
- LLM Provider Adapters:
OpenAIClient,AnthropicClient,GoogleGeminiClient, andOpenAICompatibleClientimplementing theLLMClientport with transient-error retry (max 3, exponential backoff), Zod-validated structured output, and invalid-output retry with the validation error appended to the prompt. API keys are never logged or included in error messages. LLMDrafter: Drafts and revises posts via versioned prompts from@begryp/blog-automation-prompts, validated againstDraftPostSchema.TopicSourceImplementations:LLMTopicSource(proposes topics via LLM, hydrates fullTopicobjects) andStaticTopicSource(host-provided list forprovided/importmodes).createEngineFromSettings: One-call wiring factory that selects the provider client, builds the drafter/topic source, and returns a configuredAutomationEnginefrom a tenant profile and host adapters.- Pure QA Rule Registry: Deterministic SEO rules (meta limits, banned phrases, alt text, internal links), GEO rules (summary, citations, direct answers), and Risk rules (YMYL medical claims).
- Topic Deduplication: Jaccard similarity engine with strict tenant isolation (rejection threshold) and cross-tenant duplicate flagging.
- Stage-Checkpointing Orchestrator: Idempotent state machine with pause/resume, automatic revision loop (max 1), and YMYL safety enforcement.
- Safety Invariants: Enforced in code and schema (1 post/day ceiling, YMYL review mandate, keyRef security).
Usage
Quick wiring (recommended)
import { createEngineFromSettings } from '@begryp/blog-automation-core';
const engine = createEngineFromSettings({
tenant, // TenantProfile
apiKey: await keyVault.resolveKey(tenant.llm.keyRef),
publisher: payloadPublisher, // Publisher port adapter
runStore: payloadRunStore, // RunStore port adapter
keyVault: payloadKeyVault // KeyVault port adapter
});
const run = await engine.runPipeline(tenant);Manual wiring
import {
AnthropicClient,
LLMDrafter,
LLMTopicSource,
DefaultClock,
DefaultIdGenerator,
DefaultQAGate,
AutomationEngine
} from '@begryp/blog-automation-core';
const idGen = new DefaultIdGenerator();
const llm = new AnthropicClient({ apiKey, model: 'claude-sonnet-4-20250514' });
const engine = new AutomationEngine({
topicSource: new LLMTopicSource({ llm, idGen }),
drafter: new LLMDrafter({ llm }),
qaGate: new DefaultQAGate(),
publisher,
runStore,
keyVault,
clock: new DefaultClock(),
idGen
});