Skip to content

Make heavy integrations optional dependencies (lite core install) - #196

Merged
EvilFreelancer merged 3 commits into
mainfrom
claude/library-lite-optional-deps-f45e8e
Sep 16, 2026
Merged

EvilFreelancer merged 3 commits into
mainfrom
claude/library-lite-optional-deps-f45e8e

Conversation

@hijera

@hijera hijera commented Aug 27, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

pip install sgr-agent-core pulls ~75 packages transitively, though the SGR core — agent loop, schemas, config, registries, skills — uses five of them. tavily, fastmcp, fastapi/uvicorn, agent-client-protocol and langfuse are all hard requirements today, so anyone who wants "just SGR" gets the MCP stack, a web framework, an OpenTelemetry stack, cryptography, tiktoken, numpy, pywin32 and the rest.

What changed

Core install is now PyYAML, pydantic, pydantic-settings, openai, httpx[socks] — 21 packages. Everything else is an extra:

Extra Adds Needed for
mcp fastmcp, jambo MCP servers under the mcp: config key
search tavily-python WebSearchTool with engine: tavily, ExtractPageContentTool
server fastapi, uvicorn the sgr HTTP API server
acp agent-client-protocol the sgracp stdio server
langfuse langfuse langfuse.enabled: true
all all of the above the pre-existing install profile
examples all + rank-bm25 what examples/ need

Missing extras surface as an actionable message, never a bare ModuleNotFoundError:

$ sgr --config-file config.yaml
The 'sgr' HTTP server requires the optional 'uvicorn' package, which is not installed.
Install it with:  pip install 'sgr-agent-core[server]'

Decoupling notes

  • fastmcp was the hard part. AgentConfig.mcp was typed with fastmcp's MCPConfig, and a pydantic field type cannot be made lazy. Core now ships its own lightweight MCPConfig (sgr_agent_core/mcp_config.py) with the same shape, including the wrap-servers-at-root validator; mcp_service converts it to the fastmcp model only when servers are actually configured. agent_def.mcp had exactly one consumer, so the change is contained.
  • tavily moves inside _search_tavily and _extract. The brave and perplexity engines already speak plain HTTP, so web search still works on a core-only install.
  • sgr / sgracp report the missing extra and exit 1. This required acp/__init__.py to resolve its exports through PEP 562 __getattr__ — otherwise the subpackage import died before the entrypoint could say anything useful.
  • langfuse was already imported lazily in agent_factory; it was simply listed as a hard dependency for no reason. Only the error text changed.
  • python-dateutil is unused anywhere in the repo and is dropped. rank-bm25 is used only by examples/progressive_discovery and moves to the examples extra.

Docker / CI

  • Dockerfile installs .[examples] — the image keeps its full dependency set, including rank-bm25 for the bundled examples. No capability change.
  • New test-core-only CI job installs bare core and fails if an optional package is present or gets imported at module scope. Without it, one stray top-level import silently puts every heavy dependency back.

Second commit: unrelated CI breakage, fixed here to get green

test_acp_new_session_advertises_agent_and_model_options was already failing on main before this PR (verified by running it against the previous commit). agent-client-protocol 0.12 narrowed SessionConfigOptionSelect.category to "mode" | "model" | "model_config" | "thought_level" | dict, and its schema salvages invalid values to None rather than raising — so the bridge's category="_agent" was being silently dropped.

"_agent" was never a standard value, so clients already treated the option as uncategorised. The fix removes it and asserts None, matching what clients actually see. The model selector keeps category="model", which is still valid. Happy to split this out if you'd rather land it separately.

Verification

Clean venv, pip install .:

  • 21 dependencies (was ~75); no fastmcp / tavily / fastapi / uvicorn / acp / langfuse / rank-bm25 / dateutil installed or imported
  • AgentFactory.create builds a working SGRToolCallingAgent; all 13 tools register
  • sgrsh --help works; sgr and sgracp name their extra and exit 1
  • pip install ".[mcp,search]" restores the fastmcp client and Tavily; server/acp/langfuse stay absent
  • self-referential extras (all, examples, tests, dev) resolve correctly (needs pip ≥ 21.2)

New tests/test_optional_deps.py — 26 tests covering install hints, the core MCPConfig, both tavily call sites, MCP with fastmcp blocked, and both entrypoints. CI is green: 614 passed, and the suite now also runs clean on Windows (it did not before this branch).

