argus-code-review 0.3.4__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/CHANGELOG.md +15 -1
  2. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/PKG-INFO +1 -1
  3. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/gemini_runner.py +17 -9
  4. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/graph.py +21 -14
  5. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_gemini_runner.py +30 -0
  6. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_graph_get_llm_temperature.py +9 -8
  7. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_multi_round.py +43 -0
  8. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/.gitignore +0 -0
  9. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/LICENSE +0 -0
  10. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/README.md +0 -0
  11. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/__init__.py +0 -0
  12. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/bench.py +0 -0
  13. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/bench_default.toml +0 -0
  14. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/cli.py +0 -0
  15. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/config.py +0 -0
  16. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/coverage.py +0 -0
  17. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/dotenv_utils.py +0 -0
  18. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/gemini_cache.py +0 -0
  19. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/github_client.py +0 -0
  20. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/helpers.py +0 -0
  21. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/llm/models.py +0 -0
  22. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/llm/output_models.py +0 -0
  23. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/llm/pricing.py +0 -0
  24. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/llm/usage.py +0 -0
  25. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/models.py +0 -0
  26. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/openai_client.py +0 -0
  27. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/openai_runner.py +0 -0
  28. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/pipeline_models.py +0 -0
  29. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/__init__.py +0 -0
  30. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/actions_scanner.py +0 -0
  31. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/engine.py +0 -0
  32. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/eslint_bundle/.gitignore +0 -0
  33. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/eslint_bundle/README.md +0 -0
  34. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/eslint_bundle/eslint.config.js +0 -0
  35. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/eslint_bundle/package-lock.json +0 -0
  36. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/eslint_bundle/package.json +0 -0
  37. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/js_scanner.py +0 -0
  38. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/migration_scanner.py +0 -0
  39. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/rules/README.md +0 -0
  40. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/sarif.py +0 -0
  41. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/scanner_utils.py +0 -0
  42. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/secrets_scanner.py +0 -0
  43. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/shadow.py +0 -0
  44. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/terraform_scanner.py +0 -0
  45. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/precheck/workflow_lint_scanner.py +0 -0
  46. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/__init__.py +0 -0
  47. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-blocking-validator.md +0 -0
  48. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-coverage-check.md +0 -0
  49. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-cross-cutting.md +0 -0
  50. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-feedback-verifier.md +0 -0
  51. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-lite.md +0 -0
  52. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-planner.md +0 -0
  53. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-preflight-router.md +0 -0
  54. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-prior-art.md +0 -0
  55. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-scope.md +0 -0
  56. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-deployment.md +0 -0
  57. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-frontend.md +0 -0
  58. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-infra.md +0 -0
  59. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-llm-patterns.md +0 -0
  60. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-observability.md +0 -0
  61. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-orchestration.md +0 -0
  62. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-security.md +0 -0
  63. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-slackbot.md +0 -0
  64. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-specialist-sql.md +0 -0
  65. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-subagent.md +0 -0
  66. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-tests-and-docs.md +0 -0
  67. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts/pr-review-writer.md +0 -0
  68. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/prompts_runtime.py +0 -0
  69. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/repo_provision.py +0 -0
  70. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/review_tools.py +0 -0
  71. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/runners.py +0 -0
  72. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/scope.py +0 -0
  73. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/storage/__init__.py +0 -0
  74. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/storage/http.py +0 -0
  75. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/storage/models.py +0 -0
  76. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/storage/precheck.py +0 -0
  77. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/storage/resolver.py +0 -0
  78. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/storage/session.py +0 -0
  79. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/storage/sql.py +0 -0
  80. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/argus/storage/sqlite.py +0 -0
  81. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/pyproject.toml +0 -0
  82. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/schema/008_add_code_reviews.sql +0 -0
  83. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/schema/009_add_reviewer_version.sql +0 -0
  84. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/schema/010_add_review_patterns.sql +0 -0
  85. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/schema/011_add_review_progress_columns.sql +0 -0
  86. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/schema/015_create_agent_runs.sql +0 -0
  87. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/schema/016_add_agent_runs_failure_reason.sql +0 -0
  88. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/schema/017_add_precheck_rules.sql +0 -0
  89. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/schema/018_widen_agent_runs_failure_reason.sql +0 -0
  90. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/__init__.py +0 -0
  91. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/conftest.py +0 -0
  92. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/golden/review_response.schema.json +0 -0
  93. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/storage/__init__.py +0 -0
  94. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/storage/test_backend_contract.py +0 -0
  95. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/storage/test_http.py +0 -0
  96. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/storage/test_models.py +0 -0
  97. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/storage/test_resolver.py +0 -0
  98. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/storage/test_session.py +0 -0
  99. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/storage/test_sql.py +0 -0
  100. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/storage/test_sqlite_backend.py +0 -0
  101. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_actions_scanner.py +0 -0
  102. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_argus_review_local.py +0 -0
  103. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_bench.py +0 -0
  104. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_bench_config_guard.py +0 -0
  105. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_catchup_gate.py +0 -0
  106. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_cli_args.py +0 -0
  107. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_cli_output_contract.py +0 -0
  108. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_cli_post_review.py +0 -0
  109. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_cli_preflight.py +0 -0
  110. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_cli_prompts.py +0 -0
  111. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_config.py +0 -0
  112. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_conftest.py +0 -0
  113. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_dotenv_utils.py +0 -0
  114. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_failure_diagnostics.py +0 -0
  115. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_gemini_cache.py +0 -0
  116. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_gemini_http_retry.py +0 -0
  117. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_github_client_checks_signal.py +0 -0
  118. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_github_client_write.py +0 -0
  119. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_graph_fetch_diff.py +0 -0
  120. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_graph_http_guards.py +0 -0
  121. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_graph_precheck.py +0 -0
  122. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_graph_preflight_image_bump.py +0 -0
  123. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_graph_progress.py +0 -0
  124. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_graph_storage_resolution.py +0 -0
  125. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_graph_timeout_surfacing.py +0 -0
  126. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_js_scanner.py +0 -0
  127. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_llm_models.py +0 -0
  128. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_llm_models_override.py +0 -0
  129. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_llm_pricing.py +0 -0
  130. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_migration_scanner.py +0 -0
  131. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_models.py +0 -0
  132. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_openai_client.py +0 -0
  133. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_openai_runner.py +0 -0
  134. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_output_models.py +0 -0
  135. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_packaging.py +0 -0
  136. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_plan_review.py +0 -0
  137. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_precheck_engine.py +0 -0
  138. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_precheck_engine_integration.py +0 -0
  139. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_precheck_sarif.py +0 -0
  140. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_precheck_shadow.py +0 -0
  141. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_precheck_shadow_integration.py +0 -0
  142. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_prompts.py +0 -0
  143. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_repo_provision.py +0 -0
  144. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_review_patterns_integration.py +0 -0
  145. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_review_tools.py +0 -0
  146. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_runners_context7.py +0 -0
  147. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_runners_context_usage.py +0 -0
  148. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_runners_helpers.py +0 -0
  149. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_runners_new.py +0 -0
  150. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_runners_turn_budget.py +0 -0
  151. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_scanner_utils.py +0 -0
  152. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_scope.py +0 -0
  153. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_scope_gate.py +0 -0
  154. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_sdk_bundled_cli_version.py +0 -0
  155. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_secrets_scanner.py +0 -0
  156. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_specialist_validation.py +0 -0
  157. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_stage_costs.py +0 -0
  158. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_storage_precheck.py +0 -0
  159. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_structured_output_methods.py +0 -0
  160. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_terraform_scanner.py +0 -0
  161. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_terraform_scanner_integration.py +0 -0
  162. {argus_code_review-0.3.4 → argus_code_review-0.4.1}/tests/test_workflow_lint_scanner.py +0 -0
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.1] - 2026-10-28
11
+
12
+ ### Fixed
13
+
14
+ - Rounds that carry dismissals no longer abort with `temperature: Extra inputs are not permitted` (HTTP 400). The `dismiss_match` call (the small Claude Haiku call that maps `--dismiss` text to prior findings) no longer sends `temperature`: with the current `langchain-anthropic`, structured output rejects it, and a failure there aborts the whole review. First-round reviews were unaffected.
15
+
16
+ ## [0.4.0] - 2026-10-28
17
+
18
+ ### Changed
19
+
20
+ - Gemini `caching = "auto"` (the packaged default for the bulk reviewers) no longer creates an explicit context cache; only `"on"` does. Explicit caching held just the system prompt and tools (about 1-3k tokens) and Gemini then reported only that slice as cached on every turn, while the growing conversation (the bulk of each request) was billed uncached. With no explicit cache, Gemini's automatic prefix caching applies to the whole repeated prefix. In paired live runs of the same PR the reported cached share of prompt tokens went from about 7% to about 90% (total prompt tokens about equal, verdicts and finding counts equivalent), and Argus's own cost estimate for the review fell from $12.66 to $3.20. That estimate is computed from the reported `cached_content_token_count`, not from a billing export, and Google does not document how automatic cache hits interact with `cached_content`, so treat the savings as unconfirmed. Because the default behavior of `auto` changes, this ships as a minor release (0.4.0). `caching = "on"` keeps the previous explicit-cache behavior.
21
+
10
22
  ## [0.3.4] - 2026-10-23
