Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Bug report
description: Report a problem with ContentForge
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: Thanks for taking the time to file a bug report.
- type: textarea
id: what-happened
attributes:
label: What happened?
description: A clear description of the bug, including the full error/traceback if any.
placeholder: When I run `contentforge generate ...`, it fails with ...
validations:
required: true
- type: textarea
id: repro
attributes:
label: Steps to reproduce
description: Exact commands or code that trigger the bug.
placeholder: |
1. Set provider to ...
2. Run `contentforge generate "topic"`
3. See error
validations:
required: true
- type: input
id: provider
attributes:
label: Provider and model
description: Which LLM backend and model were you using?
placeholder: openai / gpt-4o-mini
validations:
required: true
- type: input
id: version
attributes:
label: ContentForge version
description: Output of `pip show mimo-contentforge` (Version field) or commit SHA.
placeholder: 1.0.0
validations:
required: true
- type: input
id: python
attributes:
label: Python version
placeholder: "3.11"
validations:
required: true
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Question / discussion
url: https://github.com/aimanmalib/contentforge/discussions
about: Ask questions and discuss ideas before opening an issue.
40 changes: 40 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Feature request
description: Suggest an idea or improvement for ContentForge
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: Problem / motivation
description: What are you trying to do that ContentForge doesn't support today?
placeholder: I want to use ContentForge with <provider>, but ...
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: Describe what you'd like to happen.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Any workarounds or other approaches you've thought about.
validations:
required: false
- type: dropdown
id: area
attributes:
label: Area
options:
- New LLM provider preset
- New agent / agent improvement
- New publish target / export format
- CLI / config
- Documentation
- Other
validations:
required: true
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Lint with ruff
run: ruff check src/ tests/

- name: Check formatting with ruff
run: ruff format --check src/ tests/

- name: Run tests
run: pytest --cov=contentforge --cov-report=term-missing
75 changes: 75 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Contributing to ContentForge

Thanks for your interest in improving ContentForge. This project is a
provider-agnostic, 8-agent content pipeline that runs on any OpenAI-compatible
LLM endpoint. Contributions of all sizes are welcome — docs fixes, new provider
presets, agent improvements, and bug reports all help.

## Getting started

```bash
git clone https://github.com/aimanmalib/contentforge.git
cd contentforge
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest # 112 tests should pass
```

## Development workflow

