- thesis_llm.py: add _call_vllm_thesis() using /v1/chat/completions
- thesis_llm.py: check resolved model_provider and route accordingly
- values.yaml: set OLLAMA_BASE_URL to http://10.1.1.12:2701
- Migration 026: update seed defaults from ollama to vllm/AxionML
- Migration 031: fix existing rows still on old ollama defaults
- Helm values: set OLLAMA_BASE_URL to cluster ollama endpoint (was empty)
- Extractor: guard against switching to ollama when base_url is empty
- OllamaClient: validate base_url on construction to fail fast
- Migration 031: change ai_agents/agent_variants max_tokens default
from 32768 to 4096 (32768 exceeds vLLM context window, causing
HTTP 400 on every extraction)
- API: re-enqueue approved orders to broker queue — previously
approved orders sat in DB with nothing to execute them
- values-beta: enable TRADING_ENABLED, update Alpaca paper keys
- LLMClient Protocol for provider-agnostic inference
- VLLMClient for OpenAI-compatible /v1/chat/completions API
- LLM client factory with provider routing (ollama/vllm)
- VLLMConfig with VLLM_* environment variable loading
- Updated extractor worker with health check and provider switching
- Updated event classifier to use LLMClient protocol
- Helm values for vLLM configuration
- 18 unit tests + 6 property-based tests
- Full backward compatibility preserved
Two bugs: (1) trading engine omitted estimated_value from sell order
jobs, causing risk engine to compute 0 reduction; (2) risk engine
applied position size limits to sells, trapping users in positions
they couldn't exit. Sells now always pass position value/pct checks.
- pipelineEnabled: true in beta so all pods run (Kargo happy)
- PIPELINE_DEFAULT_OFF=true in beta config — scheduler initializes
the Redis toggle to OFF on first boot
- Shared Ollama (10.1.1.12:2701) between beta and paper
- Flip pipeline ON from the UI when testing, OFF when done
- Optimistic UI update for the toggle button
- Added pipelineEnabled flag to Helm values (default: true)
- Worker services (scheduler, ingestion, parser, extractor, aggregation,
recommendation, broker-adapter, lake-publisher) scale to 0 when disabled
- API services always run regardless of toggle
- Redis-based runtime toggle: POST /api/ops/pipeline/toggle
- Scheduler checks the flag before each cycle
- Frontend: green/red Pipeline ON/OFF button on the pipeline page
- Beta defaults to pipelineEnabled: false
- Base values.yaml: blanked external URLs (Ollama, Polygon, Alpaca)
so stages only connect to what they explicitly configure
The 30-minute threshold was shorter than the queue drain time, causing
the recovery sweep to re-enqueue docs that were already queued but not
yet processed. Bumped to 4 hours with matching marker TTL.
Recovery sweeps and the retry endpoint now check a per-document Redis
key (SET NX, 1h TTL) before pushing to the queue. If the marker exists,
the doc is already enqueued and gets skipped. This prevents the
scheduler from re-enqueuing the same parsed docs every 5 minutes.
The pipeline health, SSE stream, and retry endpoints were hardcoding
'stonks:queue:{name}' but services use DEPLOY_STAGE prefix
('stonks:paper:queue:{name}'). Now uses queue_key() from redis_keys.py.
- POST /api/ops/pipeline/retry-failed endpoint resets extraction_failed
docs to parsed, deletes failed intelligence rows, and re-enqueues
them (batch of 200)
- Scheduler now auto-retries extraction_failed docs every ~10 minutes
(100 per cycle, 60-min cooldown per doc)
- Pipeline page shows 'Retry Failed (N)' button when extraction_failed
count > 0, with pending/success/error states
The inline catalyst_type query in GET /api/patterns/{ticker} referenced
dir.document_id which does not exist on document_impact_records. The
table links to documents via intelligence_id -> document_intelligence ->
document_id. Added the missing JOIN to match the pattern used in
_SELF_PATTERN_QUERY.
1. patterns endpoint: fix query referencing non-existent column
di.catalyst_type → dir.catalyst_type (column is on document_impact_records)
2. lockouts seed: use relative timestamps (now + 7d) so active lockout
is always in the future regardless of when tests run
3. create_agent: make slug optional with auto-generation from name
4. create_source: json.dumps(config) + ::jsonb cast for asyncpg JSONB compat
5. approval_expiry: return count as int (len(expired)) not the list itself
6. metrics_consistency: fix test assertion to match API contract
(total >= active + reserve, not total == active + reserve + unrealized)
_parse_classification_response receives raw model output (with thinking
tags, markdown fences, etc.) but was calling json.loads directly.
Now uses _strip_markdown_fences + _repair_json from the client module
before parsing, matching what _call_ollama does for extractions.
_call_ollama validates against the document extraction schema, which
doesn't match event classification output. The event classifier was
checking 'if attempt.error is None' before trying its own parsing,
so it never got to parse the valid event JSON — 956 consecutive
failures.
Now tries _parse_classification_response whenever raw_output exists,
regardless of the extraction validation error.
Backend: assemble_trend_with_evidence now deduplicates document IDs
via dict.fromkeys() (the rollup code already did this, but the base
assembly didn't — same doc could appear multiple times from different
intelligence extractions).
Frontend: Trends.tsx deduplicates via Set before rendering as a safety
net for existing data already stored with duplicates.
- recommendation worker: filter out non-UUID document IDs (synthetic
pattern:* IDs from competitive signals) before inserting into
recommendation_evidence table — the uuid cast was failing and
silently dropping all evidence rows
- wrap executemany in try/except so partial failures don't lose all evidence
- SqlExplorer: wrap Lucide icons in <span title=...> instead of passing
title prop directly (not supported by lucide-react, broke CI build)
- Add GET/PUT /api/admin/trading/approval-config endpoints
- Add POST/DELETE /api/admin/trading/lockouts endpoints
- Add useApprovalConfig, useUpdateApprovalConfig, useCreateLockout, useDeleteLockout hooks
- Add Paper Order Approval toggle card with confirmation dialog
- Add lockout creation form and delete button to Active Lockouts card
- Add MSW handlers for all new endpoints
- Add property-based tests for bug condition exploration and preservation
API was returning a flat array but frontend expects CompanyMacroImpacts
wrapper with exposure_profile and impacts fields. Also queries the
exposure_profiles table for the company's active profile.
- Trading page: added conservative/moderate/aggressive selector that
updates the trading engine config via PUT /api/trading/config
- Recommendations page: added risk tier dropdown that defaults to the
engine's current tier and filters recs by the tier's min_confidence
- Backend: added min_confidence query param to GET /api/recommendations
- Risk tier thresholds: conservative ≥0.75, moderate ≥0.55, aggressive ≥0.40
- Removed PUT /api/trading/capital (set capital) — only touched in-memory state
- Removed POST /api/trading/capital/adjust (add/withdraw) — same problem
- Reset endpoint now: liquidates Alpaca positions, cancels orders, clears DB,
then queries Alpaca for real portfolio_value to set engine capital
- Frontend: replaced CapitalCard with simple ResetCard (one button)
- Removed useSetTradingCapital and useAdjustCapital hooks
- Added cancel_all_orders() and close_all_positions() to AlpacaBrokerAdapter
- Reset endpoint creates a temporary adapter to call Alpaca DELETE /v2/orders
and DELETE /v2/positions before clearing DB and engine state
- Also clears positions table and processed_recommendation_ids on reset
- Broker reset is best-effort — DB/engine reset proceeds even if Alpaca fails