11
23
 
12
24
  ### Fixed
@@ -457,7 +469,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
457
469
  packaged set.
458
470
  - `argus --version`, `argus prompts list`, and `argus prompts export`.
459
471
 
460
- [Unreleased]: https://github.com/redesignhealth/argus-review/compare/v0.3.4...HEAD
472
+ [Unreleased]: https://github.com/redesignhealth/argus-review/compare/v0.4.1...HEAD
473
+ [0.4.1]: https://github.com/redesignhealth/argus-review/compare/v0.4.0...v0.4.1
474
+ [0.4.0]: https://github.com/redesignhealth/argus-review/compare/v0.3.4...v0.4.0
461
475
  [0.3.4]: https://github.com/redesignhealth/argus-review/compare/v0.3.3...v0.3.4
462
476
  [0.3.3]: https://github.com/redesignhealth/argus-review/compare/v0.3.2...v0.3.3
463
477
  [0.3.2]: https://github.com/redesignhealth/argus-review/compare/v0.3.1...v0.3.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: argus-code-review
3
- Version: 0.3.4
3
+ Version: 0.4.1
4
4
  Summary: Self-orchestrated PR review agent using LangGraph + Claude Agent SDK
5
5
  Project-URL: Repository, https://github.com/redesignhealth/argus-review
