# NewsService `name` `news` - enabled by default - 3600s interval (minimum 60s). It fetches developer news from a configured feed, grades each article with an AI model, and stores every article, auto-publishing those at or above the grade threshold and saving the rest as drafts. Source: `services/news.py`. See also [Reading service data](/docs/services-data.html). > Audience: administrators and maintainers. ## What run_once does 1. Reads the source URL, grading URL, model, and threshold from configuration. 2. Fetches the feed over HTTP (30s timeout) and reads its `articles` list. On error it logs and returns. 3. Loads the set of already-synced external ids from `news_sync` so seen articles are skipped. 4. For each new article it grades the article, decides published versus draft, upserts the `news` row, records the sync state, and stores any extracted images. 5. Logs a summary: new, updated, draft, grading-failed, and skipped counts. **Grading:** the article title, description, and content (each length-capped) are sent to the grading endpoint with a content-strategy prompt asking for a single integer from 1 to 10, and the first integer in the response is parsed. An article graded at or above `news_grade_threshold` is published; below threshold it is a draft; a grading failure stores it as a draft with grade 0 and sync status `grading_failed`. Nothing is silently discarded. ## Configuration fields - `news_api_url` (url, default `https://news.app.molodetz.nl/api`) - the source feed, group Source. - `news_ai_url` (url, default the internal gateway) - the chat-completions endpoint used to grade, group AI grading. - `news_ai_model` (str, default the internal model) - the model sent to the grading endpoint. - `news_grade_threshold` (int, default 7, range 1 to 10) - at or above publishes, below drafts. - `news_ai_key` (secret) - defaults to the `NEWS_AI_KEY` env var, then the gateway internal key. Plus the inherited Enabled, Run interval, and Log buffer size fields. ## Tables it writes | Table | Purpose | |-------|---------| | `news` | The article rows: `uid`, `slug`, `external_id`, `title`, `description` (stripped, capped 5000), `url`, `image_url`, `source_name`, `grade`, `status` (`published` or `draft`), `content` (stripped, capped 10000), `author`, `article_published`, `synced_at`. Existing rows are updated in place by `external_id`. | | `news_sync` | One row per external id tracking `status` (`graded` or `grading_failed`) and `synced_at`, so an article is graded only once. | | `news_images` | Images extracted per article, keyed by `news_uid`; replaced wholesale when the article is updated. | ## Data it offers NewsService does not override `collect_metrics()`, so its `metrics` block is empty. Health is read from the framework fields surfaced at `GET /admin/services/data`: `status` (`running` / `stopped` / `stalled`), `last_run`, `next_run`, `heartbeat`, and the `log_buffer`, where each run records its new/updated/draft/failed/skipped summary. The graded articles are the product, visible under `/news` and in the `news` table.