Commit 962ab5b
fix: comprehensive bug-hunt sweep (cost, async, storage, providers, MCP)
Multi-scout audit found ~75 issues across security, cost/budget,
async/concurrency, storage durability, and provider correctness.
This commit closes the critical + high tier and most mediums.
Critical (cost/security)
- MCP confirmation gate stripped `_approved` kwarg before dispatch;
previously every approved deepr_research / deepr_agentic_research
call raised TypeError and returned a generic 500 (v2.10.2 regression).
- CostSafetyManager.ABSOLUTE_MAX_{PER_OPERATION,DAILY,MONTHLY} added —
the constants mcp/server.py and cli/budget.py referenced did not exist,
so every deepr_agentic_research silently swallowed AttributeError.
- POST /api/jobs now enforces CostController.check_cost_limit before
provider submission; services/research_api.py and services/batch_executor
added cost-safety gates; worker/poller records cost on completion.
- experts/chat.py _deep_research used $0.20 hardcoded estimate while
o4-mini-deep-research is ~$2.00 — sessions blew daily budget 10x
faster than tracked. Multi-round tool loops now re-check
cost_session.can_proceed between rounds. _quick_lookup gets a
pre-flight budget check.
- experts/cost_safety.py: threading.Lock + reservation pattern stops
N parallel council/task-planner callers from over-committing against
the same daily cap. New check_and_reserve / refund_reservation /
record_cost(reservation_id=...) settle paths.
Critical (durability)
- 17+ files migrated to deepr/utils/atomic_io.atomic_write_json
(profile_store, beliefs, memory, traces, embeddings cache,
dspy_pipeline, knowledge_consolidation, metacognition,
temporal_knowledge, user_profile, confidence_calibration,
lazy_graph_rag, observability/{metadata,costs}, storage/local,
web budget limits). The mkstemp + Windows-retry pattern from
commit 4715021 is now a shared helper.
- cost_ledger.jsonl appends now flush + fsync; routing_log uses
append_jsonl_durable so crash-loss can't truncate the last record.
- queue.update_results writes the cost-ledger event BEFORE the
SQLite commit so a crash between the two can't leave the queue
showing "completed with cost" while the canonical ledger has no row.
- SQLite queue runs in WAL mode; partial UNIQUE(provider_job_id)
index prevents double-billing on submit-retry races.
Critical (async)
- experts/task_planner.py: parallel _run_step coroutines on the same
ExpertChatSession now serialise through asyncio.Lock instead of
racing on self.messages / cost_accumulated / research_count.
- mcp/state/async_dispatcher.py: dependency wait moved OUTSIDE the
concurrency semaphore so chains of length > max_concurrent can no
longer deadlock on slot-exhaustion.
Provider correctness
- providers/anthropic_provider.py: per-turn usage now accumulated and
stored in self._jobs; get_status returns the real ResearchResponse
including cost. Previously every Anthropic call returned $0 and no
report — the provider was completely invisible to the cost ledger.
- providers/registry.py: get_token_pricing normalises dot/hyphen
(Grok 4.20 ~80% undercharge fixed) + applies aliases + sorts
partial-match candidates by length (Flash-Lite vs Flash overcharge).
- providers/openai_provider.py: rate-limit fallback now uses
dataclasses.replace instead of mutating caller's request; log a
warning when response.model is missing rather than silently
defaulting to o4-mini pricing for an o3-deep-research job.
- providers/gemini_provider.py: token counts read from
chunk.usage_metadata (prompt + candidates + thoughts) instead of
len(text)//4. Poll error handler whitelists transient errors and
promotes 401/403/404 to job failure so file_search stores don't leak.
- providers/azure_provider.py: 3-retry on RateLimit/Connection/Timeout
matching OpenAI provider; None-model defaulted before calculate_cost.
- providers/grok_provider.py: multi-agent budget pre-flight bumped to
16K worst-case output tokens; submit_research no longer re-raises
mid-call (matches Gemini contract — caller gets job_id back).
- experts/chat.py _chat_token_cost honours OpenAI's 50% cached-token
discount via prompt_tokens_details.cached_tokens.
- routing/deprecation.py strips provider prefix from migrated successor
so downstream provider clients consume plain "grok-4-3" not "xai/...".
- routing/auto_mode.py _cheapest_available no longer hard-codes
openai/gpt-4.1-mini as last resort; iterates usable providers or
raises RuntimeError honestly when none are configured.
Other security/hardening
- mcp/transport/http.py wraps hmac.compare_digest in TypeError catch
(the fix that landed in web/app.py + api/app.py in v2.10.2 was
missed here). SSE subscriber lifecycle: replacing subscriber_id
signals the old handler to exit, no more zombie streams.
- mcp/transport/stdio.py read loop dispatches handler coroutines via
asyncio.create_task so an in-flight deepr_research can't block
cancellations or other tool calls.
- mcp/client/circuit_breaker.py is_available HALF_OPEN check + claim
now under threading.Lock.
- mcp/security/tool_allowlist.py: 6 missing tools registered
(deepr_get_task_progress, deepr_list_recoverable_tasks,
deepr_resume_task, deepr_pause_task, deepr_list_skills,
deepr_install_skill).
- deploy/aws/src/worker/worker.py: JOBS_TABLE added to startup env
validation; update_job_status uses ConditionExpression so a late
completion/failed write can't overwrite a cancelled state.
- experts/profile_store.py list_all logs corruption at ERROR (not
WARNING) and exposes .errors attribute so admin UI can surface
hidden experts. ExpertStore.save uses atomic write.
- experts/beliefs.py _load catches JSONDecodeError + 50 MB size cap
guards against poisoned files; starts fresh on corruption instead
of crashing expert load.
- web/app.py: /api/experts/chat clamps caller-supplied budget against
daily cap; /api/experts/council adds 5/min limit + ABSOLUTE_MAX
budget clamp; citation-validation cache fill is now serialised
per-expert so concurrent uncached requests don't fan out paid
LLM batches; _save_limits surfaces write failures.
- api/app.py: submit_job cost guard narrows broad except (was
silently bypassing the budget check on any exception); swagger
docs no longer claim the API is unauthenticated.
- experts/chat.py session_id sanitises caller-supplied expert.name
and agent_identity.agent_id via [^\w\-] regex.
- experts/council.py: full-budget upfront reservation against the
global cost-safety manager prevents N-way fan-out over-commit
beyond the daily cap.
Tests + coverage
- 14 new test files (~80 new test cases) covering atomic_io,
cost-safety reservations + absolute ceilings, MCP _approved
stripping, MCP HTTP bearer TypeError, dispatcher dependency
deadlock fix, Grok dot/hyphen alias normalisation, OpenAI
fallback request immutability, Anthropic usage accumulation,
profile_store corruption surfacing, chat _chat_token_cost
(including cache-discount), findings store, user profile
tracker, information gain tracker.
- pyproject.toml coverage fail_under raised from 60% to 75%
(current coverage: 78.5%). Extended omit list to exclude
integration-tested surfaces (WebSocket events, CLI display,
Playwright browser, web scraper, optional DSPy integration).
- All 4401 unit tests pass. ruff check + format clean on deepr/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent e2dd9b2 commit 962ab5b
85 files changed
Lines changed: 2721 additions & 578 deletions
File tree
- deepr
- api
- experts
- mcp
- client
- security
- state
- transport
- observability
- providers
- queue
- routing
- services
- storage
- utils
- web
- worker
- deploy/aws/src/worker
- tests
- integration
- property
- test_a2a
- test_experts
- test_mcp_client
- test_mcp_provider
- test_skills
- unit
- test_a2a
- test_agents
- test_experts
- test_mcp_client
- test_mcp
- test_observability
- test_providers
- test_routing
- test_skill
- test_storage
- test_utils
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
118 | | - | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
119 | 124 | | |
120 | 125 | | |
121 | 126 | | |
| |||
621 | 626 | | |
622 | 627 | | |
623 | 628 | | |
624 | | - | |
625 | | - | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
626 | 641 | | |
627 | 642 | | |
628 | 643 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| 32 | + | |
| 33 | + | |
31 | 34 | | |
32 | 35 | | |
33 | 36 | | |
| |||
629 | 632 | | |
630 | 633 | | |
631 | 634 | | |
632 | | - | |
633 | | - | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
634 | 638 | | |
635 | 639 | | |
636 | | - | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
637 | 646 | | |
638 | 647 | | |
639 | 648 | | |
640 | | - | |
641 | | - | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
642 | 661 | | |
643 | 662 | | |
644 | 663 | | |
| |||
891 | 910 | | |
892 | 911 | | |
893 | 912 | | |
894 | | - | |
895 | | - | |
| 913 | + | |
| 914 | + | |
| 915 | + | |
896 | 916 | | |
897 | 917 | | |
898 | 918 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
| |||
58 | 59 | | |
59 | 60 | | |
60 | 61 | | |
61 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
62 | 72 | | |
63 | 73 | | |
64 | 74 | | |
| |||
125 | 135 | | |
126 | 136 | | |
127 | 137 | | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
128 | 146 | | |
129 | | - | |
| 147 | + | |
| 148 | + | |
130 | 149 | | |
131 | | - | |
| 150 | + | |
132 | 151 | | |
133 | 152 | | |
134 | 153 | | |
| |||
610 | 629 | | |
611 | 630 | | |
612 | 631 | | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
613 | 647 | | |
614 | 648 | | |
615 | 649 | | |
| |||
626 | 660 | | |
627 | 661 | | |
628 | 662 | | |
629 | | - | |
630 | | - | |
631 | | - | |
632 | | - | |
633 | | - | |
634 | | - | |
635 | | - | |
636 | | - | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
637 | 676 | | |
638 | 677 | | |
639 | 678 | | |
| |||
694 | 733 | | |
695 | 734 | | |
696 | 735 | | |
697 | | - | |
698 | | - | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
699 | 744 | | |
700 | 745 | | |
701 | 746 | | |
| |||
751 | 796 | | |
752 | 797 | | |
753 | 798 | | |
754 | | - | |
755 | | - | |
756 | | - | |
757 | | - | |
758 | | - | |
| 799 | + | |
759 | 800 | | |
760 | 801 | | |
761 | 802 | | |
| |||
784 | 825 | | |
785 | 826 | | |
786 | 827 | | |
787 | | - | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
788 | 838 | | |
789 | 839 | | |
790 | 840 | | |
| |||
1293 | 1343 | | |
1294 | 1344 | | |
1295 | 1345 | | |
| 1346 | + | |
| 1347 | + | |
| 1348 | + | |
| 1349 | + | |
| 1350 | + | |
| 1351 | + | |
| 1352 | + | |
1296 | 1353 | | |
1297 | 1354 | | |
1298 | 1355 | | |
1299 | 1356 | | |
| 1357 | + | |
| 1358 | + | |
| 1359 | + | |
| 1360 | + | |
| 1361 | + | |
| 1362 | + | |
| 1363 | + | |
| 1364 | + | |
| 1365 | + | |
| 1366 | + | |
| 1367 | + | |
| 1368 | + | |
| 1369 | + | |
| 1370 | + | |
| 1371 | + | |
| 1372 | + | |
| 1373 | + | |
| 1374 | + | |
1300 | 1375 | | |
1301 | 1376 | | |
1302 | 1377 | | |
| |||
1881 | 1956 | | |
1882 | 1957 | | |
1883 | 1958 | | |
| 1959 | + | |
| 1960 | + | |
| 1961 | + | |
1884 | 1962 | | |
1885 | 1963 | | |
1886 | 1964 | | |
| 1965 | + | |
| 1966 | + | |
| 1967 | + | |
| 1968 | + | |
| 1969 | + | |
| 1970 | + | |
| 1971 | + | |
| 1972 | + | |
| 1973 | + | |
| 1974 | + | |
| 1975 | + | |
| 1976 | + | |
| 1977 | + | |
1887 | 1978 | | |
1888 | 1979 | | |
1889 | 1980 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
366 | 366 | | |
367 | 367 | | |
368 | 368 | | |
369 | | - | |
370 | | - | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
371 | 372 | | |
372 | 373 | | |
373 | 374 | | |
| |||
0 commit comments