6
6
  Project-URL: Issues, https://github.com/redesignhealth/argus-review/issues
@@ -54,7 +54,7 @@ key so that function's own ``group_name`` fallback still applies
54
54
  (this runner has no access to the caller's ``group.name`` -- only to
55
55
  ``entry.role``, which is a different, bench-level identifier).
56
56
 
57
- Explicit context caching (``entry.caching in {"auto", "on"}``) is
57
+ Explicit context caching (``entry.caching == "on"``) is
58
58
  strictly best-effort: Gemini's explicit-cache API has a minimum
59
59
  content-size requirement a short prompt plus this small a toolset might
60
60
  not clear, so any exception raised while creating/reusing a cache is
@@ -177,14 +177,22 @@ logger = logging.getLogger(__name__)
177
177
 
178
178
  _MAX_TURNS_GEMINI = 100 # raised from 45 -- see TECH-6558
179
179
 
180
- # Both "auto" and "on" attempt explicit caching today -- there is no
181
- # separate heuristic distinguishing them yet (Track 1 defined the
182
- # `caching` values without one; see argus.bench's `_VALID_CACHING`). The
183
- # graceful-degradation try/except in `_maybe_activate_cache` is what
184
- # actually protects a caller who set "auto" against a too-small
185
- # prompt+toolset that can't clear Gemini's minimum cache-content-size
186
- # requirement. "off" skips caching entirely.
187
- _CACHE_ENABLED_VALUES = frozenset({"auto", "on"})
180
+ # Only "on" creates an explicit cache. "auto" (the packaged default) and "off"
181
+ # both send the whole request inline and leave caching to Gemini's own
182
+ # automatic prefix caching, which is enabled by default on current models.
183
+ #
184
+ # Why "auto" is not explicit: an explicit cache holds only what is placed in
185
+ # it at creation time (here the system prompt, tools and tool config, about
186
+ # 1-3k tokens), and a request that references it ("cached_content=") must
187
+ # not also carry those three fields. Every turn's growing conversation
188
+ # (the diff plus all prior tool results) is then sent as ordinary
189
+ # `contents`. In live A/B runs of the same PR,
190
+ # explicit caching reported ~7% cached prompt tokens (exactly the cache size
191
+ # on every turn) while caching="off" reported ~90%, with the same total
192
+ # prompt volume. The graceful-degradation try/except in
193
+ # `_maybe_activate_cache` still protects a caller who sets "on" against a
194
+ # prompt+toolset that cannot clear Gemini's minimum cache size.
195
+ _CACHE_ENABLED_VALUES = frozenset({"on"})
188
196
 
189
197
  _DEFAULT_READ_LIMIT = 2000 # mirrors argus.review_tools._DEFAULT_READ_LIMIT
190
198
 
@@ -522,8 +522,12 @@ async def _apply_dismissals(
522
522
  "or -1 if it doesn't match any finding."
523
523
  )
524
524
 
525
+ # No `temperature`: with langchain-anthropic 1.7.7 (anthropic 2.3.0), structured output on this
526
+ # call sends it in a way the Anthropic API rejects ("temperature: Extra inputs are not
527
+ # permitted", HTTP 400), and a failure here aborts the whole review. The matching task
528
+ # is deterministic enough without it.
525
529
  llm = _get_llm(
526
- f"anthropic:{CLAUDE_MINI}", "dismiss_match", max_tokens=1024, temperature=0
530
+ f"anthropic:{CLAUDE_MINI}", "dismiss_match", max_tokens=1024
527
531
  ).with_structured_output(DismissMatches)
528
532
  result = await llm.ainvoke([{"role": "user", "content": prompt}])
529
533
 
@@ -1037,7 +1041,8 @@ _TEMPERATURE_UNSUPPORTED_MODELS: frozenset[str] = (
1037
1041
  # set on the assumption Anthropic's temperature deprecation
1038
1042
  # applies across the Sonnet line, not per-checkpoint; re-verify
1039
1043
  # empirically and correct this comment if that assumption turns
1040
- # out wrong. claude-haiku-4-5 (CLAUDE_MINI) accepts it fine, so
1044
+ # out wrong. claude-haiku-4-5 (CLAUDE_MINI) accepts it fine on plain
1045
+ # requests (not with structured output, see _apply_dismissals), so
1041
1046
  # is deliberately not included here -- see the explicit
1042
1047
  # exclusion below for why "not included" alone isn't enough.
1043
1048
  ALIAS_MAP["claude-frontier"],
@@ -1046,18 +1051,20 @@ _TEMPERATURE_UNSUPPORTED_MODELS: frozenset[str] = (
1046
1051
  CLAUDE_DEFAULT,
1047
1052
  }
1048
1053
  )
