chore: commit remaining modified files from refactoring

main
Sven Geboers 4 weeks ago
parent 805cc7e284
commit eb71328967
  1. 5
      .gitignore
  2. 89
      .mindmodel/manifest.yaml
  3. 77
      .mindmodel/system.md
  4. 110
      ARCHITECTURE.md
  5. 2
      pipeline/run_pipeline.py
  6. 403
      thoughts/ledgers/audit_events.json
  7. 1373
      thoughts/shared/diagnostics/2026-03-31-trajectories-diagnostics.json

5
.gitignore vendored

@ -25,3 +25,8 @@ dummy
# Worktrees # Worktrees
.worktrees/ .worktrees/
# Generated analysis files
thoughts/explorer/*.json
thoughts/explorer/*_report.md
thoughts/shared/analyses/

@ -1,15 +1,74 @@
# DO NOT EDIT - read-only until validated name: stemwijzer
# Sanitized manifest: contains non-sensitive sample excerpts only version: 2
files: description: Dutch political voting compass (Stemwijzer) - Mind Model constraints
- path: src/lib/schema.ts
evidence_excerpt: "Defines schema for user input validation" categories:
flags: # Core documentation
needs_review: true - path: system.md
- path: src/api/handler.ts description: System overview and architecture summary
evidence_excerpt: "Handles API requests and routing" group: docs
flags: - path: tech-stack.yaml
needs_review: false description: Technology stack with versions and purposes
- path: README.md group: docs
evidence_excerpt: "Project overview and setup instructions" - path: conventions.yaml
flags: description: Coding conventions and style guide
needs_review: true group: docs
- path: domain.yaml
description: Domain entities, terms, and relationships
group: docs
# Design patterns
- path: patterns/architecture.yaml
description: Repository, Facade, Pipeline architectural patterns
group: patterns
- path: patterns/python.yaml
description: Python-specific patterns (Singleton, dataclass, context manager)
group: patterns
- path: patterns/database.yaml
description: DuckDB connection patterns and ORM usage
group: patterns
- path: patterns/api.yaml
description: API client patterns with retry logic and pagination
group: patterns
- path: patterns/streamlit.yaml
description: Streamlit session state and page patterns
group: patterns
# Coding constraints
- path: constraints/error-handling.yaml
description: Error handling patterns with safe fallbacks
group: constraints
- path: constraints/logging.yaml
description: Logging conventions and best practices
group: constraints
- path: constraints/naming.yaml
description: File, class, function naming rules
group: constraints
- path: constraints/imports.yaml
description: Import organization and module structure
group: constraints
- path: constraints/types.yaml
description: Type hint conventions
group: constraints
# Code examples
- path: examples/database-example.py
description: MotionDatabase usage example
group: examples
- path: examples/api-client-example.py
description: TweedeKamerAPI usage
group: examples
- path: examples/pipeline-example.py
description: Pipeline phase example
group: examples
- path: examples/streamlit-page-example.py
description: Streamlit page pattern
group: examples
# Anti-patterns and workflows
- path: anti-patterns.yaml
description: Known anti-patterns to avoid
group: meta
- path: workflows.yaml
description: Key workflows (VotingSession, DataIngestion, EmbeddingGeneration)
group: meta

@ -1,14 +1,69 @@
# System Overview: Stemwijzer # System Overview
This mindmodel documents constraints, conventions and patterns for the Stemwijzer ## Project: Stemwijzer (Dutch Political Voting Compass)
project (Python Streamlit app with DuckDB-backed pipeline for parliamentary
motions embedding analysis).
Key points: **Purpose**: A web application that maps the Dutch Tweede Kamer (House of Representatives) based on real parliamentary votes, helping citizens discover which political party aligns best with their views.
- Language: Python >=3.13
- UI: Streamlit multi-page app (Home.py, pages/)
- Storage: DuckDB with JSON fallback for tests/dev (database.py)
- Pipeline: ETL and SVD/text fusion pipeline (pipeline/run_pipeline.py)
- AI: ai_provider adapter uses HTTP-based OpenRouter/OpenAI-compatible API with retry/backoff and local fallback. QWEN via OpenRouter is the recommended path; prefer OPENROUTER_API_KEY with OPENAI_API_KEY as a fallback where applicable.
Use the .mindmodel/ constraints files to guide code changes, CI, and onboarding. ## Architecture Summary
### Data Flow
```
TweedeKamer OData API
API Client (api_client.py)
DuckDB Database (database.py)
Pipeline Processing (pipeline/)
├── fetch_mp_metadata # MP party + tenure
├── extract_mp_votes # voting_results → mp_votes
├── svd_pipeline # SVD on vote matrix + Procrustes
├── text_pipeline # AI embeddings via OpenRouter
└── fusion # Combine SVD + text vectors
Streamlit Web App (app.py, pages/)
├── Home.py # Landing page
├── 1_Stemwijzer.py # Voting quiz
└── 2_Explorer.py # Political compass explorer
```
### Key Components
| Component | Purpose | File(s) |
|-----------|---------|---------|
| **Database** | Motion storage, MP votes, embeddings | `database.py` |
| **API Client** | TweedeKamer OData API integration | `api_client.py` |
| **AI Provider** | OpenRouter API for embeddings/summaries | `ai_provider.py` |
| **Pipeline** | Orchestrated data processing | `pipeline/run_pipeline.py` |
| **Analysis** | SVD, clustering, trajectory computation | `analysis/*.py` |
| **Similarity** | Motion similarity search | `similarity/*.py` |
| **Web App** | Streamlit UI | `app.py`, `pages/*.py` |
### Data Models
**Core Entities**:
- `Motion`: Parliamentary motion with voting results
- `MP` / `MPMetadata`: Member of Parliament with party/tenure
- `MPVote`: Individual vote record (Voor/Tegen/Onthouden/Geen stem/Afwezig)
- `Party`: Political party
- `UserSession` / `UserVote`: Voting session tracking
- `SVDVector`: Dimensionality-reduced vote vectors
- `FusedEmbedding`: Combined SVD + text embedding
- `SimilarityCache`: Pre-computed motion similarities
### Technical Decisions
1. **DuckDB over SQLite**: Chosen for OLAP performance with complex analytical queries
2. **ibis ORM**: Database-agnostic query building (currently using DuckDB backend)
3. **SVD + Procrustes**: Aligns voting vectors across time windows
4. **UMAP for visualization**: Non-linear dimensionality reduction for compass display
5. **OpenRouter API**: Abstraction layer for AI embeddings (currently using Qwen)
6. **Module-level singletons**: `db = MotionDatabase()` pattern for shared state
### Key Conventions
- **DuckDB connections**: Short-lived per method, always close
- **Error handling**: Catch `Exception`, return safe fallbacks (False/[]/None)
- **Logging**: Use `logging.getLogger(__name__)` - avoid print()
- **Type hints**: Required on public functions with typing module imports
- **Config**: Dataclass `Config` in `config.py`, accessed as `from config import config`

@ -1,69 +1,81 @@
# ARCHITECTURE ---
title: ARCHITECTURE
---
## Overview ## Overview
- Small Python project that collects, stores and presents Dutch parliamentary motions (Tweede Kamer). Itingests votes (OData API or HTML scraping), stores motions in a DuckDB file, generates short humansummaries using an LLM client, and exposes a Streamlit UI for users to vote and view matching results. - Small Python project that collects, stores and presents Dutch parliamentary motions (Tweede Kamer). It ingests votes via OData API, stores motions in a DuckDB file, generates short human-readable summaries using an LLM client, and exposes a Streamlit UI for users to vote and view matching results.
## Tech stack ## Tech stack
- Language: Python (single-project repository) - Language: Python (single-project repository)
- Data: DuckDB (file: data/motions.db), ibis used in a small utility (read.py) - Data: DuckDB (file: data/motions.db)
- Web / UI: Streamlit (app.py) - Web / UI: Streamlit (app.py, pages/)
- HTTP: requests - HTTP: requests (ai_provider.py, api_client.py)
- HTML parsing: BeautifulSoup (scraper.py) - LLM: QWEN (via OpenRouter) / OpenAI-compatible client (ai_provider.py). Prefer QWEN via OpenRouter where possible.
- Scheduling: schedule (scheduler.py) - Analysis: scipy (SVD), scikit-learn (clustering), umap-learn (dimensionality reduction)
- LLM: QWEN (via OpenRouter) / OpenAI-compatible client (summarizer.py uses an OpenRouter/OpenAI-compatible client configured via config). Prefer QWEN via OpenRouter where possible. - Visualization: Plotly
- Packaging: pyproject.toml present - Packaging: pyproject.toml
## Top-level layout (annotated) ## Top-level layout (annotated)
./ ./
- app.py — Streamlit UI entrypoint (Home.py routing)
- app.py — Streamlit UI, main UI flow and session handling (entrypoint for web) - Home.py — Thin wrapper with minimal logic
- main.py — minimal CLI entry / small script
- database.py — MotionDatabase: DuckDB schema, insert/query/update, party-match calculations - database.py — MotionDatabase: DuckDB schema, insert/query/update, party-match calculations
- api_client.py — TweedeKamerAPI: fetch OData voting records and group into motions - api_client.py — TweedeKamerAPI: fetch OData voting records and group into motions
- scraper.py — MotionScraper: HTML fallback scraper for motion pages
- summarizer.py — MotionSummarizer: LLM integration to generate layman_explanation - summarizer.py — MotionSummarizer: LLM integration to generate layman_explanation
- scheduler.py — DataUpdateScheduler: initial historical loads + periodic scheduled updates
- config.py — Config dataclass: central configuration (DATABASE_PATH, API/AI settings, constants) - config.py — Config dataclass: central configuration (DATABASE_PATH, API/AI settings, constants)
- read.py — small ibis + duckdb demonstration/utility - ai_provider.py — Lightweight HTTP wrapper around OpenRouter/OpenAI-style backends
- fix_database.py — script to recreate/reset DuckDB schema - explorer.py — Explorer page logic, tab routing, SVD visualization
- reset.py / verify.py — small maintenance scripts that call into database module - explorer_helpers.py — Pure functions for chart builders, coordinate computation
- test.py — ad-hoc test script (manual insert/verification) - data/ — data/motions.db (DuckDB file, ~18GB)
- data/ — data/motions.db (DuckDB file)
- pyproject.toml — project metadata / dependencies - pyproject.toml — project metadata / dependencies
- .env — environment variables (not printed here) - .env — environment variables (not printed here)
## Directory structure
- `pages/` — Streamlit pages: 1_Stemwijzer.py, 2_Explorer.py
- `pipeline/` — Data ingestion pipelines: run_pipeline.py, svd_pipeline.py, text_pipeline.py
- `analysis/` — SVD, clustering, trajectory, visualization modules
- `similarity/` — Embedding-based similarity computation
- `scripts/` — Utility scripts for data processing
- `tests/` — Test suite using pytest
- `migrations/` — SQL migration files
## Core components ## Core components
- Streamlit UI (app.py) - Streamlit UI (app.py + pages/)
- Presents the voting UI, reads filtered motions from database, creates sessions, writes user votes - Presents the voting UI, reads filtered motions from database, creates sessions, writes user votes
- Calls: database.get_filtered_motions(), database.create_session(), database.update_user_vote(),database.calculate_party_matches(), summarizer.update_motion_summaries() - Explorer page (explorer.py) provides SVD visualization and party trajectory analysis
- Storage (database.py) - Storage (database.py)
- MotionDatabase encapsulates DuckDB schema creation and CRUD for motions and user sessions - MotionDatabase encapsulates DuckDB schema creation and CRUD for motions and user sessions
- Exposes a module-level instance `db = MotionDatabase()` used across the codebase - Exposes a module-level instance `db = MotionDatabase()` used across the codebase
- Key responsibilities: insert_motion, get_filtered_motions, create_session, update_user_vote,calculate_party_matches - Key responsibilities: insert_motion, get_filtered_motions, create_session, update_user_vote, calculate_party_matches
- Ingestion (api_client.py + scraper.py) - Ingestion (api_client.py + pipeline/)
- api_client.py fetches votes via Tweede Kamer OData API and groups records into motions - api_client.py fetches votes via Tweede Kamer OData API and groups records into motions
- scraper.py is an HTML fallback that scrapes motion pages and extracts vote info - pipeline/ orchestrates the full ingestion and analysis workflow
- Both provide structured motion dicts consumed by database.insert_motion()
- Summarization (summarizer.py) - Summarization (summarizer.py)
- Wraps an OpenRouter/OpenAI-compatible client (QWEN via OpenRouter recommended) to produce short layman explanations and persists them to DB - Wraps an OpenRouter/OpenAI-compatible client (QWEN via OpenRouter recommended) to produce short layman explanations and persists them to DB
- Reads motions without layman_explanation and updates rows - Reads motions without layman_explanation and updates rows
- Orchestration (scheduler.py) - Analysis (analysis/)
- Runs initial historical ingestion and schedules periodic updates (using schedule) - SVD decomposition of voting patterns
- Calls API client and summarizer and writes to the database - UMAP for visualization
- Clustering for motion grouping
- Trajectory computation for party movement over time
## Data flow (high level) ## Data flow (high level)
1. Ingestion 1. Ingestion
- scheduler / manual run triggers TweedeKamerAPI.get_motions(...) or MotionScraper.run_scraping_job() - Pipeline triggers TweedeKamerAPI.get_motions(...)
- Each produced motion dict is passed to MotionDatabase.insert_motion() - Each produced motion dict is passed to MotionDatabase.insert_motion()
- insert_motion writes to DuckDB (data/motions.db) - insert_motion writes to DuckDB (data/motions.db)
2. Enrichment 2. Enrichment
- summarizer.update_motion_summaries() reads motions lacking layman_explanation, calls the LLM client (OpenRouter/OpenAI-compatible client) and writes summary text back to the DB - summarizer.update_motion_summaries() reads motions lacking layman_explanation, calls the LLM client and writes summary text back to the DB
3. Presentation / Interaction 3. Analysis
- pipeline/svd_pipeline.py computes SVD embeddings from vote matrix
- Results stored in svd_vectors table for visualization
4. Presentation / Interaction
- app.py (Streamlit) queries motions via db.get_filtered_motions() and displays them - app.py (Streamlit) queries motions via db.get_filtered_motions() and displays them
- Users vote; app.py writes votes into the database via db.update_user_vote() - Users vote; app.py writes votes into the database via db.update_user_vote()
- app.py calls db.calculate_party_matches() to compute match percentages for parties - app.py calls db.calculate_party_matches() to compute match percentages for parties
@ -72,43 +84,45 @@
- Tweede Kamer OData API (api_client.py) - Tweede Kamer OData API (api_client.py)
- HTTP (requests) - HTTP (requests)
- HTML parsing (BeautifulSoup) used by scraper.py
- DuckDB (database file at data/motions.db) - DuckDB (database file at data/motions.db)
- ibis (read.py demonstrates an ibis.duckdb connection)
- Streamlit for UI - Streamlit for UI
- OpenRouter/OpenAI-compatible LLM client (summarizer.py) — configured with environment variables in config.py. Prefer using OPENROUTER_API_KEY with OPENAI_API_KEY as a fallback where appropriate. - OpenRouter/OpenAI-compatible LLM client (ai_provider.py) — configured with environment variables in config.py
## Configuration ## Configuration
- config.py: central Config dataclass. Observed keys / env variables referenced across the codebase include: - config.py: central Config dataclass. Observed keys / env variables referenced across the codebase include:
- config.DATABASE_PATH (default "data/motions.db") - config.DATABASE_PATH (default "data/motions.db")
- OPENROUTER_API_KEY / other OPENROUTER_* variables used by summarizer.py - OPENROUTER_API_KEY / other OPENROUTER_* variables used by ai_provider.py
- QWEN_MODEL (or other model identifier) referenced in summarizer.py - QWEN_MODEL (or other model identifier) referenced in summarizer.py
- API timeout / batch size constants - API timeout / batch size constants
- .env file present at repo root (do not commit secrets). See .env.example if present (none observed). - .env file present at repo root (do not commit secrets)
- Packaging metadata: pyproject.toml - Packaging metadata: pyproject.toml
## Build, run & development notes ## Build, run & development notes
- Install dependencies via the project's Python packaging (pyproject.toml). There is no Dockerfile or CIworkflows detected in the repository. - Install dependencies via the project's Python packaging (pyproject.toml)
- Use uv add and uv run to manage the dependencies in this directory and run scripts - Use `uv add` and `uv run` to manage the dependencies in this directory and run scripts
- Streamlit app: run `uv run streamlit run app.py` from project root to start the UI (app.py is the intended web entrypoint). - Streamlit app: run `uv run streamlit run app.py` from project root to start the UI (app.py is the intended web entrypoint)
- Never use pip directly! - Never use pip directly!
- Scheduler: run scheduler.run_once() (script or import) or run scheduler.run_scheduler() for periodic ingestion. - Run tests: `uv run pytest tests/`
## Tests ## Tests
- There is no test suite using pytest / unittest. One ad-hoc script `test.py` exists for manual insert verification. - Test suite in `tests/` using pytest
- Run with `uv run pytest tests/`
## Notes / caveats ## Notes / caveats
- Project is synchronous (no async/await patterns detected). Many modules rely on module-level singletons(e.g., `db = MotionDatabase()`, `summarizer = MotionSummarizer()`, `scraper = MotionScraper()`). - Project is synchronous (no async/await patterns detected)
- Error handling frequently catches broad Exception and prints to stdout (see database.py, api_client.py,scraper.py). Logging is not centralized (print statements used). - Many modules rely on module-level singletons (e.g., `db = MotionDatabase()`, `summarizer = MotionSummarizer()`)
- Error handling frequently catches broad Exception and prints to stdout (see database.py, api_client.py)
- Logging is not centralized (print statements used)
## Where to look first (for contributors) ## Where to look first (for contributors)
- app.py — follow the UI flow and see how votes & sessions are used - app.py + pages/ — follow the UI flow and see how votes & sessions are used
- database.py — core data model and calculations - database.py — core data model and calculations
- api_client.py — OData ingestion logic - explorer.py — SVD visualization and party analysis
- summarizer.py — LLM usage and environment variables - api_client.py — OData ingestion logic
- scheduler.py — how ingestion is orchestrated over time - summarizer.py — LLM usage and environment variables
- pipeline/ — how ingestion and analysis is orchestrated

@ -260,7 +260,7 @@ def build_parser() -> argparse.ArgumentParser:
parser.add_argument( parser.add_argument(
"--window-size", "--window-size",
choices=["quarterly", "annual"], choices=["quarterly", "annual"],
default="quarterly", default="annual",
help="Time window granularity", help="Time window granularity",
) )
parser.add_argument("--svd-k", type=int, default=50, help="SVD dimensions") parser.add_argument("--svd-k", type=int, default=50, help="SVD dimensions")

@ -917,5 +917,408 @@
"target_id": null, "target_id": null,
"metadata": {}, "metadata": {},
"created_at": "2026-03-30T18:52:43.630069Z" "created_at": "2026-03-30T18:52:43.630069Z"
},
{
"id": "35963a0d-c63f-4afe-95f6-5b5208e57d29",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-01T23:46:34.584639Z"
},
{
"id": "e68dfe7f-e0d9-4384-914d-4007f89b8e29",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-01T23:46:37.561361Z"
},
{
"id": "e0a791fd-108d-49eb-bb09-8d0428e52e69",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-01T23:46:37.604922Z"
},
{
"id": "f276f1ad-dc32-46a3-a74d-aa6e8a372fca",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-01T23:55:13.967080Z"
},
{
"id": "7dc7b020-6c39-43c3-8aa3-174d344f91a7",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-01T23:55:14.671187Z"
},
{
"id": "ac7e74cc-3b49-48dd-b9f0-d112df40d5ef",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-01T23:55:14.699780Z"
},
{
"id": "fa59ab8a-2bb7-4b71-a48c-138278966dfb",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:01:42.827310Z"
},
{
"id": "009b293a-fec3-49de-8492-3af93d597af0",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:01:44.222780Z"
},
{
"id": "2c31b5a9-5332-4bec-abc7-bce8a8c1f91a",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:01:44.267873Z"
},
{
"id": "fcb47926-785a-49a8-b492-6ed0f837f91e",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:02:21.461896Z"
},
{
"id": "c20946d2-153e-44c7-9b87-a75974c05b8f",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:02:22.721758Z"
},
{
"id": "6f943c69-e340-40c9-8df5-9a69dc0176d4",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:02:22.781638Z"
},
{
"id": "0b0c6aa3-777f-4d13-b445-0334ca79abbc",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:05:29.897839Z"
},
{
"id": "3fb54286-4495-4b42-8819-92fb05036d9b",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:05:31.206251Z"
},
{
"id": "93ebb7a9-4734-43a7-8e32-7e4531e8393f",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:05:31.267085Z"
},
{
"id": "d2a9f12e-9dfd-4879-b50f-1fc7db6f78f6",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:05:45.130369Z"
},
{
"id": "5d5d756e-85aa-485e-8961-f2f6d495ebd6",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:05:46.352702Z"
},
{
"id": "c4c61d53-22b5-4669-9ff3-457c0c0d0367",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:05:46.424142Z"
},
{
"id": "b2599219-407b-4ea8-865c-41f61119b76b",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:07:57.967977Z"
},
{
"id": "553c5da0-3fd6-4751-8990-5b2598f5e356",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:07:59.154098Z"
},
{
"id": "73dd2e11-4ec5-4aa2-9434-1511988ee2ae",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:07:59.196193Z"
},
{
"id": "e3bab538-e5e4-49be-9cb9-80a2fca75658",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:08:15.084851Z"
},
{
"id": "2688a143-6744-41b2-975f-c55fd7594df5",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:08:16.326930Z"
},
{
"id": "b4249645-67ef-4902-908b-f4e4aa17a7a2",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:08:16.371816Z"
},
{
"id": "c4d307fe-8363-4de7-86be-65b28b44105c",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:14:50.773665Z"
},
{
"id": "aaa28b70-3af9-4b0b-bb5a-203367206a9f",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:14:52.080492Z"
},
{
"id": "19dbbc73-2954-4045-a0d3-21189380e097",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:14:52.152262Z"
},
{
"id": "025319c5-5fc0-46e3-8230-216a70b40ac6",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:22:21.697203Z"
},
{
"id": "0a2623fb-66be-4b62-8a1c-33c7f248bbd9",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:22:22.950861Z"
},
{
"id": "7efd721a-ca00-48c4-95e5-d45a609404f4",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:22:22.994655Z"
},
{
"id": "d9506033-2fc0-4317-8dd8-a388655e087b",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:23:15.774659Z"
},
{
"id": "9fda605d-8c05-45b1-a9d6-b27c0cccbc7c",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:23:16.993127Z"
},
{
"id": "b0d9da81-4528-4d79-97ef-107ec4f207dc",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:23:17.038391Z"
},
{
"id": "4378866d-023b-4bfb-a1d1-c012bb7f2f09",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:24:31.074547Z"
},
{
"id": "b0b5c909-305d-4947-af1e-a3495aba0c39",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:24:32.344948Z"
},
{
"id": "c408f4c6-c188-4d20-8f06-ae293efce45e",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:24:32.397774Z"
},
{
"id": "e4f83fc7-12f1-4b08-8f65-48dc4335f2dd",
"actor_id": null,
"action": "embedding_failed",
"target_type": "motion",
"target_id": "99",
"metadata": {
"error": "RuntimeError(\"Simulated embedding failure for index 0: 'failing motion'\")"
},
"created_at": "2026-04-02T19:25:52.766542Z"
},
{
"id": "3705c35f-f94f-4333-9c09-318e57ec6bc3",
"actor_id": null,
"action": "test_action",
"target_type": "unit",
"target_id": "u1",
"metadata": {
"k": 1
},
"created_at": "2026-04-02T19:25:54.130128Z"
},
{
"id": "c83ce165-d7fa-437a-a99d-ba5ef50083ab",
"actor_id": null,
"action": "another_action",
"target_type": "motion",
"target_id": null,
"metadata": {},
"created_at": "2026-04-02T19:25:54.178825Z"
} }
] ]

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save