feat(agent-service): add stateless /recommend endpoint (V1 hardcoded)#6436
feat(agent-service): add stateless /recommend endpoint (V1 hardcoded)#6436gupta-sahil01 wants to merge 1 commit into
Conversation
Ambient operator recommender, Version 1 (apache#5240). Adds a stateless POST /api/recommend that, given the operator a user just added, returns a short ranked list of likely next operators for the canvas to render as ghost suggestions. V1 ranks from a hardcoded rule table (no LLM call, no persistent state), so it ships and validates the full pipeline at zero API cost, per the discussion's V1/V2 split. Suggestions are validated against the live operator catalog (WorkflowSystemMetadata) when available, so a stale rule degrades to "not suggested" rather than a broken ghost. The response carries a `strategy` discriminator ("hardcoded") so V2 can swap in a small LLM behind the same request/response shape without a breaking change.
Automated Reviewer SuggestionsBased on the
|
|
👋 Thanks for your first contribution to Texera, @gupta-sahil01! If you're looking for a good place to start, browse issues labeled You can drive common housekeeping yourself by commenting one of these commands on its own line:
Each command must match exactly: |
|
Really clean V1 — the hardcoded/strategy-discriminator split and the catalog validation are a nice way to ship the whole pipeline at zero API cost. Two forward-looking notes, both about what the endpoint
Neither blocks V1 — just wanted to surface them while the request shape is still easy to evolve. |
| .post("/", ({ body }) => recommendOperators(body as RecommendationRequest, WorkflowSystemMetadata.getInstance()), { | ||
| body: t.Object({ | ||
| operatorType: t.String({ minLength: 1 }), | ||
| existingOperatorTypes: t.Optional(t.Array(t.String())), | ||
| limit: t.Optional(t.Number()), | ||
| }), | ||
| }); |
| test("rejects an empty operatorType as 400", async () => { | ||
| const res = await postRecommend({ operatorType: "" }); | ||
| expect(res.status).toBe(400); | ||
| }); | ||
|
|
| set.status = 500; | ||
| return { error: errorMessage || "Internal server error" }; |
| /** Recommended operator type; always a real, catalog-known type. */ | ||
| operatorType: string; |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6436 +/- ##
============================================
+ Coverage 70.17% 70.28% +0.10%
Complexity 3387 3387
============================================
Files 1142 1145 +3
Lines 44844 45017 +173
Branches 4949 4949
============================================
+ Hits 31468 31638 +170
- Misses 11739 11742 +3
Partials 1637 1637
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Ambient operator recommender, Version 1 (#5240). Adds a stateless POST /api/recommend that, given the operator a user just added, returns a short ranked list of likely next operators for the canvas to render as ghost suggestions.
V1 ranks from a hardcoded rule table (no LLM call, no persistent state), so it ships and validates the full pipeline at zero API cost, per the discussion's V1/V2 split. Suggestions are validated against the live operator catalog (WorkflowSystemMetadata) when available, so a stale rule degrades to "not suggested" rather than a broken ghost. The response carries a
strategydiscriminator ("hardcoded") so V2 can swap in a small LLM behind the same request/response shape without a breaking change.What changes were proposed in this PR?
Backend half (PR 1 of 2) of the ambient operator recommender, Version 1 — see #6293 (design discussion #5240).
Adds a stateless
POST /api/recommendendpoint toagent-service: given the operator a user just added to the canvas, it returns a short ranked list of likely next operators for the frontend to render as ghost suggestions.WorkflowSystemMetadata) when available, so a stale rule degrades to "not suggested" rather than a broken suggestion.strategydiscriminator ("hardcoded"/"llm") so V2 can swap in a small-LLM ranker behind the same request/response shape without a breaking change.The frontend that consumes this endpoint (ghost rendering, opt-in flag, click-to-materialize) follows in PR 2.
Any related issues, documentation, discussions?
How was this PR tested?
bun testinagent-service/— 131 passed, including newsrc/recommender/recommender.spec.tsandsrc/server.recommend.spec.tscovering ranking order, catalog validation, terminal sinks (no suggestions), limit clamping, and 400s on malformed/empty input. Also verified live:POST /api/recommendwith{"operatorType":"CSVFileScan"}returns the expected ranked JSON.Was this PR authored or co-authored using generative AI tooling?
Generated-by: Anthropic Claude (Claude Code)