Skip to content

feat(agent): unify tool failure status contract and remove null-status error paths (T38) - #159

Merged
yuluo-yx merged 2 commits into
agentic-ai-java:mainfrom
konlue:feat/t38-tool-outcome-status
Oct 15, 2026
Merged

yuluo-yx merged 2 commits into
agentic-ai-java:mainfrom
konlue:feat/t38-tool-outcome-status

Conversation

@konlue

@konlue konlue commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Describe what this PR does / why we need it

Part 1 of T38 (#141), following the "keep it simple" agreement: unify the tool failure status contract so error exits produce a machine-readable status instead of a status-less response, and define the canonical outcome vocabulary.

Scope note (updated after merging main): the big model-SDK decoupling refactor (#152) landed mid-review and removed the ToolExecutionExceptionProcessor path in AgentToolNode — one of the three null-status pseudo-success exits this PR targeted — replacing it with the neutral ToolResult contract that already maps failures to the error status. This PR now covers the remaining ground:

  • ToolOutcome: a constants class defining the canonical outcome vocabulary from the issue (success / error / validation-denied / auth-denied / business-failed / transient / timeout / cancelled / uncertain). SUCCESS and ERROR keep the existing string values; the reserved values are declared now and wired to their producers in the follow-up (PR2: business-failure mapping + model-visible consistency).
  • ToolRetryInterceptor: the exhausted-retries exit returns an explicit error response instead of of(...).
  • ToolErrorInterceptor: the swallowed-handler-exception exit likewise returns an explicit error response.
  • ToolCallResponse.of() javadoc now documents its status-less historical shape and points new code at the status-carrying factories. The ERROR_STATUS constant (introduced on main by refactor: decouple ARGI runtime from model SDKs #152 and independently by this branch) is kept as a single declaration.

Does this pull request fix one issue?

NONE

Describe how you did it

  • New ToolOutcome constants class next to ToolCallResponse; the status field stays a String (claim-question 1: starting with the low-invasive option).
  • ToolRetryInterceptor / ToolErrorInterceptor now return ToolCallResponse.error(...) on their failure exits, so those responses carry status = "error" plus the standard error/errorMessage metadata.
  • Merged with main's refactor: decouple ARGI runtime from model SDKs #152 refactor (merge commit included): the removed processor path and its test were adapted to the neutral API; an async symmetric test keeps the sync/async consistency acceptance criterion covered.

Describe how to verify it

  • AgentToolNodeFailureMetadataTest: sync and async wrapped-exception paths both assert status == "error" / isError() == true with the wrapped exception type in metadata (the async case is new here).

  • ToolRetryInterceptorUnitTest: exhausted-retry response asserts the error status and error/errorMessage metadata.

  • New ToolErrorInterceptorTest (2 cases) covering pass-through and the explicit error shape.

  • Run:

    ./mvnw -pl argi-agent-framework -am test -DskipTests=false \
      -Dtest='AgentToolNodeFailureMetadataTest,ToolRetryInterceptorUnitTest,ToolErrorInterceptorTest,ToolCallResponseTest' \
      -Dsurefire.failIfNoSpecifiedTests=false

Special notes for reviews

  • Behavior change: responses on the two fixed exits now have status = "error" (previously null), so isError() flips from false to true there. The retry interceptors only compare against SUCCESS_STATUS — a null status already counted as non-success — so retry behavior is unchanged.
  • The exhausted-retry and swallowed-error responses now carry an Error: result prefix plus error/errorMessage metadata (the formatted message is still in the result).
  • Full regression after merging main shows only pre-existing Windows failures (the 5 skills/prompt-template cases reproduce identically on clean main ee72a721c, as do the 3 FileSystemStoreTest drive-root errors).
  • Question 2 from the claim comment (model-visible outcome in ToolResponseMessage metadata) stays with the follow-up PR together with the business-outcome mapper.

@yuluo-yx

Copy link
Copy Markdown
Contributor

hi @konlue 合并了一个比较大的 pr,有些冲突,可以解决下 🌹

Conflict resolution notes:
- ToolCallResponse: kept main's from(ToolCall, ToolResult) factory and merged the
  duplicated ERROR_STATUS constant into one declaration with the ToolOutcome javadoc.
- AgentToolNode: main's agentic-ai-java#152 refactor removed the ToolExecutionExceptionProcessor
  path entirely (it was the null-status pseudo-success path T38 targeted);
  the old sync catch blocks and processedFailureResponse helper were dropped
  in favour of the new neutral ToolResult error contract.
- AgentToolNodeFailureMetadataTest: took main's rewritten CompletionException
  test (already asserts ERROR_STATUS) and added an async symmetric case for
  the sync/async consistency acceptance criterion.

Full regression after merge: only pre-existing Windows failures remain
(FileSystemStoreTest x3, ArgiSkillChatClientTest/SkillPromptTemplateTest x3/
PromptTemplateTest x1 reproduce identically on clean origin/main ee72a72).
@konlue

konlue commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

hi @konlue 合并了一个比较大的 pr,有些冲突,可以解决下 🌹

@yuluo-yx 已解决 🌹

merge 了 main(#152 重构 + #157/#158),冲突在 ToolCallResponse / AgentToolNode / 失败元数据测试三处:#152 移除的 ToolExecutionExceptionProcessor 路径(正是本 PR 要修的 null-status 出口之一)以重构的新结构为准,我的状态契约改动收敛到剩余两个出口(重试耗尽 / 异常吞掉)+ ToolOutcome 词汇表,另补了 async 对称测试。

PR 描述已同步更新。

@yuluo-yx
yuluo-yx merged commit 283fadb into agentic-ai-java:main Oct 15, 2026
7 checks passed
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