Route create_content by content_type instead of URL presence
Content Creation tasks with a URL were incorrectly routed to the optimization path. Now the scheduler sets content_type from Work Category, and the tool routes on that. Chat callers fall back to URL-based detection when content_type is empty. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>fix/customer-field-migration
parent
3ed7f2bbdd
commit
85d9fc7a1f
|
|
@ -435,7 +435,7 @@ class Scheduler:
|
|||
field_mapping = mapping.get("field_mapping", {})
|
||||
missing_clickup = [field_mapping.get(f, f) for f in missing]
|
||||
msg = f"Skipped: missing required field(s): {', '.join(missing_clickup)}"
|
||||
log.info("Skipping ClickUp task %s (%s) — %s", task_id, task.name, msg)
|
||||
log.debug("Skipping ClickUp task %s (%s) — %s", task_id, task.name, msg)
|
||||
self._notify(
|
||||
f"Skipped ClickUp task: **{task.name}**\n{msg}",
|
||||
category="clickup",
|
||||
|
|
@ -453,6 +453,13 @@ class Scheduler:
|
|||
args["clickup_task_id"] = task_id
|
||||
args["clickup_task_status"] = task.status
|
||||
|
||||
# Map Work Category to content_type so create_content routes correctly
|
||||
if tool_name == "create_content":
|
||||
if task.task_type == "On Page Optimization":
|
||||
args["content_type"] = "on page optimization"
|
||||
elif task.task_type == "Content Creation":
|
||||
args["content_type"] = "new content"
|
||||
|
||||
# Execute the skill via the tool registry
|
||||
if hasattr(self.agent, "_tools") and self.agent._tools:
|
||||
result = self.agent._tools.execute(tool_name, args)
|
||||
|
|
|
|||
|
|
@ -741,10 +741,10 @@ def _sync_clickup_optimization_complete(
|
|||
|
||||
@tool(
|
||||
"create_content",
|
||||
"Two-phase SEO content creation: Phase 1 researches + outlines, Phase 2 writes "
|
||||
"full content from the approved outline. Auto-detects phase from ClickUp task "
|
||||
"status ('outline approved' → Phase 2). "
|
||||
"Auto-detects content type from URL presence if not specified.",
|
||||
"SEO content creation. Set content_type='new content' for new pages "
|
||||
"(Phase 1 outline → Phase 2 full write), or content_type='on page optimization' "
|
||||
"to optimize an existing page with Cora data. Auto-detects phase from ClickUp "
|
||||
"task status ('outline approved' → Phase 2). Ask the user which type if unclear.",
|
||||
category="content",
|
||||
)
|
||||
def create_content(
|
||||
|
|
@ -758,8 +758,9 @@ def create_content(
|
|||
|
||||
Args:
|
||||
keyword: Primary target keyword (e.g. "plumbing services").
|
||||
url: Target page URL. If provided → on-page optimization; if empty → new content.
|
||||
content_type: Type of content. Auto-detected from URL if empty.
|
||||
url: Target page URL (optional for new content, required for optimization).
|
||||
content_type: 'new content' or 'on page optimization'. Controls routing.
|
||||
If empty, inferred from URL presence as fallback.
|
||||
cli_flags: Optional flags (e.g. "service" for service page hint).
|
||||
"""
|
||||
if not keyword:
|
||||
|
|
@ -805,9 +806,10 @@ def create_content(
|
|||
|
||||
capabilities_default = config.content.company_capabilities_default if config else ""
|
||||
|
||||
# Optimization path: URL present → run Phase 3 test block pipeline
|
||||
# (skips the outline gate entirely)
|
||||
if url:
|
||||
# Optimization path: content_type determines route (URL fallback for chat callers)
|
||||
if content_type.lower() == "on page optimization":
|
||||
if not url:
|
||||
return "Error: On Page Optimization requires a URL (IMSURL field)."
|
||||
return _run_optimization(
|
||||
agent=agent,
|
||||
config=config,
|
||||
|
|
|
|||
|
|
@ -884,8 +884,8 @@ class TestSyncClickupOptimizationComplete:
|
|||
|
||||
class TestCreateContentRouting:
|
||||
@patch("cheddahbot.tools.content_creation._run_optimization")
|
||||
def test_url_present_routes_to_optimization(self, mock_opt, tmp_db, tmp_path):
|
||||
"""When URL is present, create_content should call _run_optimization."""
|
||||
def test_explicit_optimization_routes_correctly(self, mock_opt, tmp_db, tmp_path):
|
||||
"""When content_type='on page optimization', routes to _run_optimization."""
|
||||
mock_opt.return_value = "## Optimization Complete"
|
||||
cfg = Config()
|
||||
cfg.content = ContentConfig(outline_dir=str(tmp_path / "outlines"))
|
||||
|
|
@ -898,14 +898,15 @@ class TestCreateContentRouting:
|
|||
result = create_content(
|
||||
keyword="plumbing services",
|
||||
url="https://example.com/plumbing",
|
||||
content_type="on page optimization",
|
||||
ctx=ctx,
|
||||
)
|
||||
mock_opt.assert_called_once()
|
||||
assert result == "## Optimization Complete"
|
||||
|
||||
@patch("cheddahbot.tools.content_creation._run_optimization")
|
||||
def test_no_url_does_not_route_to_optimization(self, mock_opt, tmp_db, tmp_path):
|
||||
"""When URL is empty, create_content should NOT call _run_optimization."""
|
||||
def test_explicit_new_content_with_url_routes_to_phase1(self, mock_opt, tmp_db, tmp_path):
|
||||
"""Content Creation with URL should go to Phase 1, NOT optimization."""
|
||||
cfg = Config()
|
||||
cfg.content = ContentConfig(outline_dir=str(tmp_path / "outlines"))
|
||||
agent = MagicMock()
|
||||
|
|
@ -918,15 +919,58 @@ class TestCreateContentRouting:
|
|||
}
|
||||
result = create_content(
|
||||
keyword="new keyword",
|
||||
url="",
|
||||
url="https://example.com/future-page",
|
||||
content_type="new content",
|
||||
ctx=ctx,
|
||||
)
|
||||
mock_opt.assert_not_called()
|
||||
assert "Phase 1 Complete" in result
|
||||
|
||||
@patch("cheddahbot.tools.content_creation._run_optimization")
|
||||
def test_optimization_without_url_returns_error(self, mock_opt, tmp_db, tmp_path):
|
||||
"""On Page Optimization without URL should return an error."""
|
||||
cfg = Config()
|
||||
cfg.content = ContentConfig(outline_dir=str(tmp_path / "outlines"))
|
||||
ctx = {
|
||||
"agent": MagicMock(),
|
||||
"config": cfg,
|
||||
"db": tmp_db,
|
||||
"clickup_task_id": "",
|
||||
}
|
||||
result = create_content(
|
||||
keyword="plumbing services",
|
||||
url="",
|
||||
content_type="on page optimization",
|
||||
ctx=ctx,
|
||||
)
|
||||
mock_opt.assert_not_called()
|
||||
assert "Error" in result
|
||||
assert "URL" in result
|
||||
|
||||
@patch("cheddahbot.tools.content_creation._run_optimization")
|
||||
def test_fallback_url_routes_to_optimization(self, mock_opt, tmp_db, tmp_path):
|
||||
"""When content_type is empty and URL present, falls back to optimization."""
|
||||
mock_opt.return_value = "## Optimization Complete"
|
||||
cfg = Config()
|
||||
cfg.content = ContentConfig(outline_dir=str(tmp_path / "outlines"))
|
||||
ctx = {
|
||||
"agent": MagicMock(),
|
||||
"config": cfg,
|
||||
"db": tmp_db,
|
||||
"clickup_task_id": "routing_test",
|
||||
}
|
||||
result = create_content(
|
||||
keyword="plumbing services",
|
||||
url="https://example.com/plumbing",
|
||||
content_type="",
|
||||
ctx=ctx,
|
||||
)
|
||||
mock_opt.assert_called_once()
|
||||
assert result == "## Optimization Complete"
|
||||
|
||||
@patch("cheddahbot.tools.content_creation._run_optimization")
|
||||
def test_new_content_still_calls_phase1(self, mock_opt, tmp_db, tmp_path):
|
||||
"""Regression: new content (no URL) still goes through _run_phase1."""
|
||||
"""Regression: new content (no URL, no content_type) still goes through _run_phase1."""
|
||||
cfg = Config()
|
||||
cfg.content = ContentConfig(outline_dir=str(tmp_path / "outlines"))
|
||||
agent = MagicMock()
|
||||
|
|
|
|||
Loading…
Reference in New Issue