Skip to content

feat: curvit-content-creator service — scheduled AI editorial assistant (issue #331)#336

Merged
NickLetts2 merged 5 commits into
mainfrom
copilot/featureai-content-creator
May 30, 2026
Merged

feat: curvit-content-creator service — scheduled AI editorial assistant (issue #331)#336
NickLetts2 merged 5 commits into
mainfrom
copilot/featureai-content-creator

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 30, 2026

Introduces curvit-content-creator, a dedicated internal Docker service that generates draft content on a weekly schedule. The service acts as an editorial assistant — it never auto-publishes, never invents statistics or testimonials, and always writes to CMS as Draft.

Service architecture

  • FastAPI app with /health, /ready, /metrics, and /admin/* endpoints
  • Asyncio scheduler fires every Monday at 01:00 UTC; no external scheduler dependency
  • Anthropic client uses a separate CONTENT_CREATOR_AI_API_KEY (distinct from cv-analysis)
  • Internal-only — no Traefik labels, no host port binding

10-stage editorial workflow

Stage Purpose
1 — Content inventory Reads all CMS content via internal API; treats manual/AI/imported equally
2 — UK recruitment research Digests approved sources (GOV.UK, ONS, CIPD, REC, Acas, LinkedIn, Indeed)
3 — Opportunity scoring Weighted: 60% evergreen SEO + 25% product alignment + 15% news relevance
4 — Brief generation AI produces structured brief before any draft is written
5 — Draft generation Guided by content philosophy (useful-first, no hard sell, no fabrication)
6 — Link intelligence HEAD→GET HTTP validation, authority check, anchor quality, orphan detection
7 — Conversion rules Curvit mention after first 25%, max 2 direct refs, no aggressive sales language
8 — Quality review AI scores SEO / Trust / Readability / Conversion; rejects below threshold
9 — Draft creation POSTs to CMS with status="Draft", ai_generated=True — no publish path exists
10 — Admin support Populates review queue, quality scores, link audits, research digests

Generation modes

NewContent | Refresh | Expansion | Mixed (weekly default: 2 new evergreen + 1 refresh/expansion + 1 news blog)

AI provenance tracking

Every generated item stores PromptVersion, ModelUsed, GeneratedAt, GenerationMode for future performance comparison against analytics signals from issue #332.

New DB entities

GenerationRun · ResearchDigest · ContentOpportunity · ContentBrief · ContentQualityReview · ContentLinkAudit

Admin endpoints

POST /admin/generate          # ad-hoc cycle with mode/count/type/topic params
GET  /admin/runs              # run history
GET  /admin/queue             # drafts awaiting human review
GET  /admin/quality           # quality scores (?rejected_only)
GET  /admin/links             # link audit results (?issues_only)
GET  /admin/research          # research digests
GET  /admin/opportunities     # scored content opportunities
GET  /admin/recommendations   # refresh/expansion recommendations

Other changes

  • docker-compose.ymlcontent-creator service entry (no ingress, depends on postgres + cms-service)
  • .github/workflows/ci-python.ymlservices/content-creator/** path triggers added
  • docs/content-creator.md — architecture, workflow, AI prompts, safety controls, operations, monitoring
  • 78 tests covering unit, routes, workflow, scheduling, and safety invariants

- New Docker service: services/content-creator/
- 10-stage workflow: content inventory, research, opportunity
  analysis, brief generation, draft generation, link audit,
  conversion validation, quality review, CMS draft creation,
  admin support data
- Two operating modes: NewContent and Refresh/Expansion (Mixed default)
- Separate CONTENT_CREATOR_AI_API_KEY from cv-analysis service
- AI provenance tracking: PromptVersion, ModelUsed, GeneratedAt, GenerationMode
- Ad-hoc generation endpoint for admins (POST /admin/generate)
- Weekly Monday 01:00 UTC asyncio scheduler
- Safety: never auto-publishes, no fabrication, draft-only CMS writes
- Conversion rules: Curvit mention after 25%, max 2 refs, no hard sell
- DB models: GenerationRun, ResearchDigest, ContentOpportunity,
  ContentBrief, ContentQualityReview, ContentLinkAudit
- 78 tests: unit, route, workflow, scheduling, safety
- docker-compose.yml: content-creator service entry (no Traefik)
- .github/workflows/ci-python.yml: content-creator path triggers
- docs/content-creator.md: full architecture, workflow, operations docs

Co-authored-by: NickLetts2 <90337962+NickLetts2@users.noreply.github.com>
Comment thread services/content-creator/tests/test_unit.py Fixed
- Fix prompt version constant names (drop redundant -v1 suffix)
- Fix semaphore lazy init to avoid race condition
- Fix link auditor HEAD→GET fallback (check 405 status, not exception)
- Fix SQLAlchemy boolean comparison to use .is_(False)
- Propagate KeyboardInterrupt/SystemExit in workflow exception handler
- Fix CodeQL URL substring check in test (use startswith)

Co-authored-by: NickLetts2 <90337962+NickLetts2@users.noreply.github.com>
Copilot AI changed the title [WIP] Add AI Content Creator service for draft generation feat: curvit-content-creator service — scheduled AI editorial assistant (issue #331) May 30, 2026
Comment thread services/content-creator/tests/test_unit.py Fixed
…ring sanitization'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a new internal content-creator FastAPI service for scheduled AI-assisted draft generation, with CMS integration, link auditing, workflow persistence, admin endpoints, Docker wiring, CI triggers, documentation, and tests.

Changes:

  • Adds the content-creator service with scheduler, workflow engine, Anthropic client, CMS client, SQLAlchemy models, and admin API.
  • Adds Docker Compose integration and Python CI path/matrix coverage for the new service.
  • Adds service documentation and tests for routes, workflow behavior, scheduling, and safety invariants.

Reviewed changes

Copilot reviewed 22 out of 27 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
.github/workflows/ci-python.yml Adds content-creator to Python CI triggers and service matrix.
docker-compose.yml Adds internal content-creator service container.
docs/content-creator.md Documents architecture, workflow, safety controls, operations, and testing.
services/content-creator/Dockerfile Defines build/runtime image for the new service.
services/content-creator/internal_auth.py Re-exports shared internal auth helpers.
services/content-creator/requirements.in Adds direct Python dependencies.
services/content-creator/requirements.txt Adds pinned dependency lock file.
services/content-creator/app/__init__.py Initializes the app package.
services/content-creator/app/config.py Adds service-specific settings and startup validation.
services/content-creator/app/db.py Re-exports shared DB session helpers.
services/content-creator/app/main.py Adds FastAPI app, middleware, metrics, scheduler lifespan, and health endpoints.
services/content-creator/app/models/__init__.py Initializes models package.
services/content-creator/app/models/db_models.py Adds workflow/run/research/opportunity/quality/link audit ORM models.
services/content-creator/app/models/schemas.py Adds API request/response schemas.
services/content-creator/app/routers/__init__.py Initializes routers package.
services/content-creator/app/routers/admin.py Adds admin generation, run history, queue, quality, link, research, opportunity, and recommendation endpoints.
services/content-creator/app/scheduler.py Adds weekly asyncio scheduler.
services/content-creator/app/services/__init__.py Initializes services package.
services/content-creator/app/services/ai_client.py Adds Anthropic-based brief, draft, quality, and refresh candidate functions.
services/content-creator/app/services/cms_client.py Adds CMS inventory and draft creation client.
services/content-creator/app/services/link_auditor.py Adds markdown link extraction and HTTP link auditing.
services/content-creator/app/services/workflow.py Adds end-to-end content generation workflow orchestration.
services/content-creator/tests/__init__.py Initializes tests package.
services/content-creator/tests/conftest.py Adds test DB and HTTP client fixtures.
services/content-creator/tests/test_routes.py Adds route integration tests.
services/content-creator/tests/test_unit.py Adds scheduler, helper, conversion, and link auditor tests.
services/content-creator/tests/test_workflow.py Adds workflow and draft safety tests.

Comment on lines +85 to +98
run_id = str(uuid.uuid4())
parameters = {
"target_count": request.target_count,
"content_type": request.content_type,
"topic_hint": request.topic_hint,
"notes": request.notes,
}

background_tasks.add_task(
_run_adhoc_in_background,
run_id=run_id,
generation_mode=request.generation_mode,
parameters=parameters,
)
Comment on lines +40 to +44
response = await client.get(
f"{settings.cms_service_url}/admin/content",
headers=headers,
params=params,
)
Comment on lines +594 to +600
rejected = composite_quality < _MIN_COMPOSITE_QUALITY
rejection_reason = None
if rejected:
rejection_reason = f"Composite quality score {composite_quality} below threshold {_MIN_COMPOSITE_QUALITY}"
if not conversion_rule_pass:
rejection_reason += "; conversion rules violated"

Comment on lines +291 to +294
if generation_mode in ("Refresh", "Mixed") and existing_summaries:
refresh_candidates = await ai_client.identify_refresh_candidates(existing_summaries)
refresh_count = _DEFAULT_REFRESH_COUNT if generation_mode == "Mixed" else target_count
for candidate in refresh_candidates[:refresh_count]:
Comment on lines +69 to +72
resp = await client.head(url)
# Some servers reject HEAD with 405; fall back to GET
if resp.status_code == 405:
resp = await client.get(url)
Comment on lines +11 to +13
class GenerationRun(Base):
"""Tracks each execution of the content generation workflow (scheduled or ad-hoc)."""
__tablename__ = "ContentCreatorRuns"
Co-authored-by: NickLetts2 <90337962+NickLetts2@users.noreply.github.com>
@NickLetts2 NickLetts2 requested a review from Copilot May 30, 2026 08:30
@NickLetts2 NickLetts2 marked this pull request as ready for review May 30, 2026 08:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 28 changed files in this pull request and generated 6 comments.

Comment on lines +4 to +22
import asyncio
import logging

from fastapi import FastAPI, Response
from prometheus_fastapi_instrumentator import Instrumentator

from app.config import get_settings
from app.routers.admin import router as admin_router
from app.scheduler import run_scheduler
from internal_auth import CorrelationIdMiddleware, InternalApiKeyMiddleware
from shared_api_key_scheme import setup_api_key_scheme

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

_scheduler_task: asyncio.Task | None = None


async def _lifespan(app: FastAPI): # type: ignore[type-arg]
Comment on lines +132 to +134
protected override void Down(MigrationBuilder migrationBuilder)
{
}
Comment on lines +604 to +610
rejected = composite_quality < _MIN_COMPOSITE_QUALITY or not conversion_rule_pass
rejection_reason = None
if rejected:
rejection_reason = f"Composite quality score {composite_quality} below threshold {_MIN_COMPOSITE_QUALITY}"
if not conversion_rule_pass:
rejection_reason += "; conversion rules violated"


# ── Stage 6: Link intelligence ──────────────────────────────────────────
logger.info("[%s] Stage 6: Link audit for '%s'", run_id, title)
links = link_auditor.extract_links_from_markdown(content_markdown)
Comment on lines +69 to +72
async with httpx.AsyncClient(
timeout=timeout,
follow_redirects=True,
) as client:
Comment on lines +106 to +107
except ValueError:
return True
@NickLetts2 NickLetts2 merged commit a70ed3f into main May 30, 2026
30 checks passed
NickLetts2 added a commit that referenced this pull request Jun 1, 2026
Co-authored-by: NickLetts2 <90337962+NickLetts2@users.noreply.github.com>
NickLetts2 added a commit that referenced this pull request Jun 1, 2026
…ator

feat: curvit-content-creator service — scheduled AI editorial assistant (issue #331)
@NickLetts2 NickLetts2 deleted the copilot/featureai-content-creator branch June 2, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Step 3 - AI Content Creator service with research, SEO gap analysis and draft generation

4 participants