feat(tools): route country/region questions to the semantic layer - #107
Conversation
Regional data now exists across activities, memberships, event registrations and enrollments, but the tool description sent all membership questions to query_lfx_lens, so regional membership breakdowns were routed away from the only path that has the data. Carve country/region breakdowns out of that routing rule and add a tip naming the two dimension shapes (country__* for people, organization_lf_region for organization HQ). Membership metrics exceed the 15-metric threshold at which list_metrics inlines dimensions, so the tip also points at get_dimensions explicitly. Both lines live in the main tool description, so clients see them without calling describe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com>
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
There was a problem hiding this comment.
Pull request overview
Updates semantic-layer guidance so regional questions, including memberships, route correctly.
Changes:
- Adds country/region routing and dimension guidance.
- Extends description assertions for regional guidance.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
internal/tools/lens.go |
Updates semantic-layer tool guidance. |
internal/tools/lens_test.go |
Asserts the new description content. |
|
|
||
| Use query_lfx_lens INSTEAD for: | ||
| - All membership questions (memberships model works better with ad-hoc SQL) | ||
| - Membership questions, EXCEPT country/region breakdowns (see the country/region tip) |
There was a problem hiding this comment.
Good catch — fixed in 88c461e. Confirmed against a running server that the action property's description does ship in tools/list next to the tool description, so clients were seeing both the new regional carve-out and the old unconditional "use query_lfx_lens for memberships" instruction.
The schema text now reads "For memberships (except country/region breakdowns), ..." to match. I also added a schemaPropertyDescription helper and assert the registered schema's action description directly, rather than only the tool description, so the two can't drift apart again.
The action property's jsonschema description still told clients to use query_lfx_lens for memberships, with no regional exception. That text ships with tools/list alongside the tool description, so clients were getting contradictory routing guidance and regional membership prompts could still be sent to the wrong tool. Add the same carve-out there, and assert the registered schema's action description rather than only the tool description, so the two cannot drift apart again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
internal/tools/lens.go:164
- The complementary
query_lfx_lensmetadata still says “Always use this tool for: All membership questions” (lines 43–44), and itsinputschema likewise says “Always use for memberships” (line 74). Clients receive those instructions intools/list, so they still conflict with this regional exception and can route regional membership questions to the wrong tool. Update both Lens descriptions to carry the same exception; the new tests currently inspect only the semantic-layer tool and will not detect this contradiction.
- Membership questions, EXCEPT country/region breakdowns (see the country/region tip)
query_lfx_lens claimed "Always use this tool for: All membership questions" in its description, and "Always use for memberships" in its input schema. Both ship with tools/list, so a client saw them alongside the semantic layer's new carve-out and could still route a regional membership question to the SQL agent. Add the same exception to both, and cover them with a test — the existing assertions only inspected query_lfx_semantic_layer, so this contradiction was invisible to them. Descriptions only; the tool's behavior, handler and scoping are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com>
|
Addressed the low-confidence finding from the latest Copilot pass in 50b231f — it was correct.
Scope note: this touches |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
internal/tools/lens.go:201
- “ANY topic” contradicts this same tool description’s unconditional Lens-only cases, such as event sponsorships: a request for sponsorship revenue by region now matches both routing rules. The PR’s stated live regional domains are activities, memberships, event registrations, and enrollments, so please scope this tip (and its test assertion) to the supported domains rather than overriding every exception above.
- Country/region breakdowns belong here for ANY topic — contributors, organizations, memberships, event registrations, enrollments. A person's country uses country__* (e.g. country__lf_region); an organization's HQ uses organization_lf_region. Membership metrics don't inline dimensions, so call get_dimensions with search "country" or "region".
"ANY topic" overrode every routing exception above it, including event sponsorships — so "sponsorship revenue by region" matched both rules, and the tip claimed regional coverage the semantic layer does not have. Bound the tip to the four models that actually carry country data (activities, memberships, event registrations, enrollments) while keeping the routing force that motivated it: these breakdowns belong here even when the topic would otherwise route to query_lfx_lens. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com>
|
Third Copilot pass flagged (low confidence) that "ANY topic" in the regional tip overrode the routing exceptions above it — notably event sponsorships, which the semantic layer genuinely doesn't cover. "Sponsorship revenue by region" matched both rules. Correct, and fixed in 73a81d3. The tip is now bound to the four models that actually carry country data — activities, memberships, event registrations, enrollments — while keeping the routing force that motivated it: those breakdowns belong here even when the topic would otherwise route to query_lfx_lens. The sponsorship exception is left intact. Test assertions updated to match. |
The regional lens is now live across activities, memberships, event registrations and enrollments, but
query_lfx_semantic_layer's description still sent all membership questions toquery_lfx_lens— routing regional membership breakdowns away from the only path that has the data.This carves country/region breakdowns out of that routing rule and adds one tip stating that regional questions belong here for any topic, naming the two dimension shapes:
country__*for a person's country (contributor, attendee, learner) andorganization_lf_regionfor an organization's HQ. The tip also points atget_dimensions, because membership metrics (18) exceed the 15-metric threshold at whichlist_metricsinlines dimensions — so without an explicit call the client never sees the country fields.Both lines sit in the main tool description rather than the
describetext, so clients see them ontools/listwithout an extra round trip (verified against a locally running server). Description-only change: no handler, schema, or routing code touched. Validated withgofmt,go build,go vet,go test ./..., plus extended description assertions.🤖 Generated with Claude Code