1. Fork the repo and create a feature branch: `git checkout -b feat/my-change`
2. Make your change with tests.
3. Run the full local gate before pushing:
```bash
ruff check src/ tests/ # lint
ruff format src/ tests/ # format
pytest --cov=contentforge # tests + coverage
```
4. Commit using [Conventional Commits](https://www.conventionalcommits.org/)
(`feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `ci:`).
5. Open a pull request against `main`. CI runs lint + format + tests on
Python 3.10/3.11/3.12 — keep it green.

## Adding a new LLM provider

ContentForge talks to any OpenAI-compatible `/chat/completions` endpoint, so
most providers need only a preset entry. In `src/contentforge/core/config.py`,
add to `PROVIDER_PRESETS`:

```python
"myprovider": {
"base_url": "https://api.myprovider.com/v1",
"auth_style": "bearer", # "bearer" or "api-key"
"model": "default-model-name",
"env_key": "MYPROVIDER_API_KEY",
"env_base": "MYPROVIDER_BASE_URL",
},
```

Then add a test in `tests/unit/test_llm_config.py` mirroring the existing
provider cases. No client changes are needed unless the provider deviates from
the OpenAI protocol.

## Good first issues

- Add a provider preset (Together, Groq, DeepSeek, Mistral, ...)
- Add a new publish target in `utils/export.py`
- Improve an agent's system prompt
- Expand test coverage for streaming paths

## Code style

- Python 3.10+ with type hints
- `ruff` for linting and formatting (config in `pyproject.toml`)
- Keep functions focused; prefer small, testable units
- Public APIs get docstrings

## Reporting bugs / requesting features

Use the issue templates (bug report / feature request). Include repro steps,
your provider/model, and the ContentForge version for bugs.

## License

By contributing, you agree your contributions are licensed under the MIT
License, the same as the project.
78 changes: 43 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
# MiMo ContentForge
# ContentForge

> **8-Agent AI Content Pipeline powered by Xiaomi MiMo V2.5 Pro**
> **8-Agent AI Content Pipeline for any OpenAI-compatible LLM**
>
> From topic to published article in minutes — research, write, optimize, translate, and publish with 8 specialized AI agents orchestrated through a single pipeline.

[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Tests: 96](https://img.shields.io/badge/tests-96-brightgreen.svg)](tests/)
[![LOC: 2,800+](https://img.shields.io/badge/LOC-2%2C800%2B-orange.svg)](src/)
[![Tests: 112](https://img.shields.io/badge/tests-112-brightgreen.svg)](tests/)
[![LOC: 2,900+](https://img.shields.io/badge/LOC-2%2C900%2B-orange.svg)](src/)

Works with **OpenAI, OpenRouter, Ollama, llama.cpp, Xiaomi MiMo**, or any endpoint that speaks the OpenAI `/chat/completions` protocol. Pick a provider with one config line — no code changes.

---

## Architecture

```
┌─────────────────────────────────────────────────────────────────┐
MiMo ContentForge Pipeline │
Powered by Xiaomi MiMo V2.5 Pro
│ ContentForge Pipeline
Any OpenAI-compatible LLM backend
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
Expand All @@ -36,9 +38,9 @@
│ │ Per-agent consumption · Cache hit rate · Latency │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
API: token-plan-sgp.xiaomimimo.com/v1/chat/completions │
│ Auth: api-key header (Token Plan format)
Model: mimo-v2.5-pro · Streaming SSE · reasoning_content │
Protocol: OpenAI-compatible /chat/completions
│ Auth: bearer token or api-key header (per provider)
│ Streaming SSE · optional reasoning_content support
└─────────────────────────────────────────────────────────────────┘
```

Expand All @@ -57,26 +59,29 @@

**Total per pipeline run: ~9,400 tokens** (single language)

## Why MiMo V2.5 Pro?
## Supported Providers

ContentForge talks to any OpenAI-compatible `/chat/completions` endpoint. Built-in presets:

We specifically chose MiMo over Claude/GPT for this pipeline because:
| Provider | `provider=` | Default model | Auth | Env vars |
|----------|-------------|---------------|------|----------|
| OpenAI | `openai` | `gpt-4o-mini` | Bearer | `OPENAI_API_KEY`, `OPENAI_BASE_URL` |
| OpenRouter | `openrouter` | `openai/gpt-4o-mini` | Bearer | `OPENROUTER_API_KEY` |
| Ollama (local) | `ollama` | `llama3.1` | Bearer | `OLLAMA_BASE_URL` |
| Xiaomi MiMo | `mimo` | `mimo-v2.5-pro` | api-key | `MIMO_API_KEY` |

1. **Long-chain reasoning** — The Quality Agent's 8-dimension scoring benefits from MiMo's `reasoning_content` field, which shows the model's step-by-step evaluation process
2. **Streaming SSE quality** — Real-time token-by-token output for the Writer Agent allows live preview without buffering delays
3. **Chinese/Malay proficiency** — The Translator Agent produces natural zh/ms/id output without the awkward phrasing common in Western models
4. **Cost efficiency** — Token Plan pricing at `token-plan-sgp.xiaomimimo.com` makes high-volume content production viable (~$0.20/M cache hit)
5. **Structured output** — MiMo reliably produces valid JSON for Research, Outline, SEO, and Quality agents without schema enforcement
Point `base_url` at any other compatible endpoint (llama.cpp, vLLM, LM Studio, a local proxy) and it just works. The pipeline benefits from models that expose a `reasoning_content` field (used by the Quality Agent's 8-dimension scoring) and strong multilingual output (used by the Translator Agent), but neither is required.

## Quick Start

```bash
# Install
pip install -e ".[dev]"

# Set API key
export MIMO_API_KEY="your-token-plan-key"
# Pick any provider — set its API key (OpenAI shown here)
export OPENAI_API_KEY="sk-..."

# Generate content
# Generate content (uses the default provider unless overridden in config)
contentforge generate "AI in Healthcare" --words 2000 --output ./output

# With translation
Expand Down Expand Up @@ -153,10 +158,12 @@ Daily estimate: **50-100 pipeline runs** = ~500K–1M tokens/day

```yaml
# contentforge.yaml
mimo:
api_key: ${MIMO_API_KEY}
base_url: https://token-plan-sgp.xiaomimimo.com/v1
model: mimo-v2.5-pro
llm:
provider: openai # openai | openrouter | ollama | mimo
api_key: ${OPENAI_API_KEY}
# base_url and model default from the provider preset; override if needed
# base_url: https://api.openai.com/v1
# model: gpt-4o-mini
max_tokens: 4096
temperature: 0.7
max_retries: 3
Expand Down Expand Up @@ -198,19 +205,20 @@ pytest -m integration
pytest -v
```

**96 tests** covering:
- Configuration management (16 tests)
**112 tests** covering:
- Configuration management (17 tests)
- Multi-backend LLM config: presets, auth styles, env resolution (16 tests)
- Token tracking & reporting (14 tests)
- Text utilities (12 tests)
- Export utilities (4 tests)
- Agent base class & all 8 agents (34 tests)
- Pipeline orchestration (8 tests)
- Error handling & edge cases (8 tests)
- Error handling & edge cases (7 tests)

## Project Structure

```
mimo-contentforge/
contentforge/
├── src/contentforge/
│ ├── __init__.py
│ ├── cli.py # Click CLI with Rich output
Expand All @@ -227,8 +235,9 @@ mimo-contentforge/
│ │ └── publisher.py # Agent 8: Publisher
│ ├── core/
│ │ ├── __init__.py
│ │ ├── config.py # Pydantic config management
│ │ ├── mimo_client.py # MiMo API client (SSE streaming)
│ │ ├── config.py # Pydantic config (multi-provider presets)
│ │ ├── llm_client.py # OpenAI-compatible client (SSE streaming)
│ │ ├── mimo_client.py # Backward-compat shim → llm_client
│ │ └── token_tracker.py # Per-agent token metrics
│ ├── pipeline/
│ │ ├── __init__.py
Expand Down Expand Up @@ -260,12 +269,12 @@ mimo-contentforge/

## API Reference

### MiMoClient
### LLMClient

```python
from contentforge.core.mimo_client import MiMoClient, ChatMessage
from contentforge.core.llm_client import LLMClient, ChatMessage

async with MiMoClient(config) as client:
async with LLMClient(config) as client:
# Non-streaming
response = await client.chat([
ChatMessage(role="system", content="You are helpful."),
Expand All @@ -279,7 +288,7 @@ async with MiMoClient(config) as client:
print(chunk.delta, end="", flush=True)
```

**Important**: MiMo Token Plan uses `api-key` header, NOT `Authorization: Bearer`.
**Auth styles**: `provider="openai"` (and openrouter/ollama) use `Authorization: Bearer`; `provider="mimo"` uses the `api-key` header. The right style is selected automatically from the provider preset. `MiMoClient` remains importable as a backward-compatible alias of `LLMClient`.

### TokenTracker

Expand All @@ -301,5 +310,4 @@ MIT License — see [LICENSE](LICENSE) for details.

---

**Built with Xiaomi MiMo V2.5 Pro** via Token Plan API
`token-plan-sgp.xiaomimimo.com/v1`
**Provider-agnostic** — works with OpenAI, OpenRouter, Ollama, llama.cpp, Xiaomi MiMo, or any OpenAI-compatible `/chat/completions` endpoint.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ build-backend = "setuptools.build_meta"
[project]
name = "mimo-contentforge"
version = "1.0.0"
description = "8-Agent AI Content Pipeline powered by Xiaomi MiMo V2.5 Pro"
description = "8-Agent AI Content Pipeline for any OpenAI-compatible LLM (OpenAI, OpenRouter, Ollama, MiMo)"
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.10"
authors = [
{name = "ContentForge Team"}
]
keywords = ["ai", "content", "agents", "mimo", "pipeline", "automation"]
keywords = ["ai", "content", "agents", "llm", "openai", "pipeline", "automation"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
Expand Down
Loading
Loading