refactor: deduplicate models, LiteLLM helpers, prompts, and CLI across agent runners#273
Merged
ShuxinLin merged 4 commits intofeat/otel-observabilityfrom Apr 23, 2026
Merged
Conversation
The three SDK runners (claude-agent, openai-agent, deep-agent) each
shipped byte-identical Trajectory/TurnRecord/ToolCall dataclasses and
their own _resolve_model / _LITELLM_PREFIX helpers. Consolidates into:
- src/agent/models.py: canonical ToolCall, TurnRecord, Trajectory
alongside the existing AgentResult.
- src/agent/_litellm.py: shared LITELLM_PREFIX + resolve_model().
- Removed src/agent/{claude,openai,deep}_agent/models.py.
- Collapsed six duplicated per-runner _resolve_model tests into one
parametrized suite at src/agent/tests/test_litellm.py.
Net -110 lines, no behaviour change.
Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
- src/agent/_prompts.py: single AGENT_SYSTEM_PROMPT used by the three SDK runners (claude-agent, openai-agent, deep-agent). plan_execute keeps its own planning/summarisation prompts. - src/agent/_cli_common.py: setup_logging, add_common_args, print_trajectory, print_answer, print_result. The three SDK CLIs now only encode their prog name, default model, epilog text, and runner-specific arg (--max-turns vs --recursion-limit). - Extract _WATSONX_PREFIX constant in LiteLLMBackend. Net -110 lines; each CLI shrinks from ~140 LoC to ~60 LoC. Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
2 tasks
refactor: share system prompt and CLI boilerplate
- src/agent/_cli_common.py: new run_sdk_cli(service_name, build_parser, run_coro) that bundles dotenv → parse → logging → init_tracing → asyncio.run. The three SDK main() bodies shrink from 9 lines each to one. - DeepAgentRunner._chat_model is now a cached_property so _build_chat_model runs once per runner instance instead of once per run(). Matches the ClaudeAgentRunner / OpenAIAgentRunner pattern of pre-building per-instance config, with lazy init so constructor tests don't need env set. Signed-off-by: Shuxin Lin <linshuhsin@gmail.com>
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidation pass over the four agent runners. Follow-up to #272 (OTEL tracing) and supersedes #274 (which was merged into this branch).
Summary
src/agent/models.py— canonicalToolCall/TurnRecord/Trajectoryused by every SDK runner. Three byte-identical per-runnermodels.pyfiles deleted.src/agent/_litellm.py—LITELLM_PREFIX+resolve_model(). Three copies collapsed to one.src/agent/_prompts.py—AGENT_SYSTEM_PROMPT. Three identical 7-line prompts collapsed to one.src/agent/_cli_common.py—setup_logging,add_common_args,print_trajectory,print_answer,print_result, andrun_sdk_cli(bundlesload_dotenv→ parse → logging → OTEL init →asyncio.run). Each SDK CLI shrinks from ~140 LoC to ~55 LoC; themain()body is now one line.src/llm/litellm.py— extracted_WATSONX_PREFIXconstant.DeepAgentRunner._chat_model— now acached_propertyso_build_chat_modelruns once per runner instance instead of once perrun()call. Matches the per-instance-config pattern of the other two SDK runners, with lazy init so constructor tests don't need env set._resolve_modeltests consolidated into one parametrized suite atsrc/agent/tests/test_litellm.py. Subpackage__init__.pyfiles slimmed to re-export only the runner class.Net -217 lines across 22 files, no behavior change.
Test plan
uv run pytest src/ -k \"not integration\"— 255 pass, 0 failures.uv run pytest src/agent src/observability -v— 139 pass.uv run {claude,openai,deep}-agent --helpall render correctly.models.pymodules.Out of scope
_build_mcp_servers/_build_mcp_connections— each SDK requires a different output shape (claude-agent-sdk dict, langchain spec, OpenAIMCPServerStdio); forcing a common adapter would be more complex than three ~10-line helpers.src/tmp/cleanup — user is handling separately.