You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@texera.apache.org by gi...@apache.org on 2026/09/20 00:44:06 UTC
(texera) 01/01: fix(agent-service): reject maxSteps 0 in settings (#7821)
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 <ro...@gmail.com>
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 <16...@users.noreply.github.com>
---
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())),
}),