1049
- # Explicit exclusion, not just an absent literal: without this, an
1050
- # override that happens to land CLAUDE_DEFAULT/CLAUDE_FRONTIER on
1051
- # CLAUDE_MINI's own resolved value (e.g. `--specialist-model
1052
- # claude-haiku-4-5`, used directly by this suite's own tests) would pull
1053
- # that value into the union above via the CLAUDE_DEFAULT/CLAUDE_FRONTIER
1054
- # terms, and then _apply_dismissals's `_get_llm(f"anthropic:{CLAUDE_MINI}",
1055
- # "dismiss_match", temperature=0)` call would have ITS temperature silently stripped too
1056
- # -- a real, silent determinism regression in a call site that has
1057
- # nothing to do with the override, caught in Argus round 3 review of
1058
- # this PR. `- {CLAUDE_MINI}` guarantees CLAUDE_MINI's resolved value can
1059
- # never end up in this set regardless of what any other alias happens to
1060
- # resolve to.
1054
+ # Explicit exclusion, not just an absent literal: without this, an override that
1055
+ # happens to land CLAUDE_DEFAULT/CLAUDE_FRONTIER on CLAUDE_MINI's own resolved value
1056
+ # (e.g. `--specialist-model claude-haiku-4-5`, used directly by this suite's own
1057
+ # tests) would pull that value into the union above via the
1058
+ # CLAUDE_DEFAULT/CLAUDE_FRONTIER terms, silently stripping `temperature` from any plain
1059
+ # `_get_llm(f"anthropic:{CLAUDE_MINI}", ..., temperature=...)` call that has nothing
1060
+ # to do with the override. `- {CLAUDE_MINI}` guarantees CLAUDE_MINI's resolved value
1061
+ # can never end up in this set.
1062
+ #
1063
+ # `_apply_dismissals`, the former user of such a call, no longer passes `temperature`
1064
+ # at all: structured output on Haiku is rejected with langchain-anthropic 1.7.7
1065
+ # (HTTP 400, "temperature: Extra inputs are not permitted"). `langchain-anthropic` is
1066
+ # unpinned above 1.2.0, so re-check this before relying on `temperature` together
1067
+ # with structured output.
1061
1068
  - {CLAUDE_MINI}
1062
1069
  )
1063
1070
 
@@ -606,6 +606,36 @@ class TestCacheActivation:
606
606
  assert request_config.tools is None
607
607
  assert request_config.tool_config is None
608
608
 
609
+ async def test_cache_auto_never_calls_caches_create(self) -> None:
610
+ # "auto" leaves caching to Gemini's automatic prefix caching: it must
611
+ # NOT create or reference an explicit cache, because a request that
612
+ # references one carries only the cache's static prefix and the
613
+ # growing conversation is then billed uncached.
614
+ entry = _make_entry(caching="auto")
615
+ settings = _make_settings()
616
+
617
+ response = _make_response(calls=[("finish_review", {"files_explored": []})])
618
+ fake_client = _make_fake_client([response], cache_name="cachedContents/should-not-be-used")
619
+
620
+ with patch("argus.gemini_runner.genai.Client", return_value=fake_client):
621
+ await run_session_gemini(
622
+ entry=entry,
623
+ system_prompt="sys",
624
+ user_message="msg",
625
+ settings=settings,
626
+ repo_root="/tmp/does-not-need-to-exist",
627
+ )
628
+
629
+ fake_client.caches.create.assert_not_called()
630
+ generate_kwargs = fake_client.aio.models.generate_content.await_args.kwargs
631
+ assert generate_kwargs["config"].system_instruction == _with_turn_budget_line("sys")
632
+ assert generate_kwargs["config"].cached_content is None
633
+ # With no cache to carry them, tools and the forced tool_config must be
634
+ # sent inline on the request.
635
+ assert generate_kwargs["config"].tools is not None
636
+ assert generate_kwargs["config"].tool_config is not None
637
+ assert generate_kwargs["config"].tool_config.function_calling_config.mode == "ANY"
638
+
609
639
  async def test_cache_off_never_calls_caches_create(self) -> None:
610
640
  entry = _make_entry(caching="off")
611
641
  settings = _make_settings()
@@ -188,14 +188,15 @@ def test_temperature_guard_does_not_swallow_claude_mini_when_specialist_model_co
188
188
  was over-inclusive. Setting ARGUS_SPECIALIST_MODEL=claude-haiku-4-5 makes
189
189
  CLAUDE_DEFAULT resolve to the SAME value as CLAUDE_MINI, pulling
190
190
  CLAUDE_MINI's own resolved value into _TEMPERATURE_UNSUPPORTED_MODELS via
