argus-code-review 0.2.5__tar.gz → 0.2.7__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.
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/CHANGELOG.md +33 -1
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/PKG-INFO +2 -2
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/graph.py +5 -1
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/pyproject.toml +1 -1
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/storage/test_backend_contract.py +98 -16
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/storage/test_http.py +68 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_openai_client.py +38 -0
- argus_code_review-0.2.7/tests/test_output_models.py +188 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/.gitignore +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/LICENSE +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/README.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/__init__.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/bench.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/bench_default.toml +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/cli.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/config.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/coverage.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/dotenv_utils.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/gemini_cache.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/gemini_runner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/github_client.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/helpers.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/llm/models.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/llm/output_models.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/llm/pricing.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/llm/usage.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/models.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/openai_client.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/openai_runner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/pipeline_models.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/__init__.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/actions_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/engine.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/.gitignore +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/README.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/eslint.config.js +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/package-lock.json +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/package.json +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/js_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/migration_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/rules/README.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/sarif.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/scanner_utils.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/secrets_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/shadow.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/terraform_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/workflow_lint_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/__init__.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-blocking-validator.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-coverage-check.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-cross-cutting.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-feedback-verifier.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-lite.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-planner.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-preflight-router.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-prior-art.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-deployment.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-frontend.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-infra.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-llm-patterns.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-observability.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-orchestration.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-security.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-slackbot.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-sql.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-subagent.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-tests-and-docs.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-writer.md +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts_runtime.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/repo_provision.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/review_tools.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/runners.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/storage/__init__.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/storage/http.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/storage/models.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/storage/precheck.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/storage/resolver.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/storage/session.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/storage/sql.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/storage/sqlite.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/008_add_code_reviews.sql +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/009_add_reviewer_version.sql +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/010_add_review_patterns.sql +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/011_add_review_progress_columns.sql +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/015_create_agent_runs.sql +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/016_add_agent_runs_failure_reason.sql +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/017_add_precheck_rules.sql +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/018_widen_agent_runs_failure_reason.sql +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/__init__.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/conftest.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/golden/review_response.schema.json +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/storage/__init__.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/storage/test_models.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/storage/test_resolver.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/storage/test_session.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/storage/test_sql.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/storage/test_sqlite_backend.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_actions_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_argus_review_local.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_bench.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_bench_config_guard.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_catchup_gate.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_cli_args.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_cli_output_contract.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_cli_post_review.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_cli_preflight.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_cli_prompts.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_config.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_conftest.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_gemini_cache.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_gemini_runner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_github_client_checks_signal.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_github_client_write.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_fetch_diff.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_get_llm_temperature.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_http_guards.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_precheck.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_preflight_image_bump.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_progress.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_storage_resolution.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_timeout_surfacing.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_js_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_llm_models.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_llm_models_override.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_llm_pricing.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_migration_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_models.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_multi_round.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_openai_runner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_packaging.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_plan_review.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_precheck_engine.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_precheck_engine_integration.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_precheck_sarif.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_precheck_shadow.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_precheck_shadow_integration.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_prompts.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_repo_provision.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_review_patterns_integration.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_review_tools.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_runners_context7.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_runners_context_usage.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_runners_helpers.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_runners_new.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_scanner_utils.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_secrets_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_specialist_validation.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_stage_costs.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_storage_precheck.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_terraform_scanner.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_terraform_scanner_integration.py +0 -0
- {argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_workflow_lint_scanner.py +0 -0
|
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.7] - 2026-09-26
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Reworded a misleading log message in `argus/graph.py`'s `_node_lite_review`
|
|
15
|
+
(TECH-6878, #29). The log previously stated "Lite round history skipped on HTTP
|
|
16
|
+
storage path", which was misinterpreted as indicating that review persistence
|
|
17
|
+
was skipped altogether and caused an operator/agent to file a false bug
|
|
18
|
+
report. The message now clarifies that only the comment markdown section for
|
|
19
|
+
prior lite history is omitted (because `select_recent_lite_rounds` is not
|
|
20
|
+
implemented in the HTTP storage shim), while the round's own finalize write is
|
|
21
|
+
not skipped and is attempted normally.
|
|
22
|
+
- Added test coverage for lite-round persistence round-tripping through both the
|
|
23
|
+
SQLite and HTTP storage backends (previously untested for HTTP), verifying that
|
|
24
|
+
`reviewer_version="v3-lite"` is preserved on write and read (TECH-6878, #29).
|
|
25
|
+
Also marked `test_insert_agent_runs_batch[http]` as an explicit xfail for the
|
|
26
|
+
documented HTTP shim analytics gap rather than letting it pass vacuously.
|
|
27
|
+
|
|
28
|
+
## [0.2.6] - 2026-09-19
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Bounded `openai` dependency to `>=1.66.0,<2` in `pyproject.toml` (TECH-6590) to
|
|
33
|
+
prevent unconstrained fresh installs from resolving to a future breaking major
|
|
34
|
+
release. The floor was also tightened from `>=1.50.0` to `>=1.66.0` where the
|
|
35
|
+
OpenAI Responses API (`client.responses.create()`) used by this codebase was
|
|
36
|
+
introduced. (Note: this is a general hygiene fix; it does not address the root
|
|
37
|
+
cause of the TECH-6590-reported crash, which was a separate shared-venv version-skew
|
|
38
|
+
issue tracked in TECH-6592.)
|
|
39
|
+
|
|
10
40
|
## [0.2.5] - 2026-09-18
|
|
11
41
|
|
|
12
42
|
### Added
|
|
@@ -326,7 +356,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
326
356
|
packaged set.
|
|
327
357
|
- `argus --version`, `argus prompts list`, and `argus prompts export`.
|
|
328
358
|
|
|
329
|
-
[Unreleased]: https://github.com/redesignhealth/argus-review/compare/v0.2.
|
|
359
|
+
[Unreleased]: https://github.com/redesignhealth/argus-review/compare/v0.2.7...HEAD
|
|
360
|
+
[0.2.7]: https://github.com/redesignhealth/argus-review/compare/v0.2.6...v0.2.7
|
|
361
|
+
[0.2.6]: https://github.com/redesignhealth/argus-review/compare/v0.2.5...v0.2.6
|
|
330
362
|
[0.2.5]: https://github.com/redesignhealth/argus-review/compare/v0.2.4...v0.2.5
|
|
331
363
|
[0.2.4]: https://github.com/redesignhealth/argus-review/compare/v0.2.3...v0.2.4
|
|
332
364
|
[0.2.3]: https://github.com/redesignhealth/argus-review/compare/v0.2.2...v0.2.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: argus-code-review
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
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
|
|
@@ -26,7 +26,7 @@ Requires-Dist: langgraph-checkpoint-sqlite>=2.0.0
|
|
|
26
26
|
Requires-Dist: langgraph<2,>=1.0.10
|
|
27
27
|
Requires-Dist: langsmith<1,>=0.2.0
|
|
28
28
|
Requires-Dist: litellm<2.0.0,>=1.50.0
|
|
29
|
-
Requires-Dist: openai
|
|
29
|
+
Requires-Dist: openai<2,>=1.66.0
|
|
30
30
|
Requires-Dist: psycopg-pool>=3.2.0
|
|
31
31
|
Requires-Dist: psycopg[binary]>=3.2.0
|
|
32
32
|
Requires-Dist: pydantic-settings>=2.0.0
|
|
@@ -2228,7 +2228,11 @@ async def _node_lite_review(state: ReviewState) -> dict[str, Any]:
|
|
|
2228
2228
|
except Exception: # noqa: BLE001
|
|
2229
2229
|
logger.error("Failed to build lite round history — skipping section", exc_info=True)
|
|
2230
2230
|
elif req.pr_number and _lite_history_backend_kind == "http":
|
|
2231
|
-
logger.info(
|
|
2231
|
+
logger.info(
|
|
2232
|
+
"Lite round history markdown section omitted on HTTP storage path "
|
|
2233
|
+
"(select_recent_lite_rounds not implemented in HTTP shim; "
|
|
2234
|
+
"the round's own finalize write is not skipped and will be attempted normally)"
|
|
2235
|
+
)
|
|
2232
2236
|
|
|
2233
2237
|
response.review_comment = (
|
|
2234
2238
|
f"## Code Review — Round {round_num} (Lite Mode)\n\n"
|
|
@@ -46,7 +46,7 @@ dependencies = [
|
|
|
46
46
|
# HTTP client (GitHub API, storage HTTP shim)
|
|
47
47
|
"httpx>=0.27.0",
|
|
48
48
|
# OpenAI SDK for the plan-extraction fallback path
|
|
49
|
-
"openai>=1.
|
|
49
|
+
"openai>=1.66.0,<2",
|
|
50
50
|
# Per-token model pricing table (argus/llm/pricing.py) -- single source
|
|
51
51
|
# of truth for $/token rates, instead of a hand-maintained duplicate.
|
|
52
52
|
"litellm>=1.50.0,<2.0.0",
|
|
@@ -3,22 +3,20 @@
|
|
|
3
3
|
Every scenario in this file exercises the seven logical operations defined
|
|
4
4
|
by ``argus/storage/sql.py`` (the Postgres canonical writer) purely through
|
|
5
5
|
their Pydantic input/output shapes — no backend-specific assertions. The
|
|
6
|
-
goal is a single suite that
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
goal is a single suite that runs against any backend implementing the same
|
|
7
|
+
operations:
|
|
8
|
+
|
|
9
|
+
- **sqlite**: fully supported locally (:class:`argus.storage.sqlite.SqliteHistoryBackend`).
|
|
10
|
+
- **http**: wired via ``pytest-httpx`` standing in for the two-endpoint HTTP
|
|
11
|
+
storage contract (see ``docs/STORAGE.md``). Operations not implemented by
|
|
12
|
+
the minimal HTTP shim (e.g. status polling, recent-rounds listing,
|
|
13
|
+
select_recent_lite_rounds, insert_agent_runs) are documented and marked xfail.
|
|
14
|
+
Completed-round persistence and latest-round reads (including for lite-mode
|
|
15
|
+
reviews) are fully supported and verified.
|
|
14
16
|
- **postgres**: needs a live database and ``argus.storage.session`` wired
|
|
15
|
-
up.
|
|
16
|
-
the ``integration`` marker
|
|
17
|
+
up. Guarded with ``ARGUS_DB_URL`` / ``SUPABASE_DB_URL`` env-var presence and
|
|
18
|
+
the ``integration`` marker, mirroring the pattern in
|
|
17
19
|
``tests/storage/test_sql.py``.
|
|
18
|
-
- **http**: needs a ``pytest-httpx`` fixture standing in for your configured
|
|
19
|
-
HTTP backend's endpoints (see ``docs/STORAGE.md`` for the contract). Once
|
|
20
|
-
wired, ``HttpStorageClient`` would sit behind the same seven-operation
|
|
21
|
-
surface as a thin adapter.
|
|
22
20
|
|
|
23
21
|
Scenario coverage:
|
|
24
22
|
|
|
@@ -103,6 +101,9 @@ _HTTP_UNSUPPORTED_TESTS = {
|
|
|
103
101
|
# select_recent_rounds -- not exposed over HTTP.
|
|
104
102
|
"test_select_recent_rounds_orders_desc_and_respects_limit",
|
|
105
103
|
"test_repeated_running_upsert_same_flow_run_id_does_not_duplicate",
|
|
104
|
+
# insert_agent_runs -- documented no-op on HTTP path (analytics inserts
|
|
105
|
+
# not covered by minimal HTTP shim, TECH-3487 follow-up).
|
|
106
|
+
"test_insert_agent_runs_batch",
|
|
106
107
|
# select_recent_lite_rounds -- documented no-op (always returns []).
|
|
107
108
|
"test_select_recent_lite_rounds_filters_reviewer_version",
|
|
108
109
|
"test_select_recent_lite_rounds_default_limit_is_200",
|
|
@@ -112,6 +113,14 @@ _HTTP_UNSUPPORTED_TESTS = {
|
|
|
112
113
|
def _mark_known_http_gaps(request: pytest.FixtureRequest) -> None:
|
|
113
114
|
base_name = request.node.name.split("[")[0]
|
|
114
115
|
if base_name in _HTTP_UNSUPPORTED_TESTS:
|
|
116
|
+
# insert_agent_runs is a documented no-op on the HTTP shim (analytics
|
|
117
|
+
# inserts not covered, TECH-3487 follow-up); the call does not assert
|
|
118
|
+
# rows or raise, so xfail directly rather than letting it pass vacuously.
|
|
119
|
+
if base_name == "test_insert_agent_runs_batch":
|
|
120
|
+
pytest.xfail(
|
|
121
|
+
"HTTP storage shim does not support agent_runs analytics inserts "
|
|
122
|
+
"(documented Phase-1 gap, TECH-3487)"
|
|
123
|
+
)
|
|
115
124
|
request.node.add_marker(
|
|
116
125
|
pytest.mark.xfail(
|
|
117
126
|
reason=(
|
|
@@ -202,10 +211,10 @@ async def backend(
|
|
|
202
211
|
return
|
|
203
212
|
|
|
204
213
|
if backend_kind == "http":
|
|
214
|
+
_mark_known_http_gaps(request)
|
|
205
215
|
httpx_mock = request.getfixturevalue("httpx_mock")
|
|
206
216
|
httpx_mock.add_callback(_FakeHttpStorageBackend().handle, is_reusable=True)
|
|
207
217
|
install_http_storage(read_url=_HTTP_READ_URL, write_url=_HTTP_WRITE_URL)
|
|
208
|
-
_mark_known_http_gaps(request)
|
|
209
218
|
try:
|
|
210
219
|
yield HttpHistoryBackend()
|
|
211
220
|
finally:
|
|
@@ -317,6 +326,79 @@ async def test_upsert_completed_row_defaults_reviewer_version_and_stage(
|
|
|
317
326
|
assert written.verdict is None
|
|
318
327
|
|
|
319
328
|
|
|
329
|
+
async def test_lite_round_completed_upsert_round_trips_through_latest(
|
|
330
|
+
backend: HistoryBackend,
|
|
331
|
+
) -> None:
|
|
332
|
+
"""Lite-mode rounds (reviewer_version='v3-lite') must persist via
|
|
333
|
+
upsert_completed_row and round-trip through select_latest_completed_round
|
|
334
|
+
on all backends (including the HTTP storage shim).
|
|
335
|
+
"""
|
|
336
|
+
payload = CodeReviewRoundIn(
|
|
337
|
+
flow_run_id="fr-lite-rt-1",
|
|
338
|
+
repo="org/repo-lite",
|
|
339
|
+
pr_number=10,
|
|
340
|
+
verdict="approve",
|
|
341
|
+
risk_level="low",
|
|
342
|
+
blocking_count=0,
|
|
343
|
+
suggestion_count=0,
|
|
344
|
+
review_comment="lgtm lite review",
|
|
345
|
+
reviewer_version="v3-lite",
|
|
346
|
+
result_json={"review_round": 2, "preflight_reason": "test-only diff"},
|
|
347
|
+
sha="deadbeef123",
|
|
348
|
+
base_ref="main",
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
written = await backend.upsert_completed_row(row=payload)
|
|
352
|
+
assert written.reviewer_version == "v3-lite"
|
|
353
|
+
|
|
354
|
+
latest = await backend.select_latest_completed_round(repo="org/repo-lite", pr_number=10)
|
|
355
|
+
assert latest is not None
|
|
356
|
+
assert latest.id == written.id
|
|
357
|
+
assert latest.reviewer_version == "v3-lite"
|
|
358
|
+
assert latest.verdict == "approve"
|
|
359
|
+
assert latest.sha == "deadbeef123"
|
|
360
|
+
assert latest.result_json == {"review_round": 2, "preflight_reason": "test-only diff"}
|
|
361
|
+
assert latest.prior_count == 1
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
async def test_lite_round_becomes_latest_completed_round(
|
|
365
|
+
backend: HistoryBackend,
|
|
366
|
+
) -> None:
|
|
367
|
+
"""When a lite review follows a full review on the same PR, the lite
|
|
368
|
+
round becomes the latest completed round and preserves its version.
|
|
369
|
+
"""
|
|
370
|
+
first_full = await backend.upsert_completed_row(
|
|
371
|
+
row=CodeReviewRoundIn(
|
|
372
|
+
flow_run_id="fr-seq-1",
|
|
373
|
+
repo="org/repo-seq",
|
|
374
|
+
pr_number=20,
|
|
375
|
+
verdict="block",
|
|
376
|
+
reviewer_version="v3",
|
|
377
|
+
sha="sha-round-1",
|
|
378
|
+
)
|
|
379
|
+
)
|
|
380
|
+
second_lite = await backend.upsert_completed_row(
|
|
381
|
+
row=CodeReviewRoundIn(
|
|
382
|
+
flow_run_id="fr-seq-2",
|
|
383
|
+
repo="org/repo-seq",
|
|
384
|
+
pr_number=20,
|
|
385
|
+
verdict="approve",
|
|
386
|
+
reviewer_version="v3-lite",
|
|
387
|
+
sha="sha-round-2",
|
|
388
|
+
result_json={"review_round": 2, "preflight_reason": "catchup on green CI"},
|
|
389
|
+
)
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
latest = await backend.select_latest_completed_round(repo="org/repo-seq", pr_number=20)
|
|
393
|
+
assert latest is not None
|
|
394
|
+
assert latest.id == second_lite.id
|
|
395
|
+
assert latest.id != first_full.id
|
|
396
|
+
assert latest.reviewer_version == "v3-lite"
|
|
397
|
+
assert latest.verdict == "approve"
|
|
398
|
+
assert latest.sha == "sha-round-2"
|
|
399
|
+
assert latest.prior_count == 2
|
|
400
|
+
|
|
401
|
+
|
|
320
402
|
# ---------------------------------------------------------------------------
|
|
321
403
|
# 3. Two completed rounds -> latest wins; select_recent_rounds ordering + limit
|
|
322
404
|
# ---------------------------------------------------------------------------
|
|
@@ -471,7 +553,7 @@ async def test_completed_upsert_same_flow_run_id_preserves_sha_via_coalesce(
|
|
|
471
553
|
# ---------------------------------------------------------------------------
|
|
472
554
|
|
|
473
555
|
|
|
474
|
-
async def test_insert_agent_runs_batch(backend:
|
|
556
|
+
async def test_insert_agent_runs_batch(backend: HistoryBackend) -> None:
|
|
475
557
|
review = await backend.upsert_completed_row(
|
|
476
558
|
row=CodeReviewRoundIn(repo="org/repo6", pr_number=2, verdict="approve")
|
|
477
559
|
)
|
|
@@ -11,6 +11,7 @@ Tests use ``pytest-httpx`` to mock the backend side; no real network.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
+
import json
|
|
14
15
|
from datetime import UTC, datetime
|
|
15
16
|
from uuid import uuid4
|
|
16
17
|
|
|
@@ -249,3 +250,70 @@ def test_install_raises_on_conflicting_reinstall() -> None:
|
|
|
249
250
|
install_http_storage(read_url=_READ_URL, write_url=_WRITE_URL, auth="ApiKey k")
|
|
250
251
|
with pytest.raises(HttpStorageError, match="different parameters"):
|
|
251
252
|
install_http_storage(read_url=_READ_URL, write_url=_WRITE_URL, auth="ApiKey DIFFERENT")
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@pytest.mark.asyncio
|
|
256
|
+
async def test_write_and_read_lite_round_round_trip(httpx_mock: HTTPXMock) -> None:
|
|
257
|
+
"""A lite-mode round (reviewer_version="v3-lite") written via
|
|
258
|
+
HttpStorageClient.write_round round-trips through
|
|
259
|
+
read_latest_completed_round with reviewer_version preserved.
|
|
260
|
+
"""
|
|
261
|
+
written_id = uuid4()
|
|
262
|
+
round_payload = _row_payload(
|
|
263
|
+
id=str(written_id),
|
|
264
|
+
reviewer_version="v3-lite",
|
|
265
|
+
verdict="APPROVE",
|
|
266
|
+
sha="131e334046bf",
|
|
267
|
+
result_json={"review_round": 8, "preflight_reason": "test-only diff"},
|
|
268
|
+
)
|
|
269
|
+
httpx_mock.add_response(
|
|
270
|
+
method="POST",
|
|
271
|
+
url="https://fake-storage-backend.test/api/v1/code-review/storage/reviews/acme/example-repo/158/rounds",
|
|
272
|
+
status_code=201,
|
|
273
|
+
json=round_payload,
|
|
274
|
+
)
|
|
275
|
+
httpx_mock.add_response(
|
|
276
|
+
method="GET",
|
|
277
|
+
url="https://fake-storage-backend.test/api/v1/code-review/storage/reviews/acme/example-repo/158",
|
|
278
|
+
status_code=200,
|
|
279
|
+
json={"rounds": [round_payload]},
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
client = HttpStorageClient(read_url=_READ_URL, write_url=_WRITE_URL, auth="ApiKey test")
|
|
283
|
+
try:
|
|
284
|
+
round_data = CodeReviewRound(
|
|
285
|
+
repo="acme/example-repo",
|
|
286
|
+
pr_number=158,
|
|
287
|
+
verdict="APPROVE",
|
|
288
|
+
risk_level="LOW",
|
|
289
|
+
blocking_count=0,
|
|
290
|
+
suggestion_count=0,
|
|
291
|
+
reviewer_version="v3-lite",
|
|
292
|
+
sha="131e334046bf",
|
|
293
|
+
result_json={"review_round": 8, "preflight_reason": "test-only diff"},
|
|
294
|
+
)
|
|
295
|
+
written = await client.write_round(
|
|
296
|
+
owner="acme", repo="example-repo", pr=158, round_data=round_data
|
|
297
|
+
)
|
|
298
|
+
assert written.id == written_id
|
|
299
|
+
assert written.reviewer_version == "v3-lite"
|
|
300
|
+
|
|
301
|
+
# Verify POST payload sent the reviewer_version
|
|
302
|
+
post_request = httpx_mock.get_requests()[0]
|
|
303
|
+
assert post_request.method == "POST"
|
|
304
|
+
posted_body = json.loads(post_request.content)
|
|
305
|
+
assert posted_body["reviewer_version"] == "v3-lite"
|
|
306
|
+
assert posted_body["sha"] == "131e334046bf"
|
|
307
|
+
|
|
308
|
+
latest, count = await client.read_latest_completed_round(
|
|
309
|
+
owner="acme", repo="example-repo", pr=158
|
|
310
|
+
)
|
|
311
|
+
assert latest is not None
|
|
312
|
+
assert latest.id == written_id
|
|
313
|
+
assert latest.reviewer_version == "v3-lite"
|
|
314
|
+
assert latest.verdict == "APPROVE"
|
|
315
|
+
assert latest.sha == "131e334046bf"
|
|
316
|
+
assert latest.result_json == {"review_round": 8, "preflight_reason": "test-only diff"}
|
|
317
|
+
assert count == 1
|
|
318
|
+
finally:
|
|
319
|
+
await client.aclose()
|
|
@@ -80,3 +80,41 @@ class TestGetAsyncOpenAIClientBaseURL:
|
|
|
80
80
|
|
|
81
81
|
mock_async_openai.assert_called_once()
|
|
82
82
|
assert mock_async_openai.call_args.kwargs["base_url"] is None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class TestOpenAIClientSyncRespond:
|
|
86
|
+
def test_respond_passes_text_format_to_responses_create(self) -> None:
|
|
87
|
+
"""text_format is wrapped in text={'format': text_format} for client.responses.create."""
|
|
88
|
+
settings = _make_settings(None)
|
|
89
|
+
mock_response = MagicMock()
|
|
90
|
+
mock_response.model = "gpt-5.4-mini"
|
|
91
|
+
mock_response.status = "completed"
|
|
92
|
+
mock_response.output = []
|
|
93
|
+
mock_response.output_text = "{}"
|
|
94
|
+
|
|
95
|
+
mock_client_instance = MagicMock()
|
|
96
|
+
mock_client_instance.responses.create.return_value = mock_response
|
|
97
|
+
|
|
98
|
+
text_format = {
|
|
99
|
+
"type": "json_schema",
|
|
100
|
+
"name": "test_schema",
|
|
101
|
+
"strict": True,
|
|
102
|
+
"schema": {"type": "object", "properties": {}},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
with (
|
|
106
|
+
patch("argus.openai_client.get_settings", return_value=settings),
|
|
107
|
+
patch("argus.openai_client.OpenAI", return_value=mock_client_instance),
|
|
108
|
+
patch("argus.openai_client.wrap_openai", side_effect=lambda client: client),
|
|
109
|
+
):
|
|
110
|
+
client = OpenAIClientSync()
|
|
111
|
+
resp = client.respond(
|
|
112
|
+
input="test prompt",
|
|
113
|
+
text_format=text_format,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
mock_client_instance.responses.create.assert_called_once()
|
|
117
|
+
call_kwargs = mock_client_instance.responses.create.call_args.kwargs
|
|
118
|
+
assert call_kwargs["text"] == {"format": text_format}
|
|
119
|
+
assert call_kwargs["input"] == "test prompt"
|
|
120
|
+
assert resp == mock_response
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Tests for argus.llm.output_models: schema generation and Responses API format.
|
|
2
|
+
|
|
3
|
+
Verifies pydantic_to_response_format (including exclude= and strict= handling)
|
|
4
|
+
and LLMOutputModel schema generation for compatibility with OpenAI Responses API.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Literal
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from argus.graph import _PIPELINE_ONLY_RESPONSE_FIELDS
|
|
13
|
+
from argus.llm.output_models import (
|
|
14
|
+
LLMOutputModel,
|
|
15
|
+
make_schema_strict,
|
|
16
|
+
pydantic_to_response_format,
|
|
17
|
+
)
|
|
18
|
+
from argus.models import ReviewResponse
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SampleModel(BaseModel):
|
|
22
|
+
name: str = Field(description="The name")
|
|
23
|
+
count: int = Field(default=0, description="The count")
|
|
24
|
+
tag: str | None = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SampleModelWithExcludedFields(BaseModel):
|
|
28
|
+
title: str
|
|
29
|
+
verdict: str
|
|
30
|
+
dropped_field_1: dict[str, float] = Field(default_factory=dict)
|
|
31
|
+
dropped_field_2: dict[str, float] = Field(default_factory=dict)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SampleOutputModel(LLMOutputModel):
|
|
35
|
+
_schema_name = "custom_output"
|
|
36
|
+
|
|
37
|
+
summary: str = Field(description="Summary of results")
|
|
38
|
+
status: Literal["pass", "fail"] = Field(description="Pass or fail")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestPydanticToResponseFormat:
|
|
42
|
+
def test_default_name_and_strict(self) -> None:
|
|
43
|
+
"""pydantic_to_response_format defaults name to model name in lowercase and strict=True."""
|
|
44
|
+
rf = pydantic_to_response_format(SampleModel)
|
|
45
|
+
|
|
46
|
+
assert rf["type"] == "json_schema"
|
|
47
|
+
assert rf["name"] == "samplemodel"
|
|
48
|
+
assert rf["strict"] is True
|
|
49
|
+
schema = rf["schema"]
|
|
50
|
+
assert schema["type"] == "object"
|
|
51
|
+
assert schema["additionalProperties"] is False
|
|
52
|
+
assert set(schema["required"]) == set(schema["properties"].keys())
|
|
53
|
+
|
|
54
|
+
def test_custom_name(self) -> None:
|
|
55
|
+
"""pydantic_to_response_format respects custom name."""
|
|
56
|
+
rf = pydantic_to_response_format(SampleModel, name="my_custom_schema")
|
|
57
|
+
assert rf["name"] == "my_custom_schema"
|
|
58
|
+
|
|
59
|
+
def test_strict_false(self) -> None:
|
|
60
|
+
"""pydantic_to_response_format respects strict=False."""
|
|
61
|
+
rf = pydantic_to_response_format(SampleModel, strict=False)
|
|
62
|
+
assert rf["strict"] is False
|
|
63
|
+
# strict=False does not enforce additionalProperties: false
|
|
64
|
+
assert rf["schema"].get("additionalProperties") is not False
|
|
65
|
+
|
|
66
|
+
def test_exclude_fields(self) -> None:
|
|
67
|
+
"""exclude= removes specified fields from schema properties and required."""
|
|
68
|
+
rf = pydantic_to_response_format(
|
|
69
|
+
SampleModelWithExcludedFields,
|
|
70
|
+
name="filtered_model",
|
|
71
|
+
exclude={"dropped_field_1", "dropped_field_2"},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
properties = rf["schema"]["properties"]
|
|
75
|
+
assert "title" in properties
|
|
76
|
+
assert "verdict" in properties
|
|
77
|
+
assert "dropped_field_1" not in properties
|
|
78
|
+
assert "dropped_field_2" not in properties
|
|
79
|
+
|
|
80
|
+
required = rf["schema"]["required"]
|
|
81
|
+
assert "title" in required
|
|
82
|
+
assert "verdict" in required
|
|
83
|
+
assert "dropped_field_1" not in required
|
|
84
|
+
assert "dropped_field_2" not in required
|
|
85
|
+
|
|
86
|
+
def test_review_response_with_pipeline_only_fields_excluded(self) -> None:
|
|
87
|
+
"""ReviewResponse with _PIPELINE_ONLY_RESPONSE_FIELDS matches production graph.py usage."""
|
|
88
|
+
rf = pydantic_to_response_format(
|
|
89
|
+
ReviewResponse,
|
|
90
|
+
"review_response",
|
|
91
|
+
exclude=_PIPELINE_ONLY_RESPONSE_FIELDS,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
assert rf["type"] == "json_schema"
|
|
95
|
+
assert rf["name"] == "review_response"
|
|
96
|
+
assert rf["strict"] is True
|
|
97
|
+
|
|
98
|
+
properties = rf["schema"]["properties"]
|
|
99
|
+
required = rf["schema"]["required"]
|
|
100
|
+
|
|
101
|
+
for excluded in _PIPELINE_ONLY_RESPONSE_FIELDS:
|
|
102
|
+
assert excluded not in properties
|
|
103
|
+
assert excluded not in required
|
|
104
|
+
|
|
105
|
+
# Essential ReviewResponse fields must remain present and required
|
|
106
|
+
assert "verdict" in properties
|
|
107
|
+
assert "verdict" in required
|
|
108
|
+
assert "risk_level" in properties
|
|
109
|
+
assert "risk_level" in required
|
|
110
|
+
assert "findings" in properties
|
|
111
|
+
assert "findings" in required
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class TestLLMOutputModel:
|
|
115
|
+
def test_to_prompt_schema(self) -> None:
|
|
116
|
+
"""to_prompt_schema generates markdown with field definitions and example."""
|
|
117
|
+
schema_text = SampleOutputModel.to_prompt_schema()
|
|
118
|
+
assert "Your output MUST be valid JSON" in schema_text
|
|
119
|
+
assert "| `summary` | string |" in schema_text
|
|
120
|
+
assert "| `status` | enum |" in schema_text
|
|
121
|
+
assert (
|
|
122
|
+
"custom_output" not in schema_text
|
|
123
|
+
) # schema name is in API response_format, not prompt
|
|
124
|
+
|
|
125
|
+
def test_to_response_format(self) -> None:
|
|
126
|
+
"""to_response_format produces Chat Completions nested format."""
|
|
127
|
+
rf = SampleOutputModel.to_response_format()
|
|
128
|
+
assert rf["type"] == "json_schema"
|
|
129
|
+
assert "json_schema" in rf
|
|
130
|
+
assert rf["json_schema"]["name"] == "custom_output"
|
|
131
|
+
assert rf["json_schema"]["strict"] is True
|
|
132
|
+
|
|
133
|
+
def test_to_response_format_flat(self) -> None:
|
|
134
|
+
"""to_response_format_flat produces Responses API flat format."""
|
|
135
|
+
rf = SampleOutputModel.to_response_format_flat()
|
|
136
|
+
assert rf["type"] == "json_schema"
|
|
137
|
+
assert rf["name"] == "custom_output"
|
|
138
|
+
assert rf["strict"] is True
|
|
139
|
+
assert "schema" in rf
|
|
140
|
+
assert rf["schema"]["type"] == "object"
|
|
141
|
+
assert rf["schema"]["additionalProperties"] is False
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class TestMakeSchemaStrict:
|
|
145
|
+
def test_recursively_sets_additional_properties_false_and_required(self) -> None:
|
|
146
|
+
"""make_schema_strict sets additionalProperties=False and required on all objects."""
|
|
147
|
+
schema = {
|
|
148
|
+
"type": "object",
|
|
149
|
+
"properties": {
|
|
150
|
+
"user": {
|
|
151
|
+
"type": "object",
|
|
152
|
+
"properties": {
|
|
153
|
+
"name": {"type": "string"},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
"items": {
|
|
157
|
+
"type": "array",
|
|
158
|
+
"items": {
|
|
159
|
+
"type": "object",
|
|
160
|
+
"properties": {
|
|
161
|
+
"id": {"type": "integer"},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
strict_schema = make_schema_strict(schema)
|
|
169
|
+
|
|
170
|
+
assert strict_schema["additionalProperties"] is False
|
|
171
|
+
assert set(strict_schema["required"]) == {"user", "items"}
|
|
172
|
+
|
|
173
|
+
user_prop = strict_schema["properties"]["user"]
|
|
174
|
+
assert user_prop["additionalProperties"] is False
|
|
175
|
+
assert user_prop["required"] == ["name"]
|
|
176
|
+
|
|
177
|
+
item_schema = strict_schema["properties"]["items"]["items"]
|
|
178
|
+
assert item_schema["additionalProperties"] is False
|
|
179
|
+
assert item_schema["required"] == ["id"]
|
|
180
|
+
|
|
181
|
+
def test_ref_clears_sibling_keywords(self) -> None:
|
|
182
|
+
"""OpenAI strict mode requires $ref to have no sibling keywords."""
|
|
183
|
+
schema = {
|
|
184
|
+
"$ref": "#/$defs/SomeType",
|
|
185
|
+
"description": "Sibling keyword that OpenAI rejects in strict mode",
|
|
186
|
+
}
|
|
187
|
+
strict_schema = make_schema_strict(schema)
|
|
188
|
+
assert strict_schema == {"$ref": "#/$defs/SomeType"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/eslint.config.js
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/package-lock.json
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/package.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-blocking-validator.md
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-coverage-check.md
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-cross-cutting.md
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-feedback-verifier.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-preflight-router.md
RENAMED
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-deployment.md
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-frontend.md
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-infra.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-security.md
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-slackbot.md
RENAMED
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-sql.md
RENAMED
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/argus/prompts/pr-review-tests-and-docs.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/011_add_review_progress_columns.sql
RENAMED
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/016_add_agent_runs_failure_reason.sql
RENAMED
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/schema/018_widen_agent_runs_failure_reason.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/golden/review_response.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_github_client_checks_signal.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_graph_preflight_image_bump.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_precheck_engine_integration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_precheck_shadow_integration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_review_patterns_integration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{argus_code_review-0.2.5 → argus_code_review-0.2.7}/tests/test_terraform_scanner_integration.py
RENAMED
|
File without changes
|
|
File without changes
|