add event clustering feature to group articles by similarity and time proximity
This commit is contained in:
@@ -23,7 +23,7 @@ On startup the server:
|
||||
1. Opens the SQLite database and runs any pending migrations.
|
||||
2. Registers routes.
|
||||
3. Starts the HTTP server.
|
||||
4. Launches continuous background loops for each source, content backfill, and embedding backfill.
|
||||
4. Launches continuous background loops for each source, content backfill, embedding backfill, and event clustering.
|
||||
|
||||
When a new article is inserted:
|
||||
|
||||
@@ -31,6 +31,7 @@ When a new article is inserted:
|
||||
- `content` starts as `null`
|
||||
- content backfill workers pick it up asynchronously — plain HTTP first, Playwright fallback for JS-heavy sites
|
||||
- vector embeddings are generated after title, description, and content are all available
|
||||
- the clustering worker assigns the article to an event once it has an embedding
|
||||
- only articles with content + embedding are exposed via the API
|
||||
|
||||
Content backfill prioritises recent articles (`pub_date_effective DESC`) so newest content surfaces first regardless of ingestion order.
|
||||
@@ -107,6 +108,41 @@ Semantic and similarity results also include `"distance": 0.1234`.
|
||||
|
||||
Returns one article by numeric ID. Same usability filter as the list endpoint — returns `404` if the article exists but has no content or embedding.
|
||||
|
||||
### `GET /events`
|
||||
|
||||
Returns a single event and its articles.
|
||||
|
||||
#### Query params
|
||||
|
||||
| Param | Description |
|
||||
|---|---|
|
||||
| `id` | Event ID (required) |
|
||||
|
||||
#### Response shape
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"title": "...",
|
||||
"created_at": "2025-01-01T12:35:10.000Z",
|
||||
"articles": [
|
||||
{
|
||||
"id": 123,
|
||||
"title": "...",
|
||||
"description": "...",
|
||||
"content": "...",
|
||||
"url": "...",
|
||||
"normalized_title": "...",
|
||||
"source": "rss:BBC",
|
||||
"pub_date": "2025-01-01T12:34:56.000Z",
|
||||
"ingested_at": "2025-01-01T12:35:10.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Returns `404` if the event ID does not exist.
|
||||
|
||||
### `GET /status`
|
||||
|
||||
Returns archive summary. Cached for 30 seconds.
|
||||
@@ -149,3 +185,4 @@ Use `domains[].policy` to diagnose why a source has high `skipped` or `failed` c
|
||||
- Embeddings use OpenRouter and are indexed in `sqlite-vec` for ANN search.
|
||||
- Query embeddings are cached in SQLite to avoid redundant API calls.
|
||||
- SEC requests use the `User-Agent` from `config.json`.
|
||||
- Event clustering groups articles by embedding similarity (cosine distance ≤ `config.clustering.distanceThreshold`, default `0.25`) and time proximity (within `config.clustering.windowHours`, default `72`). Articles outside the time window are never grouped together even if embeddings are close.
|
||||
|
||||
Reference in New Issue
Block a user