Skip to content

feat: add ClaudeAgentRunner using claude-agent-sdk#245

Merged
DhavalRepo18 merged 20 commits intomainfrom
feat/claude-agent-sdk-runner
Apr 7, 2026
Merged

feat: add ClaudeAgentRunner using claude-agent-sdk#245
DhavalRepo18 merged 20 commits intomainfrom
feat/claude-agent-sdk-runner

Conversation

@ShuxinLin
Copy link
Copy Markdown
Collaborator

$(cat <<'EOF'
Closes #244

Summary

  • Adds src/agent/claude_agent/ subpackage with ClaudeAgentRunner that implements the AgentRunner ABC using the claude-agent-sdk agentic loop
  • Wires the same IoT / FMSR / TSFM / utilities / WO MCP servers as stdio MCP servers via ClaudeAgentOptions.mcp_servers — no custom plan loop needed
  • Adds a claude-agent CLI entry point backed by agent.claude_agent.cli:main
  • Adds claude-agent-sdk to project dependencies
  • Exports ClaudeAgentRunner from agent.__init__
  • 7 unit tests (all pass, no real API calls required)

Usage

uv run claude-agent "What sensors are on Chiller 6?"
uv run claude-agent --model claude-opus-4-6 --max-turns 20 "List failure modes for pumps"
uv run claude-agent --json "What is the current time?"

Or programmatically:

import anyio
from agent.claude_agent import ClaudeAgentRunner

runner = ClaudeAgentRunner()
result = anyio.run(runner.run, "What assets are at site MAIN?")
print(result.answer)

Test plan

  • uv run pytest src/agent/claude_agent/tests/ -v — 7 passed
  • uv run pytest src/ -v -k "not integration" — 185 passed (1 pre-existing CouchDB failure unrelated to this PR)
    EOF
    )

ShuxinLin added 20 commits April 6, 2026 16:13
Closes #244

- Add `src/agent/claude_agent/` subpackage with `ClaudeAgentRunner`
  that implements `AgentRunner` via the claude-agent-sdk agentic loop
- Wire the same IoT/FMSR/TSFM/utilities/WO MCP servers as stdio
  servers via `ClaudeAgentOptions.mcp_servers`
- Add `claude-agent` CLI entry point (`agent.claude_agent.cli:main`)
- Add `claude-agent-sdk>=0.0.14` to project dependencies
- Export `ClaudeAgentRunner` from `agent.__init__`
- Add 7 unit tests (all pass, no real API calls)

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
…_execute/models

- Delete src/agent/models.py
- Add AgentResult to src/agent/plan_execute/models.py alongside Plan/PlanStep/StepResult
- Update AgentRunner ABC and all subclasses to use AgentResult
- Export AgentResult from agent.__init__

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
…stratorResult for plan-execute

- src/agent/models.py: AgentResult(question, answer, history: Any)
  — thin base result for all AgentRunner subclasses; history type TBD
- src/agent/plan_execute/models.py: OrchestratorResult(question, answer, plan, history: list[StepResult])
  — kept for PlanExecuteRunner with full plan/step-result detail
- ClaudeAgentRunner.run() returns AgentResult(history=None)
- AgentRunner ABC return type is AgentResult

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
…OPIC_BASE_URL

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
…tellm_proxy/ model ID

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
…y/ model ID

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
…tool use

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
…K message stream

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
…execute

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Add output field to ToolCall and register a PostToolUse hook so the
MCP server response is attached to each ToolCall in the trajectory.
Tool outputs are flushed onto the previous turn's calls when the next
AssistantMessage or ResultMessage arrives.

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
The SDK passes tool_response as a plain string, not a nested dict.
The hook now handles both string and dict responses gracefully.

Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
@ShuxinLin ShuxinLin requested a review from DhavalRepo18 April 6, 2026 21:02
@ShuxinLin
Copy link
Copy Markdown
Collaborator Author

@DhavalRepo18 I have implement a preliminary trajectory in claude agent (related to #239).

text: str
tool_calls: list[ToolCall] = field(default_factory=list)
input_tokens: int = 0
output_tokens: int = 0
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are reasoning model and their output schema are different. Pleasr see LLM vs LRM.

@DhavalRepo18 DhavalRepo18 merged commit 9e7a4d0 into main Apr 7, 2026
1 check passed
@ShuxinLin ShuxinLin deleted the feat/claude-agent-sdk-runner branch April 7, 2026 18:13
caroline-cahill pushed a commit to jasonlee-1024/AssetOpsBench that referenced this pull request Apr 29, 2026
feat: add ClaudeAgentRunner using claude-agent-sdk
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.

feat: add ClaudeAgentRunner using claude-agent-sdk

2 participants