Reviewer notes

  • This is a breaking change. Upgrading without extras loses the sgr server and MCP. Worth a minor version bump and a release note pointing at pip install "sgr-agent-core[all]".
  • openai deliberately stays in core. Its types (ChatCompletionMessageParam, ChatCompletionChunk, AsyncOpenAI) run through base_agent, stream, models, prompt_loader and the public API; removing it means rewriting the core typing, not adding a lazy import.
  • One test was genuinely broken by this change and is fixed: test_extract_calls_tavily_api patched the now-absent module attribute AsyncTavilyClient; it patches tavily.AsyncTavilyClient at the source instead.
  • The third commit fixes a separate pre-existing problem: GlobalConfig caches its instance on the class, and TestLangfuseConfiguration cleared that cache before building a config with langfuse.enabled=True but never after, so every later test inherited it. On a machine without the optional langfuse package that surfaced as 23 failures in test_agent_factory.py; on CI, where langfuse is installed, the pollution was silent — later tests ran with tracing enabled and passed for the wrong reason. An autouse fixture in conftest.py now resets the singleton around every test, making the suite order-independent. Happy to split this out too.

рџ¤– Generated with Claude Code

hijera and others added 3 commits August 27, 2026 08:01
`pip install sgr-agent-core` pulled ~75 packages transitively, though the
SGR core -- agent loop, schemas, config, registries, skills -- used five of
them. tavily, fastmcp, fastapi/uvicorn, agent-client-protocol and langfuse
were all hard requirements.

Core now installs PyYAML, pydantic, pydantic-settings, openai and
httpx[socks] only: 21 packages. Integrations move to extras (mcp, search,
server, acp, langfuse, plus meta-extras all/examples) and are imported at
call time, so a missing one surfaces as an actionable message naming the
install command instead of a bare ModuleNotFoundError.

Decoupling notes:

- AgentConfig.mcp was typed with fastmcp's MCPConfig, which a lazy import
  cannot fix. Core now ships its own lightweight MCPConfig (same shape,
  including the wrap-servers-at-root validator); mcp_service converts it to
  the fastmcp model only when servers are actually configured.
- tavily moves inside _search_tavily and _extract. The brave and perplexity
  engines already speak plain HTTP, so web search still works on core.
- sgr and sgracp report the missing extra and exit 1. This needed
  acp/__init__.py to resolve its exports through PEP 562 __getattr__,
  otherwise the subpackage import itself died first.
- python-dateutil was unused anywhere and is dropped; rank-bm25 is used only
  by examples/progressive_discovery and moves to the examples extra.

Docker installs .[examples] so the image keeps its full dependency set. A
new CI job installs bare core and fails if a top-level import of an optional
package creeps back in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The agent selector passed category="_agent". agent-client-protocol 0.12
narrowed SessionConfigOptionSelect.category to "mode" | "model" |
"model_config" | "thought_level" | a free-form dict, and its schema salvages
invalid values to None instead of raising -- so the option silently lost its
category and test_acp_new_session_advertises_agent_and_model_options failed.

"_agent" was never a standard value, so clients already treated it as an
unknown category. Drop it and assert None, which matches what they see. The
model selector keeps category="model", which is still valid.

Pre-existing on main: reproduces on the commit before the extras split.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GlobalConfig caches its instance on the class, and TestLangfuseConfiguration
clears that cache before building a config with langfuse.enabled=True but
never after -- so every test that ran later inherited it.

Where the optional langfuse package is absent that surfaced as 23 failures in
test_agent_factory.py, since AgentFactory._create_client then took the
Langfuse path and raised. Where langfuse is installed, as on CI, the same
pollution was silent: later tests ran with tracing enabled and passed for the
wrong reason.

An autouse fixture resets the singleton on both sides of every test. The
suite is now order-independent and green on Windows too: 614 passed.

The per-test resets already scattered through three test modules are left
alone -- they are redundant now but harmless.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@EvilFreelancer
EvilFreelancer merged commit 1ad9641 into main Sep 16, 2026
3 checks passed
hijera added a commit to googio/sgr-agent-core that referenced this pull request Sep 16, 2026
vamplabAI#196 documents brave and perplexity as the engines that work without the
[search] extra; serply also only needs httpx, so add it to those lists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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