This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-7821-957b6c965640c07ace034eb9edcae53ef974324a in repository https://gitbox.apache.org/repos/asf/texera.git
commit 28594efe6cf7628be81bd9ff8939199417773ecd Author: roshiiiiz <[email protected]> AuthorDate: Sun Sep 20 00:43:43 2026 +0000 fix(agent-service): reject maxSteps 0 in settings (#7821) <!-- Thanks for sending a pull request (PR)! Here are some tips for you: 1. If this is your first time, please read our contributor guidelines: [Contributing to Texera](https://github.com/apache/texera/blob/main/CONTRIBUTING.md) 2. Ensure you have added or run the appropriate tests for your PR 3. If the PR is work in progress, mark it a draft on GitHub. 4. Please write your PR title to summarize what this PR proposes, we are following Conventional Commits style for PR titles as well: - `fix` is for behavior that worked before and no longer does; adding or removing a functionality, or reworking one so that user-facing behavior intentionally changes, is a `feat`; a change that leaves the user-facing behavior unchanged is a `refactor`. - A test-only PR is `test(<module>): ...`; repairing a broken test is `fix(test, <module>): ...`. - A dependency bump is `fix(deps, <module>): ...` when it patches a CVE and `chore(deps, <module>): ...` otherwise; GitHub Actions bumps take `ci` as their module, e.g. `chore(deps, ci): ...`. - A PR targeting a release branch appends the version as the last scope component, e.g. `fix(deps, frontend, v1.2): ...`. See CONTRIBUTING.md for the full convention. 5. Be sure to keep the PR description updated to reflect all changes. --> ### What changes were proposed in this PR? <!-- Please clarify what changes you are proposing. The purpose of this section is to outline the changes. Here are some tips for you: 1. If you propose a new API, clarify the use case for a new API. 2. If you fix a bug, you can clarify why it is a bug. 3. If it is a refactoring, clarify what has been changed. 3. It would be helpful to include a before-and-after comparison using screenshots or GIFs. 4. Please consider writing useful notes for better and faster reviews. --> This PR fixes a critical bug where providing a `maxSteps` value of `0` or negative would cause the agent execution loop to hang indefinitely. Changes made: - Updated the Elysia validation schema in `server.ts` to enforce a `{ minimum: 1 }` rule for `maxSteps`. This guarantees the server rejects invalid limits at the API boundary with a 400 Bad Request. - Added a fallback boundary clamp using `Math.max(1, updates.maxSteps)` in `texera-agent.ts` during agent settings updates. This guarantees internal state strictly enforces the limit even if schema validation is bypassed. - Added a new automated unit test to `texera-agent.spec.ts` asserting that `maxSteps` is correctly clamped to 1 if `0` or a negative number is provided. ### Any related issues, documentation, discussions? <!-- Please use this section to link other resources if not mentioned already. 1. If this PR fixes an issue, please include `Fixes #1234`, `Resolves #1234` or `Closes #1234`. If it is only related, simply mention the issue number. 2. If there is design documentation, please add the link. 3. If there is a discussion in the mailing list, please add the link. --> Closes #7484 ### How was this PR tested? <!-- If tests were added, say they were added here. Or simply mention that if the PR is tested with existing test cases. Make sure to include/update test cases that check the changes thoroughly including negative and positive cases if possible. If it was tested in a way different from regular unit tests, please clarify how you tested step by step, ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future. If tests were not added, please describe why they were not added and/or why it was difficult to add. --> Tested manually via local API and verified with automated unit tests: 1. Booted the local `agent-service` via `bun run dev`. 2. Issued a `PATCH /api/agents/:id/settings` HTTP request passing `{"maxSteps": 0}`. 3. Verified the server correctly rejected the payload with a `400 Bad Request` schema validation error ("Expected number to be greater or equal to 1"). 4. Ran the automated `agent-service` test suite (`bun test`) which executes the new dedicated unit test asserting the clamping behavior. The entire suite successfully passed. ### Was this PR authored or co-authored using generative AI tooling? <!-- If generative AI tooling has been used in the process of authoring this PR, please include the phrase: 'Generated-by: ' followed by the name of the tool and its version. If no, write 'No'. Please refer to the [ASF Generative Tooling Guidance](https://www.apache.org/legal/generative-tooling.html) for details. --> Generated-by: Antigravity (DeepMind) <img width="897" height="301" alt="image" src="https://github.com/user-attachments/assets/3c6b76c9-0fd2-4609-9a9b-1187e7f19ca0" /> ========================================================================================================================================================================================================================================================= <img width="917" height="402" alt="image" src="https://github.com/user-attachments/assets/7a01398e-0db2-4088-a32f-9f810458d692" /> --------- Co-authored-by: Xuan Gu <[email protected]> --- agent-service/src/agent/texera-agent.spec.ts | 11 +++++++++++ agent-service/src/agent/texera-agent.ts | 2 +- agent-service/src/server.ts | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/agent-service/src/agent/texera-agent.spec.ts b/agent-service/src/agent/texera-agent.spec.ts index 7c99ffaa3e..d173fe7ae0 100644 --- a/agent-service/src/agent/texera-agent.spec.ts +++ b/agent-service/src/agent/texera-agent.spec.ts @@ -223,6 +223,17 @@ describe("TexeraAgent", () => { expect(after.maxOperatorResultCharLimit).toBe(before.maxOperatorResultCharLimit); }); + test("clamps maxSteps to a minimum of 1 and truncates fractions", () => { + agent.updateSettings({ maxSteps: 0 }); + expect(agent.getSettings().maxSteps).toBe(1); + + agent.updateSettings({ maxSteps: -5 }); + expect(agent.getSettings().maxSteps).toBe(1); + + agent.updateSettings({ maxSteps: 2.5 }); + expect(agent.getSettings().maxSteps).toBe(2); + }); + test("returns a new settings object (mutating primitive fields does not stick)", () => { agent.getSettings().maxSteps = 12345; diff --git a/agent-service/src/agent/texera-agent.ts b/agent-service/src/agent/texera-agent.ts index 34b5f32f98..b98c812b0b 100644 --- a/agent-service/src/agent/texera-agent.ts +++ b/agent-service/src/agent/texera-agent.ts @@ -379,7 +379,7 @@ export class TexeraAgent { this.settings.disabledTools = updates.disabledTools; } if (updates.maxSteps !== undefined) { - this.settings.maxSteps = updates.maxSteps; + this.settings.maxSteps = Math.max(1, Math.trunc(updates.maxSteps)); } if (updates.allowedOperatorTypes !== undefined) { this.settings.allowedOperatorTypes = updates.allowedOperatorTypes; diff --git a/agent-service/src/server.ts b/agent-service/src/server.ts index 42247399e8..ae358293fc 100644 --- a/agent-service/src/server.ts +++ b/agent-service/src/server.ts @@ -242,7 +242,7 @@ const agentsRouter = new Elysia({ prefix: "/agents" }) toolTimeoutSeconds: t.Optional(t.Number()), executionTimeoutMinutes: t.Optional(t.Number()), disabledTools: t.Optional(t.Array(t.String())), - maxSteps: t.Optional(t.Number()), + maxSteps: t.Optional(t.Integer({ minimum: 1 })), allowedOperatorTypes: t.Optional(t.Array(t.String())), }) ), @@ -382,7 +382,7 @@ const agentsRouter = new Elysia({ prefix: "/agents" }) operatorResultSerializationMode: t.Optional(t.Literal("tsv")), toolTimeoutSeconds: t.Optional(t.Number()), executionTimeoutMinutes: t.Optional(t.Number()), - maxSteps: t.Optional(t.Number()), + maxSteps: t.Optional(t.Integer({ minimum: 1 })), disabledTools: t.Optional(t.Array(t.String())), allowedOperatorTypes: t.Optional(t.Array(t.String())), }),
