Skip to content

Port upstream tool catalog update (#1831): get_trace_activity, resolve_dataset/log context schema changes - #62

Merged
VibeSWE merged 6 commits into
mainfrom
upstream/tool-catalog-1831
Oct 24, 2026
Merged

VibeSWE merged 6 commits into
mainfrom
upstream/tool-catalog-1831

Conversation

@VibeSWE

@VibeSWE VibeSWE commented Oct 24, 2026 •

Copy link
Copy Markdown

Upstream origin

Ports parseablehq/parseable@32979cb4, "update: tool catalog (parseablehq#1831)", via git cherry-pick -x. Upstream authorship is kept.

Upstream changes:

  • New get_trace_activity tool. It returns a bucketed (DATE_BIN) series of COUNT(DISTINCT span_span_id) / COUNT(DISTINCT span_trace_id), with optional groupBy (max 5) and scalar filters. It also returns the exact SQL it ran.
  • resolve_dataset: new telemetryType/limit arguments, plus scored candidate ranking. It never falls back to an arbitrary dataset.
  • get_log_context: the catalog schema now requires exactly one of log/body/message via a root oneOf. Clients never see that oneOf: the fork strips root combinators from listed schemas (see "Review fixes"). The tool now delegates to the same code as POST /api/v1/query/context, through the new query_context_for_authorized_dataset in query_context.rs.
  • sample_events: new paired startTime/endTime arguments.
  • Upstream's schema validator gains oneOf, and anyOf no longer short-circuits sibling keywords. Fork main already had both, so this isn't a behaviour change here.
  • The executor gains reusable public helpers, such as execute_oss_tool and execute_oss_tool_with_query_headers, for Enterprise. It also gains parser-based SQL validation, IANA-timezone resolve_time_range, activity-count discover_datasets, and exclusion of internal datasets.

Conflict resolutions

src/tool_catalog.rs

  • oneOf: the fork already had an inline check, and it is semantically identical to upstream's: exactly one variant must match, with the same error message. I used upstream's validate_one_of helper to reduce future upstream conflicts. The fork's const check is kept immediately after it.
  • Catalog counts: 54 total and 41 read tools. That is the fork's 53/40 plus get_trace_activity.
  • Tests: kept both the fork's format and schema_combinators_* tests and upstream's resolve_dataset and get_log_context assertions.

src/tool_executor.rs

  • SQL bounding: took upstream's DataFusion-parser validate_tool_sql/bounded_tool_sql, replacing the fork's first-word check. It covers the fork's goals (multiline queries, trailing comments). The fork test's input WITH x AS (SELECT 1) SELECT * FROM x is rejected because of its outer SELECT * (a wildcard inside a CTE would be accepted), so I changed it to select an explicit column. The port commit message wrongly calls this "a wildcard CTE"; see the correction comment on this PR.
  • Imports: kept the fork's time-expression imports (parse_start_time_expression, etc., used by list_log_errors) and added oss_tool_specs.
  • list_dashboards in the executor: took upstream's title/tag query filter and kept the fork's Result handling of list_dashboards_internal.
  • call_tool_registry: kept the fork's version as-is. Dashboard tools route to dashboard_tools::execute, and the tenant-suspension and Query gates still apply to everything else. It still calls execute_oss_tool_inner, not upstream's execute_oss_tool. The spec is already validated against external_tool_specs, so execute_oss_tool would validate the arguments a second time, and oss_tool_specs() allocates and clones the catalog on every call. (The dashboard operation schemas themselves are cached in a LazyLock.)

query_context.rs, Cargo.toml, Cargo.lock applied cleanly. The fork had no divergence in query_context.rs.

Branch contents

Commit What
2f8718da The upstream port (cherry-pick of 32979cb plus conflict resolutions).
0097de1e ci: migrate all workflows off Blacksmith. Not part of the port: it is identical to main's #66 (de6c4a97), and git diff origin/main origin/upstream/tool-catalog-1831 -- .github README.md is empty. It was added so CI could run on this branch. It's left in place deliberately (rewriting would need a force-push) and becomes a no-op when merged.
cc46ee5f Enforce JSON Schema type unions in argument validation (review nit 24). validate_type accepts a type array, such as ["string","null"], when any listed type matches; previously an array-valued type was ignored. docs/tool-api.md documents this, and schema_type_unions_accept_any_listed_type tests it.
62185382 Review fixes: listed schemas, log-context anchors, checked chrono arithmetic, helper extraction and tests.
77723f97 Docs: make docs/tool-api.md claims match the code.
83a43099 Merge of origin/main (brings in #59 ingestion throughput and #60 API-key privilege filter). No conflicts; main did not touch the tool files.

Review fixes

  • Listed schemas have no root combinators. The Anthropic Messages API and OpenAI strict function schemas reject a tool input schema with a root-level oneOf/anyOf/allOf. Upstream's get_log_context oneOf would have broken every tool for those clients, not just this one. list_tool_registry now emits listed_input_schema(...), which removes those root keys. The catalog keeps the oneOf, call_tool_registry still validates against it (400, "arguments must match exactly one allowed schema"), and the log/body/message descriptions state the rule. A test checks every listed tool. The catalog itself still matches upstream.
  • log_context_request requires exactly one anchor. It used to accept two or more and say "at least one".
  • No panics on huge durations. resolve_time_range (last 1000000 years) and the log-context contextWindow (300000y) overflowed chrono and panicked. Both now use checked_sub_signed/checked_add_signed and return errors.
  • Small helper extraction for tests: trace_activity_time_field and summarize_dataset_activity. Both are moved verbatim from the async functions, which now call them.

Dependency: chrono-tz = "0.10"

  • Needed: yes. It parses IANA zones for resolve_time_range (today/yesterday local-day boundaries, DST-aware). Before this, the tool rejected any timezone other than UTC.
  • Acceptable: yes. chrono-tz 0.10.4 was already in the build through arrow-array and datafusion-functions. Cargo.lock only gains a direct-edge line, so there are no new crates and no extra compile cost. The license is MIT OR Apache-2.0, which about.toml allows.

Overlap with pending tool-catalog work

The uncommitted src/tool_catalog.rs, src/tool_executor.rs and docs/tool-api.md changes in the deploy-main checkout (run_dashboard_queries, after cursors, oneOf/const/maxLength) are byte-identical to the current origin/main versions. They already landed via 54e8de3 / #51; that checkout sits on an older main (9747e2b). So this PR is resolved directly against that work:

  • oneOf semantics are unchanged, and const/maxLength are preserved.
  • run_dashboard_queries, after cursors and the dashboard tool routing are untouched.
  • The fork's schema_combinators_enforce_branches_and_sibling_constraints test still passes.

Once that checkout moves to current main, its diff should be empty. The changes in docs/tool-api.md from this PR are appended sections only.

Ramifications found

  • get_trace_activity fits this fork's trace storage. span_span_id/span_trace_id are the fork's flattened OTel trace columns (src/handlers/http/traces.rs). The tool requires traces telemetry and both columns, and uses the dataset's time partition, or p_timestamp if none is set. A new test runs the generated SQL, wrapped by bounded_tool_sql, against a DataFusion fixture. This confirms that DATE_BIN with a string interval, positional GROUP BY and distinct counts work, including a duplicate span row.
  • Behaviour changes for tool callers:
    • query_sql/explain_query now reject SELECT * and projections wider than 10 columns. The fork's list_log_errors SQL (5 columns) still validates. dashboard_runtime has its own bounded_sql and is unaffected.
    • get_log_context now requires exactly one anchor value, and its response is now the query/context shape: records, anchorIndex, scope, queries, etc.
    • get_log_context bounds now follow query/context. You give either contextWindow or both contextStartTime and contextEndTime. A single explicit bound is now an error; the old tool filled in the other side with ±5 minutes. Combining a window with an explicit bound is also an error. The default is contextWindow: "5 minutes" on each side of the anchor, with both ends rounded down to the minute. Explicit bounds are no longer rounded to the minute, and the anchor must fall inside them.
    • resolve_time_range now parses IANA zone names case-sensitively, so lowercase "utc" is rejected. The fork used to accept any casing of UTC, and only UTC.
    • The listed inputSchema never carries root-level oneOf/anyOf/allOf (see "Review fixes").
    • discover_datasets results are now {name, eventsInRange}, plus unavailableDatasets, in place of {name, info} and errors.
    • list_datasets/resolve_dataset/discover_datasets hide internal datasets.
  • Consumers: the only in-repo caller of the tools API, scripts/test-dashboard-agent.py, uses only dashboard tools and is unaffected. The frontend only mentions the endpoints in a comment, and there are no tool snapshots. docs/tool-api.md now documents the SQL limits, dataset and time resolution, log context, get_trace_activity and the validator keywords.
  • Enterprise reuse path: upstream's execute_oss_tool dispatches only to the OSS executor. Dashboard-service tools other than list_dashboards/get_dashboard/list_dashboard_tags, such as run_dashboard_queries, return "no OSS internal executor" there. list_dashboards returns a bare array there, while the external path returns {dashboards, nextCursor}. That divergence already existed in the fork; I left it as-is.

Tests

New tests:

  • get_trace_activity:
    • SQL execution against a DataFusion fixture, with and without filters.
    • groupBy/filters field validation and identifier/literal quoting.
    • Bucket interval bounds.
    • Catalog schema bounds: groupBy max 5, bucketCount 1–100, required fields, no extra properties.
    • Registry listing with readOnlyHint.
  • resolve_dataset: telemetryType enum and limit bounds.
  • get_log_context: each anchor alone is accepted; all three together are rejected.
  • Review follow-ups (62185382):
    • No listed tool has a root combinator, and the catalog still enforces the oneOf.
    • The oneOf error text for zero and two matches.
    • log_context_request: exactly one anchor; the default contextWindow; explicit bounds suppress it; pageSize clamped to 500; the output deserializes as LogContextRequest.
    • Log-context projection with 15 fields, for both the array and {records} shapes. An identity projection now fails.
    • The sample_events 10-column cap.
    • Trace activity: rows ordered by bucket, without re-sorting in the fixture; millisecond timestamps; Float64 status codes.
    • SQL validator query forms: COUNT(*), UNION with *, parenthesized *, INT64/UINT64/FLOAT64 casts, VALUES, EXPLAIN, multiple statements.
    • resolve_dataset: ties, case-insensitive selectedDataset, invisible selectedDataset.
    • Trace-activity time field and span-column preconditions.
    • discover_datasets summary: ordering and tie-break, truncated, status: "error", and the 20-item unavailableDatasets cap.
    • Duration overflow in resolve_time_range and contextWindow.
  • resolve_time_range: local today/yesterday boundaries in Asia/Kolkata, and rejection of an unknown zone.
  • sample_events: startTime/endTime must be paired.

Results:

  • cargo fmt --check: clean.
  • cargo clippy --all-targets: exit 0. Its warnings are pre-existing and none are in files this PR touches.
  • cargo clippy -- -D warnings: clean.
  • cargo test (at 77723f97): 870 lib tests passed and 2 were ignored; all integration suites passed; 0 failures. The earlier "860" figure counted only the tests that passed at the port commit, out of 862.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

nikhilsinhaparseable and others added 3 commits October 23, 2026 19:16
Port of upstream parseablehq/parseable 32979cb.

add get_trace_activity to tool catalog
update executor to have tool implementations and reuse in enterprise

Fork conflict resolutions:
- tool_catalog: use upstream's validate_one_of helper (same exactly-one
  semantics and message as the fork's inline check) and keep the fork's
  `const` validation; tool counts become 54 total / 41 read.
- tool_executor: adopt upstream's parser-based validate_tool_sql and
  bounded_tool_sql; keep fork time-expression imports, the Result handling
  for list_dashboards_internal, and the fork's call_tool_registry
  (dashboard tool routing, suspension and Query gates); keep calling
  execute_oss_tool_inner there because the spec is already validated.
- tests: merge both suites; update the fork bounded-SQL test to avoid a
  wildcard CTE that upstream's validator now rejects.

Also add tests for get_trace_activity (executed against a DataFusion
fixture), its argument validation, resolve_dataset/get_log_context schema
changes, timezone day boundaries and sample_events range pairing, and
document the new and changed tools in docs/tool-api.md.

(cherry picked from commit 32979cb)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`validate_type` ignored array-valued `type` (for example
`["string", "null"]`), so the dashboard operation schemas' nullable and
union fields were never type-checked. Accept a value when it matches any
listed type, and name every allowed type in the error.

Verified the dashboard agent E2E script's create, update, rename,
placement, invalid-layout and protected-field payloads still pass
schema validation, so service-side 422/400 responses are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VibeSWE and others added 2 commits October 24, 2026 14:25
…flow, tests

- list_tool_registry omits root-level oneOf/anyOf/allOf from each listed
  inputSchema, since LLM tool APIs reject them; the catalog schema keeps
  them and call_tool_registry still validates against it.
- log_context_request requires exactly one of log, body or message.
- resolve_time_range and the log-context window use checked chrono
  arithmetic, so huge durations return errors instead of panicking.
- Extract trace_activity_time_field and summarize_dataset_activity so the
  trace preconditions and discover_datasets ranking can be unit tested.
- Tests: no listed root combinators; exactly-one anchor; oneOf error text;
  log-context projection with more than 10 fields; log_context_request
  defaults; sample_events 10-column cap; trace activity ordering with
  millisecond timestamps and Float64 status; SQL validator query forms;
  resolve_dataset ties, case-insensitive and invisible selections;
  duration overflow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Listed inputSchemas omit root combinators; the server still enforces them.
- SQL limits apply to the outermost projection and top-level set-operation
  branches, not CTEs or subqueries; a semicolon followed by a comment fails.
- resolve_dataset: case-insensitive names, the `error` status, and scoring.
- get_log_context: bound pairing and exclusivity, minute rounding of
  windows, and the anchor range check.
- get_trace_activity: `query` is the inner SQL without a time predicate;
  rerun it through the query API with the same startTime and endTime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@VibeSWE

VibeSWE commented Oct 24, 2026 •

Copy link
Copy Markdown
Author

Correction to the port commit message (2f8718d). It says the fork's bounded-SQL test was changed "to avoid a wildcard CTE that upstream's validator now rejects". That's inaccurate. The original input was WITH x AS (SELECT 1) SELECT * FROM x, and it's rejected because of the outer SELECT *; the CTE has no wildcard. Upstream's validate_tool_sql checks only the outermost projection (and each top-level set-operation branch), so a wildcard inside a CTE would still be accepted. The test now uses WITH x AS (SELECT 1 AS value) SELECT value FROM x.

We're not rewriting history to fix the message, since that would need a force-push. This comment records the correction instead, and the PR description has been updated to match.

…-1831

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@VibeSWE
VibeSWE merged commit 6cd70d5 into main Oct 24, 2026
9 checks passed
VibeSWE added a commit that referenced this pull request Oct 24, 2026
…ity-truncation

PR #62 follow-up 14: signal get_trace_activity truncation at 500 rows
VibeSWE added a commit that referenced this pull request Oct 24, 2026
…-tool-gates

PR #62 follow-up 20: document caller gates for execute_oss_tool helpers
VibeSWE added a commit that referenced this pull request Oct 24, 2026
…aset-exact-case

PR #62 follow-up 17: resolve_dataset prefers exact-case selection; correct not_found message
VibeSWE added a commit that referenced this pull request Oct 24, 2026
…ity-exact-range

PR #62 follow-up 15: apply exact time bounds in get_trace_activity
VibeSWE added a commit that referenced this pull request Oct 24, 2026
…aset-token-match

PR #62 follow-up 16: match resolve_dataset names on token boundaries
VibeSWE added a commit that referenced this pull request Oct 24, 2026
…by-collisions

PR #62 follow-up 21: reject repeated and output-colliding trace groupBy fields
VibeSWE added a commit that referenced this pull request Oct 25, 2026
…-projection

PR #62 follow-up 19: run tool SQL ending in '; -- comment'
VibeSWE added a commit that referenced this pull request Oct 25, 2026
…e-range-parsing

PR #62 follow-up 18: fix resolve_time_range parsing gaps
@VibeSWE
VibeSWE deleted the upstream/tool-catalog-1831 branch October 25, 2026 03:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants