npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

markdown-ai-creator

v0.3.1

Published

AI-driven original content creation from one or more Markdown sources. Designed for higher-tier users who want fresh writing instead of paraphrasing.

Readme

markdown-ai-creator

AI-driven original content creation from one or more Markdown sources. Designed for higher-tier users who want fresh writing instead of paraphrasing.

markdown-ai-creatormarkdown-ai-rewriter 的「上一档」—— 不做改写,直接基于素材重新创作一篇有判断、有立场、能溯源的博客草稿。

何时用 creator vs rewriter

| 场景 | 推荐包 | |---|---| | 想保留原文结构,只改表达(去 AI 味、去版权痕迹) | markdown-ai-rewriter | | 想从 N 份素材里合成一篇新文章,有自己的角度和判断 | markdown-ai-creator |

定位上,creator 适合 pro / ultra / admin 等付费档;rewriter 留给免费 / 入门档。

安装

npm install markdown-ai-creator

要求 Node ≥ 18(用了 native fetch 和 ESM)。

最小调用

import { generateOriginalDraft } from 'markdown-ai-creator';

const result = await generateOriginalDraft({
  sources: [
    {
      url: 'https://example.com/article',
      fullMarkdown: '# 原文标题\n\n原文正文...'
    }
  ],
  provider: 'deepseek',
  apiKey: process.env.DEEPSEEK_API_KEY!
});

console.log(result.contentMarkdown);
console.log(`tokens=${result.totalTokens} images=${result.imagesRestored}/${result.imagesDetected}`);

完整参数

interface CreatorInput {
  // 必填
  sources: SourceMaterial[];                // 至少 1 个,每个有 fullMarkdown
  provider: 'minimax' | 'deepseek' | 'openai' | 'anthropic' | 'gemini';
  apiKey: string;

  // 可选
  title?: string;                           // 不传则让 LLM 自行拟题
  /**
   * v0.3.0 新增。默认 false(title 当 brief,LLM 可润色)。
   * true 时强制 LLM 用原 title,并在 sanitize 后做一次保险替换 H1。
   * 必须搭配非空 title,否则抛 INVALID_INPUT。
   */
  lockTitle?: boolean;
  angle?: WritingAngle;                     // 6 选 1,默认 'custom'
  persona?: PersonaInput | null;            // null = 中立模式
  customInstruction?: string;               // 本次额外要求
  targetLength?: { min: number; max: number }; // 不传按素材总长动态推断
  model?: string;                           // 不传走 provider 默认值
  temperature?: number;                     // 默认 0.7
  maxTokens?: number;                       // 默认 4000
  preserveImages?: boolean;                 // 默认 true
  baseUrl?: string;                         // OpenAI 兼容自定义网关
  verbose?: boolean;
  imagePolicy?: ImagePolicy;                // v0.2.0+ 图片策略,详见下方
  /**
   * v0.3.0 新增。'auto'(默认) | 'always' | 'never':
   *  - auto:LLM 一份 source URL 都没引用时,文末自动追加 "原文:[…](url)" 列表
   *  - always:永远追加
   *  - never:完全关闭
   */
  appendSourceFooter?: 'auto' | 'always' | 'never';
}

v0.3.0 关键能力

lockTitle —— 锁死最终标题

热搜稿、SEO 稿、命题作文场景,标题不能被 LLM 改:

const result = await generateOriginalDraft({
  sources: [...],
  provider: 'minimax',
  apiKey,
  title: '小米 SU7 Ultra 明天发布,售价 81.49 万',  // 必填
  lockTitle: true                                   // 锁死
});
// 第一行 H1 一定是 '# 小米 SU7 Ultra 明天发布,售价 81.49 万'
// 即使 LLM 加了副标题 / 改了写法,包内会强制改回去
// 改写时会冒一条 warning:'lockTitle: H1 mismatch was rewritten to "..."'

实现是双保险:

  1. prompt 改成"必须完全照抄"硬约束
  2. sanitize 后扫第一行 H1,不一致就强制改写(标点差异容忍)

appendSourceFooter —— 防止 LLM 漏链接

LLM 偶尔会"忘记"在文中引用 source URL。auto 模式(默认)会扫一遍最终 markdown, 所有 source URL 都没出现时,在文末自动追加:

---

