fix(azure-sql): wire is_available + extract_params into Azure SQL @tool decorators#707
Conversation
… decorators Same @tool decorator wiring bug the PostgreSQL and MySQL sides fixed in Tracer-Cloud#703 and Tracer-Cloud#704, now for Azure SQL. All 5 Azure SQL diagnostic tools were silently failing with TypeError: missing 2 required positional arguments ('server' and 'database') on every agent invocation, because the decorator was missing the is_available and extract_params callbacks that inject identifying params from resolved integrations. Mirror the MariaDB/PostgreSQL/MySQL pattern: add azure_sql_is_available and azure_sql_extract_params to app/integrations/azure_sql.py, then wire both into all 5 Azure SQL tools (current queries, resource stats, server status, slow queries, wait stats). Azure SQL integration resolves credentials internally via resolve_azure_sql_config, so extract_params only surfaces the three identifying params (server, database, port). Credentials stay out of tool signatures and are never seen by the LLM. Applied Greptile's port-None hardening preemptively: int(az.get("port") or DEFAULT_AZURE_SQL_PORT). Covers both missing and explicit None port values in the resolved integration dict. Added 14 unit tests covering azure_sql_is_available (6 cases) and azure_sql_extract_params (8 cases including port-None, port-0, port-as-string, and credential isolation). Verified on make test-rds-synthetic (Gemini 2.5-flash): Azure SQL TypeError failures drop from 92 to 0 across the 15 RDS scenarios.
Greptile SummaryThis PR completes the three-part fix for the Confidence Score: 5/5Safe to merge — no functional regressions, all edge cases covered, and prior review concerns fully addressed. All changes are additive and narrowly scoped. The two new helpers are defensively implemented with No files require special attention. Important Files Changed
|
Address Greptile review on Tracer-Cloud#707: `str(None).strip()` produces the literal string "None", not an empty string. If a stored integration has `{"server": null}` or `{"database": null}`, az.get("server", "") returns None (the default only applies when the key is absent), and str(None).strip() yields "None" — a misleading non-empty string. In practice is_available guards against this, but the extract_params function can be called directly in tests or future code paths and silently produce garbage. Mirror the `or` pattern already applied to port, and match the AzureSQLConfig._normalize_server / _normalize_database validators which use str(value or "").strip(). Added two unit tests covering the None case for both server and database.
There was a problem hiding this comment.
Pulled the branch and ran it locally. All 35 tests in tests/integrations/test_azure_sql.py pass, ruff is clean on the changed files, and CI is fully green. The wiring mirrors what already landed for Postgres in #703 and MySQL in #704, so the pattern is well-trodden at this point.
The port=None / server=None / database=None fallbacks are a nice touch, those are exactly the cases that would have bitten us later with int(None) and str(None) gotchas, and each one has its own test.
One thing worth mentioning (you already called it out in the PR description): app/nodes/plan_actions/detect_sources.py has branches for postgresql, mariadb, and mysql but no azure_sql branch, so sources['azure_sql'] never gets populated at runtime. This PR correctly fixes the false-positive case where the tools crash on unrelated scenarios, but the false-negative case where they never fire when Azure SQL is actually configured still needs a separate fix. Not a blocker for #706 since that issue was scoped to the TypeError wiring, but please do open that follow-up right after this lands so Azure SQL tools are actually reachable end to end.
@ebrahim-sameh LGTM, approving.
|
Follow-up filed as #711 — @ebrahim-sameh Works for this one, but next time bundle the full feature — tools + detect_sources wiring — into a single PR so we don't ship things that look green but aren't actually reachable. |
Fixes #706
Follow-up to #703 and #704. Third of three SQL tool families sharing the same
@tooldecorator wiring bug. Completes the cleanup.Summary
Adds the missing
is_availableandextract_paramscallbacks to the@tool(...)decorators on all 5 Azure SQL diagnostic tools. Mirrors the approach already landed for PostgreSQL in #703 and MySQL in #704, and the working MariaDB pattern atapp/integrations/mariadb.py:160-176.What changed
azure_sql_is_availableandazure_sql_extract_paramstoapp/integrations/azure_sql.py.@tool(...)decorator of all 5 Azure SQL tools: current queries, resource stats, server status, slow queries, wait stats.Azure SQL integration resolves credentials internally via
resolve_azure_sql_config(reading from the store/env), soextract_paramsonly surfaces the three identifying params:server,database,port. Credentials (username, password, driver, encrypt) stay out of tool signatures and are never seen by the LLM.Scope: 7 files, +155 / -5 lines.
Port-None hardening applied preemptively
Used the
orfallback for the port cast that Greptile flagged on #704:Both missing and explicit-
Noneport values collapse to the default, avoiding aTypeErrorif a stored integration holds{"port": null}. Covered by a dedicated unit test (test_port_none_collapses_to_default).Evidence
During the end-to-end audit (
make test-rds-syntheticon Gemini 2.5-flash, post-#703/#704 baseline), the agent opportunistically invoked Azure SQL tools on RDS PostgreSQL scenarios, producing 92 Azure SQL TypeError failures across the 15 scenarios:get_azure_sql_resource_statsget_azure_sql_current_queriesget_azure_sql_server_statusget_azure_sql_wait_statsAfter this fix, the agent no longer attempts Azure SQL tools on unrelated scenarios — the tools correctly report unavailable when no Azure SQL integration is present.
Test plan
make test-cov— 2739 passed, 1 skipped (added 14 new unit tests forazure_sql_is_availableandazure_sql_extract_params, no regressions elsewhere)ruff checkclean on all changed filesmypyclean on all changed filesmake test-rds-syntheticrun shows 0 Azure SQL TypeError failures (was 92 in post-fix(mysql): wire is_available + extract_params into MySQL @tool decorators #704 baseline)Out-of-scope adjacent gap (will file separately)
While verifying, I noticed
app/nodes/plan_actions/detect_sources.pyhas no Azure SQL branch —sources["azure_sql"]is never populated at runtime even when a user configures the integration. This fix makes the tools correctly report unavailable when they shouldn't fire, but they also won't fire when they should until that gap closes. Will file as a follow-up issue after this lands.