191
- the CLAUDE_DEFAULT term of the union. _match_dismissals
192
- (argus/graph.py) calls `_get_llm(f"anthropic:{CLAUDE_MINI}",
193
- temperature=0)` for deterministic dismissal-index matching -- that
194
- temperature=0 would then be silently stripped, breaking the exact
195
- invariant this set's own comment documents ("claude-haiku-4-5
196
- (CLAUDE_MINI) accepts it fine, so is deliberately not included here").
197
- The explicit `- {CLAUDE_MINI}` exclusion in the set's construction is
198
- what prevents this.
191
+ the CLAUDE_DEFAULT term of the union. A plain
192
+ `_get_llm(f"anthropic:{CLAUDE_MINI}", ..., temperature=0)` call (this was
193
+ the dismissal-index matcher, which no longer passes temperature because
194
+ Haiku structured output rejects it) would then have its temperature
195
+ silently stripped, breaking the invariant this set's own comment
196
+ documents ("claude-haiku-4-5 (CLAUDE_MINI) accepts it fine on plain
197
+ requests, so is deliberately not included here"). The explicit
198
+ `- {CLAUDE_MINI}` exclusion in the set's construction is what prevents
199
+ this.
199
200
  """
200
201
  overridden_set = _temperature_unsupported_models_under_override(CLAUDE_MINI)
201
202
  assert CLAUDE_MINI not in overridden_set
@@ -7,6 +7,7 @@ round 2 writer messages, and model contracts.
7
7
  from __future__ import annotations
8
8
 
9
9
  import importlib
10
+ import inspect
10
11
  import json
11
12
  import sys
12
13
  from unittest.mock import AsyncMock, MagicMock, patch
@@ -1174,6 +1175,48 @@ class TestApplyDismissals:
1174
1175
  assert matched[0].description == "except Exception"
1175
1176
  assert matched[0].dismissed_by == "user1"
1176
1177
 
1178
+ @pytest.mark.asyncio
1179
+ async def test_dismiss_match_call_sends_no_temperature(self) -> None:
1180
+ """The dismiss_match LLM call must not pass `temperature`.
1181
+
1182
+ With structured output, the current langchain-anthropic request is rejected by the
1183
+ Anthropic API when a temperature is set ("temperature: Extra inputs are not
1184
+ permitted", HTTP 400), and a failure here aborts the whole review.
1185
+ """
1186
+ _reload_graph()
1187
+ from argus.graph import _apply_dismissals
1188
+ from argus.graph import _get_llm as _get_llm_fn
1189
+
1190
+ from pydantic import BaseModel
1191
+
1192
+ class DismissMatch(BaseModel):
1193
+ dismiss_index: int
1194
+ finding_index: int
1195
+
1196
+ class DismissMatches(BaseModel):
1197
+ matches: list[DismissMatch]
1198
+
1199
+ findings = [PriorFinding(severity="BLOCKING", description="issue")]
1200
+ dismissals = [DismissedFinding(description="issue", dismissed_by="u", reason="r")]
1201
+
1202
+ mock_structured = MagicMock()
1203
+ mock_structured.ainvoke = AsyncMock(
1204
+ return_value=DismissMatches(matches=[DismissMatch(dismiss_index=0, finding_index=0)])
1205
+ )
1206
+ mock_llm = MagicMock()
1207
+ mock_llm.with_structured_output = MagicMock(return_value=mock_structured)
1208
+
1209
+ with patch("argus.graph._get_llm", return_value=mock_llm) as mock_get_llm:
1210
+ await _apply_dismissals(findings, dismissals)
1211
+
1212
+ mock_get_llm.assert_called_once()
1213
+ mock_llm.with_structured_output.assert_called_once()
1214
+ bound = inspect.signature(_get_llm_fn).bind(
1215
+ *mock_get_llm.call_args.args, **mock_get_llm.call_args.kwargs
1216
+ )
1217
+ assert bound.arguments["stage"] == "dismiss_match"
1218
+ assert "temperature" not in bound.arguments
1219
+
1177
1220
  @pytest.mark.asyncio
1178
1221
  async def test_out_of_range_indices_ignored(self) -> None:
1179
1222
  """Matches with out-of-range indices are silently skipped."""