Implement comprehensive resource tracking and cost metering for SciDEX operations. This enables understanding of compute costs per hypothesis/analysis, budgeting, and cost optimization. The system tracks Bedrock token usage (input/output), API calls to external services (PubMed, Semantic Scholar), and estimates costs based on current pricing ($3/1M input tokens, $15/1M output tokens for Sonnet; $0.25/1M input, $1.25/1M output for Haiku).
resource_usage table with columns: id, entity_type, entity_id, resource_type, amount, model_id, cost_usd_estimate, created_atresource_budgets table with columns: entity_type, entity_id, token_budget, budget_period, allocated_atresource_usage and resource_budgets tables via SQL migration (migrations/add_resource_tracking.sql)v_tokens_per_analysis, v_tokens_per_hypothesis, v_cost_per_dayresource_tracker.py module with functions:log_llm_usage() - tracks Bedrock input/output tokens with cost estimateslog_api_call() - tracks external API calls (PubMed, Semantic Scholar, etc.)get_resource_summary() - aggregates resource usage by entityget_analysis_stats(), get_hypothesis_stats() - queries views for statsget_daily_costs() - daily cost breakdown
scidex_orchestrator.py:import resource_trackercall_claude() signature to accept entity_type and entity_idtest_resource_tracking.py - all tests pass ✅apply_resource_migration.pylog_tool_call decoratorPrior iteration left resource_tracker imported in the orchestrator but never called. This run completed the wiring:
record_usage() in scidex/core/resource_tracker.py: removed explicit id from INSERT (it's an auto-increment integer in PG; passing a string like ru_<hex> caused silent failures). Removed ON CONFLICT(id) clause.reserve_gpu_job(): same id bug fixed.COST_RATES dict with per-million-token pricing for Sonnet, Haiku, Opus._model_cost_rates() helper to normalize model id strings (handles us.anthropic.claude-sonnet-4-6 → Sonnet rates).meter_llm_call(): inserts two rows (input tokens + output tokens) with cost estimates.meter_api_call(): inserts an api_call row for tool invocations.call_claude() in orchestrator: calls resource_tracker.meter_llm_call() after each Bedrock response (both in tool-use loop and final call). Uses entity_type/entity_id params or falls back to self.current_analysis_id.execute_tool() in orchestrator: calls resource_tracker.meter_api_call() on successful tool execution.record_usage, meter_llm_call, meter_api_call all insert to DB; v_tokens_per_analysis and v_cost_per_day views aggregate correctly.resource_usage; views v_tokens_per_analysis, v_tokens_per_hypothesis, v_cost_per_day, resource_usage_by_day all operational.