feat: add seo_meta service for AI-generated SEO metadata with CLI management and database layer
DevPlace CI / test (push) Failing after 2m13s

Implement a new `SeoMetaService` subservice that generates clean SEO title/description/keywords for published content items, distinct from the existing SEO diagnostics auditor. Add `seo_metadata` polymorphic table with soft-delete support, batch query methods, and usage tracking. Extend the CLI with `seo-meta prune` and `seo-meta clear` commands for job row lifecycle management. Wire `schedule_seo_meta_for_table` into content creation and editing flows in `content.py`. Document the new service in `AGENTS.md` and `README.md`, including the `extra_head` site setting for custom `<head>` injection.
This commit is contained in:
2026-06-19 20:15:22 +00:00
parent 426d3639c6
commit d10f1af118
51 changed files with 2262 additions and 93 deletions
@@ -0,0 +1,84 @@
# retoor <retoor@molodetz.nl>
import json
from tests.conftest import run_async
from devplacepy.services.jobs.deepsearch import orchestrate as orchestrate_module
from devplacepy.services.jobs.deepsearch.crawl import CrawledPage
from devplacepy.services.jobs.deepsearch.orchestrate import Orchestration, orchestrate, source_diversity
def _page(url, text="content " * 30):
return CrawledPage(url=url, title="T", text=text, source="httpx", status=200)
def test_source_diversity_empty_is_zero():
assert source_diversity([]) == 0.0
def test_source_diversity_increases_with_distinct_domains():
one = source_diversity([_page("https://a.example/1"), _page("https://a.example/2")])
many = source_diversity([_page("https://a.example/1"), _page("https://b.example/2")])
assert 0.0 < one <= 1.0
assert many > one
def test_source_diversity_capped_at_one():
pages = [_page(f"https://d{i}.example") for i in range(8)]
assert source_diversity(pages) <= 1.0
def test_orchestrate_with_no_pages_returns_gap():
async def fake_complete(messages, api_key, **kwargs):
return {}, {}, 0
result = run_async(orchestrate("q", [], "k", lambda frame: None))
assert isinstance(result, Orchestration)
assert result.source_diversity == 0.0
assert result.gaps
def test_orchestrate_grounded_run_emits_agent_frames(monkeypatch):
frames = []
summary_payload = json.dumps(
{
"summary": "A grounded answer.",
"findings": [
{"title": "Finding", "detail": "Detail", "confidence": 0.7, "citations": [1]}
],
}
)
gaps_payload = json.dumps({"gaps": ["one open question"]})
link_payload = json.dumps({"confidence": 0.8})
replies = iter([summary_payload, gaps_payload, link_payload])
async def fake_request_completion(messages, api_key, **kwargs):
return ({"choices": [{"message": {"content": next(replies)}}]}, {}, 5)
monkeypatch.setattr(orchestrate_module, "request_completion", fake_request_completion)
pages = [_page("https://a.example"), _page("https://b.example")]
result = run_async(orchestrate("question", pages, "k", frames.append))
assert result.summary == "A grounded answer."
assert result.findings and result.findings[0]["citations"] == [1]
assert result.gaps == ["one open question"]
assert 0.0 < result.confidence <= 1.0
assert result.source_diversity > 0.0
assert result.score > 0
agents = {f["agent"] for f in frames if f.get("type") == "agent"}
assert agents == {"summarizer", "critic", "linker"}
def test_orchestrate_falls_back_to_heuristic_on_failure(monkeypatch):
async def boom(messages, api_key, **kwargs):
raise RuntimeError("upstream down")
monkeypatch.setattr(orchestrate_module, "request_completion", boom)
pages = [_page("https://a.example"), _page("https://b.example")]
result = run_async(orchestrate("question", pages, "k", lambda frame: None))
assert result.findings
assert result.gaps
assert result.source_diversity > 0.0