feat(agent-office): YouTubeResearchAgent + 스케줄러 + /youtube/research API
- db.py: youtube_research_jobs 테이블 추가 + CRUD 3종 (add/update/get_latest) - agents/youtube.py: YouTubeResearchAgent 신규 구현 (on_schedule/on_command/on_approval/_run_research/send_weekly_report) - agents/__init__.py: YouTubeResearchAgent 등록 - scheduler.py: youtube_research(매일 09:00) + youtube_weekly_report(월 08:00) cron 추가 - main.py: POST /api/agent-office/youtube/research + GET /api/agent-office/youtube/research/status 엔드포인트 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -86,6 +86,17 @@ def init_db() -> None:
|
||||
CREATE INDEX IF NOT EXISTS idx_conv_chat
|
||||
ON conversation_messages(chat_id, created_at DESC)
|
||||
""")
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS youtube_research_jobs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
status TEXT NOT NULL DEFAULT 'running',
|
||||
countries TEXT NOT NULL DEFAULT '[]',
|
||||
trends_collected INTEGER NOT NULL DEFAULT 0,
|
||||
error TEXT,
|
||||
started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
||||
completed_at TEXT
|
||||
)
|
||||
""")
|
||||
# Seed default agent configs
|
||||
for agent_id, name in [
|
||||
("stock", "주식 트레이더"),
|
||||
@@ -93,6 +104,7 @@ def init_db() -> None:
|
||||
("blog", "블로그 마케터"),
|
||||
("realestate", "청약 애널리스트"),
|
||||
("lotto", "로또 큐레이터"),
|
||||
("youtube", "YouTube 리서치"),
|
||||
]:
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO agent_config(agent_id, display_name) VALUES(?,?)",
|
||||
@@ -501,3 +513,45 @@ def get_activity_feed(limit: int = 50, offset: int = 0) -> dict:
|
||||
items.append(item)
|
||||
|
||||
return {"items": items, "total": total}
|
||||
|
||||
|
||||
# ── youtube_research_jobs CRUD ────────────────────────────────────────────────
|
||||
|
||||
def add_youtube_research_job(countries: list) -> int:
|
||||
with _conn() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO youtube_research_jobs (countries) VALUES (?)",
|
||||
(json.dumps(countries),),
|
||||
)
|
||||
return conn.execute("SELECT last_insert_rowid()").fetchone()[0]
|
||||
|
||||
|
||||
def update_youtube_research_job(
|
||||
job_id: int, status: str, trends_collected: int, error: Optional[str] = None
|
||||
) -> None:
|
||||
with _conn() as conn:
|
||||
conn.execute(
|
||||
"""UPDATE youtube_research_jobs
|
||||
SET status=?, trends_collected=?, error=?,
|
||||
completed_at=strftime('%Y-%m-%dT%H:%M:%fZ','now')
|
||||
WHERE id=?""",
|
||||
(status, trends_collected, error, job_id),
|
||||
)
|
||||
|
||||
|
||||
def get_latest_youtube_research_job() -> Optional[Dict[str, Any]]:
|
||||
with _conn() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM youtube_research_jobs ORDER BY started_at DESC LIMIT 1"
|
||||
).fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return {
|
||||
"id": row["id"],
|
||||
"status": row["status"],
|
||||
"countries": json.loads(row["countries"]),
|
||||
"trends_collected": row["trends_collected"],
|
||||
"error": row["error"],
|
||||
"started_at": row["started_at"],
|
||||
"completed_at": row["completed_at"],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user