原文:
- [素材的 briefingTitle](https://...)
- [...](...)

并冒一条 warning 给调用方知道。always / never 让你完全控制。

Source truncation warnings

当某份 source 的 fullMarkdown 超过 12000 字(prompt 内置阈值)会被截断。 v0.3.0 起会把这条信息冒到 result.warnings

source[0] (https://example.com/long): truncated from 25430 to 12000 chars

调用方可以据此提示用户"素材太长,可能丢失尾部信息"。

Provider 默认模型

| Provider | Model | Base URL | 备注 | |---|---|---|---| | minimax | MiniMax-M2.7-highspeed | https://api.minimaxi.com/v1 | M2.7 系列的低延迟变体(同写作档位、首 token 更快,CLI 阻塞调用最划算);要极限质量传 model: 'MiniMax-M2.7',要压成本传 'abab6.5s-chat' | | deepseek | deepseek-chat | https://api.deepseek.com/v1 | 性价比首选 | | openai | gpt-4o-mini | https://api.openai.com/v1 | 平衡成本 + 中文质量 | | anthropic | claude-3-5-sonnet-20241022 | (Anthropic 原生 API) | 长文写作最强 | | gemini | gemini-1.5-flash | OpenAI-compatible 网关 | 免费额度大 |

minimax 走 MiniMax 官方的 OpenAI-compatible endpoint,和 markdown-ai-rewriter 共用同一个 MINIMAX_API_KEY——服务端只需配置一次。Creator 引擎的目标是"原创合成"而不是"逐段改写",所以默认选了 MiniMax-M2.7-highspeed(M2.7 系列的低延迟变体)。如果你的 minimax 账户没有 M-2 系列权限或想压成本,传 model: 'abab6.5s-chat'(老 abab 系列);想极限质量传 model: 'MiniMax-M2.7'。海外节点请通过 baseUrl 覆盖到 https://api.minimax.io/v1

任意 OpenAI 兼容的网关(自建中转 / openrouter 等)传 provider: 'openai' + baseUrl 即可。

写作角度(angle)

| Angle | 说明 | |---|---| | fact_judgment | 先讲事实,再给"我的判断" | | comparison | 横向对比多个 source | | timeline | 用时间线 / 事件链重组信息 | | translate | 把英文 / 论文翻译并给中文工程师听 | | hands_on | "我装了一遍 / 跑了一遍"的口吻 | | custom | 兜底 —— 综合素材写完整起承转合(默认) |

Persona(人设)

不传 / 传 null → 走中立模式:克制、有判断、面向中文工程师/产品读者。

PersonaInput 时支持注入:

  • name / positioning / styleDescription
  • mustHaveElements(每篇必带要素)
  • bannedWords(禁用词)
  • preferredKeywords(自然处可提到的关键词)
  • openingSignatureMarkdown / signatureMarkdown(文首/文末签名 —— LLM 偶尔会忘,包内会兜底拼一次)

长度推断(targetLength)

不传时按素材总字符数(去除空白)动态推断:

| 素材总字符 | 输出字数区间 | |---|---| | <800 | 300–600 | | 800–3000 | 600–1200 | | 3000–8000 | 1000–1800 | | >8000 | 1500–2500 |

并且输出永远 ≤ 素材的 60%,强制 LLM「提炼」而不是「注水」。

图片占位符保护

包内默认 preserveImages: true,会:

  1. 把素材里每张 ![alt](url) 替换成 {{IMAGE_ANCHOR_001}}{{IMAGE_ANCHOR_002}}...
  2. 在 prompt 里硬约束 LLM「占位符必须原样保留」
  3. 拿回结果后还原占位符为原始图片语法
  4. 如果 LLM 漏了占位符,启用 4 级修复策略(关键词 → 位置比例 → 相邻锚 → 文末兜底)

返回值的 imagesDetected / imagesRestored / warnings 反映保护效果。如果你不想保护图片(比如想让 LLM 自己决定要不要带图),传 preserveImages: false

图片处理策略(v0.2.0+)

把图片"原样保留"之外,本包还可以为每张图执行三种动作之一:

| Strategy | 行为 | 何时用 | |---|---|---| | preserve | 原样保留 | 默认;图片来源可信、不会过期 | | caption-only | 替换成 *图:{alt}* 注脚 | 图片 404 / 反爬挡 / 被识别为水印图、文字截图 | | regenerate | 调 image API 重新生成(text-to-image) | 想替换成无版权风险、风格统一的原创图 |

import { generateOriginalDraft } from 'markdown-ai-creator';

const result = await generateOriginalDraft({
  sources: [...],
  provider: 'minimax',
  apiKey: process.env.MINIMAX_API_KEY!,
  imagePolicy: {
    // 默认对所有图采取的动作
    default: 'preserve',
    // 给特定 url 的精确指示,优先级高于 default
    perImage: {
      'https://watermarked.example.com/img1.jpg': 'caption-only',
      'https://watermarked.example.com/img2.jpg': 'regenerate'
    },
    // regenerate / generateCover 必填
    provider: 'minimax',
    apiKey: process.env.MINIMAX_API_KEY!,
    imageModel: 'image-01',           // 默认即此值,可省
    bodyAspectRatio: '16:9',
    // 单独再生成一张封面图
    generateCover: true,
    coverAspectRatio: '16:9',
    coverStyle: '现代简洁、信息密度低、视觉冲击力强'
  }
});

console.log(result.contentMarkdown);
console.log(result.coverImageUrl);    // 24h 过期的临时 URL(minimax)
console.log(result.imageActions);     // 每张图的处理审计

关于决策来源:本包不做 OCR / 水印检测 / 视觉判断 —— 那些会让包从 26KB 撑到几 MB,且需要原生依赖。语义判别留给上层(比如 web-publisher 的 api 服务里有独立的 image-classifier),把分类结果通过 imagePolicy.perImage 喂进来即可。

关于 URL 寿命:MiniMax image-01 返回的 URL 24h 过期,OpenAI DALL-E 3 返回的 URL 1h 过期。caller 务必下载 + 转存到稳定图床(比如微信公众号素材库),不要直接把这个 URL 长期保存到 DB。

关于 regenerate 失败的兜底:默认 fallbackOnRegenerateFail: true —— 单图 regenerate 失败会自动退化为 caption-only,不阻断整篇文章生成。把这个值设成 false 才会保留原图 + 写一条 warning。

关于支持的 image provider

| Provider | Endpoint | 默认 model | aspect_ratio | |---|---|---|---| | minimax | https://api.minimaxi.com/v1/image_generation | image-01 | 1:1 / 16:9 / 4:3 / 3:2 / 2:3 / 3:4 / 9:16 / 21:9 原生支持 | | openai | https://api.openai.com/v1/images/generations | dall-e-3 | 内部映射到 1024×1024 / 1792×1024 / 1024×1792 |

错误处理

所有错误都是 CreatorError,按 code 路由:

import { CreatorError } from 'markdown-ai-creator';

try {
  await generateOriginalDraft(input);
} catch (err) {
  if (err instanceof CreatorError) {
    switch (err.code) {
      case 'INVALID_INPUT':         /* → 400 */ break;
      case 'MISSING_API_KEY':       /* → 401 / 配置错 */ break;
      case 'LLM_FAILED':            /* → 502 */ break;
      case 'LLM_EMPTY_RESPONSE':    /* → 502 */ break;
      case 'IMAGE_PROTECTION_FAILED': /* → 500 */ break;
    }
  }
}

不做内置 retry —— 重试责任在 caller(前端「重新生成」按钮 / pipeline 重试逻辑)。

开发

npm install
npm run typecheck
npm test          # node --test,纯函数单测,不调真 LLM
npm run build     # 输出到 dist/

设计取舍

为什么不直接用 markdown-ai-rewriter 的 full mode?因为 rewriter 的 system prompt 是写死的「改写不改本意」,对原创合成场景反而是约束。creator 把 system prompt 调成「你是博主,请基于素材写一篇」,温度更高,鼓励有判断有立场的输出。

~~为什么不引 minimax?~~ v0.1.1 加了 minimax 支持。MiniMax 官方提供 OpenAI-compatible chat completions endpoint,可以直接走现有 OpenAI-compatible 调用路径,不增加额外的依赖;同时让 markdown-ai-rewritermarkdown-ai-creator 复用同一份 MINIMAX_API_KEY

为什么 v0.1 不做流式?一篇博客几秒生成完,前端 toast loading 即可;流式有 token 拼接 + 错误恢复成本,留到后续 PR 看是否真有需求。

License

MIT © Ping Si