Repository navigation
refactor!: install only bub.core by default and streamline the agent kernel - #361
Merged
Merged
Conversation
- Import bub's top-level names lazily so `import bub.core` no longer loads the framework, CLI or settings; accessing them still imports bub.framework first so `.env` is loaded as before. - Move errors, prompt, tracing, streaming and TurnState into bub.core; the old top-level modules remain aliases of the core modules. - Move FileTapeStore's newest-first, deduplicated fuzzy search into bub.builtin.store.FuzzyFileTapeStore, so bub.core no longer needs yaml or rapidfuzz. The builtin tape store keeps the existing behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bub | 75797dc | Commit Preview URL Branch Preview URL |
Oct 15 2026, 10:48 AM |
Add import-linter contracts that forbid bub.core from importing the rest of bub or application-only dependencies, and run them in `make check` and CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 task
frostming
added this pull request to stack #363
October 15, 2026 07:29
frostming
removed this pull request from stack #363
October 15, 2026 07:30
frostming
added this pull request to stack #364
October 15, 2026 07:30
Run the import contracts from .pre-commit-config.yaml instead of a separate `make check` / CI step, so they run on commit and in the existing prek CI job. The hook installs only import-linter and finds the sources through PYTHONPATH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frostming
removed this pull request from stack #364
October 15, 2026 08:09
`pip install bub` now installs only the agent kernel's dependencies (republic, pydantic, loguru, typing-extensions). The framework, CLI, channels, hooks, skills and builtin agent need the new `app` extra. BREAKING CHANGE: install `bub[app]` (e.g. `uv tool install 'bub[app]'`) to get the `bub` command and runtime. Without it, the `bub` script exits with an install hint instead of an ImportError. Plugins should depend on `bub[app]`. - The `bub` script moves to `bub._cli:main`, which checks the app extra's requirements from bub's metadata before importing the CLI. - The dev group depends on `bub[app]`, so `uv sync` keeps a full environment; the Dockerfile, install scripts and `bub install` request the extra, and `logfire` implies it. - CI builds the wheel and checks that a plain install imports bub.core without app dependencies and that `bub` asks for the extra. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The `bub` script is `bub.__main__:main` again. The module no longer imports typer or the framework at import time: `main` checks the app extra first, and `bub.__main__.app` is created on first access, so `from bub.__main__ import app` keeps working for distributions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the lazily created `bub.__main__.app`; `main` checks the app extra and runs `create_cli_app()` directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rename the agent kernel package `bub.core` to `bub.agent`, without a compatibility alias. `bub.store`, `bub.tape`, `bub.tools` and `bub.sidecars` stay as aliases of the `bub.agent` modules. - Move `errors`, `prompt`, `tracing`, `streaming` and `TurnState` back to their original top-level modules and drop the aliases added for them; they need only the kernel's dependencies. - Remove the import-linter hook and contracts. A test still checks that importing `bub.agent` loads none of the `app` extra's dependencies. BREAKING CHANGE: `bub.core` (only in 0.6.0a1) is now `bub.agent`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Import these modules from `bub.agent` instead. BREAKING CHANGE: `bub.store`, `bub.tape`, `bub.tools` and `bub.sidecars` are removed; use `bub.agent.store`, `bub.agent.tape`, `bub.agent.tools` and `bub.agent.sidecars`. `bub.tool` is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restore `bub.store`, `bub.tape`, `bub.tools` and `bub.sidecars` as real modules at their original location, and have `bub.agent` import them, so only the new agent kernel lives in `bub.agent`. `bub.agent` exports its names lazily: `bub.tools` imports `bub.agent.events`, and loading the whole package there would import the agent loop back into `bub.tools`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`bub.agent` no longer re-exports `Tape`, `TapeContext`, `session_tape_name`, `Tool`, `ToolContext` and `tool`; import them from `bub.tape` and `bub.tools`, where they are defined. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rename `bub.agent` back to `bub.core` and move the agent concepts into it: `tape`, `tools`, `store`, `sidecars` and `events`. The top-level aliases `bub.tape`, `bub.tools`, `bub.store` and `bub.sidecars` are removed; `errors`, `prompt`, `streaming`, `tracing` and `turn` stay at the top level. - Load `.env` in `bub.configure`, where settings read the environment, instead of `bub.framework`. All lazy `bub.*` exports, including `bub.tool`, resolve through `_EXPORTS`; only `bub.home` is computed. `bub.hookimpl` no longer loads `.env`. - Add `RunFinished` (status `ok`, `error` or `cancelled`, steps, elapsed time, error). It follows every `RunStarted` when the run's stream closes, even if it is never iterated, and is recorded as `loop.finish`. - Rename events to `*Started` / `*Finished`: `LLMCallStarted`, `LLMCallFinished`, `ToolCallStarted`, `ToolCallFinished` and `ChatFinished`; `LlmCallRequest`, `LlmCallResult` and `LlmCallDecision` become `LLMCall*`. Hook names such as `before_llm_call` are unchanged. - Build one stream per run: `run_on` takes the `invoke_agent` span and a close callback from `start`, so the `_trace_events` wrapper and the second `AsyncStreamEvents` are gone. `run_once` is now an async generator that writes usage into the run's `StreamState` instead of returning its own stream to be copied. BREAKING CHANGE: import `bub.core.tape`, `bub.core.tools`, `bub.core.store` and `bub.core.sidecars` instead of the top-level modules; the event classes and `LlmCall*` payloads are renamed; and `Agent.run_once` returns an async generator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frostming
added a commit
that referenced
this pull request
Oct 18, 2026
…kernel (#361) Users who only embed the agent loop had to install all of Bub, and importing the kernel loaded the framework. Make `bub.core` installable and importable on its own, and tidy the kernel's API while it is in alpha. Packaging: - `pip install bub` installs only the kernel's dependencies (republic, pydantic, loguru, typing-extensions). The framework, CLI, channels, hooks, skills and builtin agent need the new `app` extra; `logfire` implies it. - The `bub` script (`bub.__main__:main`) checks the `app` extra's requirements from bub's metadata and exits with an install hint instead of an ImportError. `bub.__main__` imports nothing from the extra at import time. - The dev group depends on `bub[app]`, so `uv sync` keeps a full environment. The Dockerfile, install scripts and `bub install` request the extra. A new CI job checks that a plain wheel install imports `bub.core` without app dependencies and that `bub` asks for the extra. - `bub` exports its top-level names lazily through one `_EXPORTS` table, and `.env` is loaded by `bub.configure`, where settings read the environment, instead of `bub.framework`. Kernel layout: - `bub.core` holds the agent concepts: agent, commands, context, events, model, recorder, system_prompt, tape, tools, store and sidecars. `bub.tape`, `bub.tools`, `bub.store` and `bub.sidecars` remain aliases of the `bub.core` modules and need only the kernel's dependencies. - `errors`, `prompt`, `streaming`, `tracing` and `turn` stay top-level modules that need only the kernel's dependencies. - The newest-first, deduplicated fuzzy tape search moves from `FileTapeStore` into `bub.builtin.store.FuzzyFileTapeStore`, which `provide_tape_store` returns. The kernel's `FileTapeStore` uses the plain substring query, so `bub.core` needs neither yaml nor rapidfuzz. Agent events: - New `RunFinished` (status `ok`, `error` or `cancelled`, steps, elapsed time, error). It follows every `RunStarted` when the run's stream closes, even if it is never iterated, and is recorded as `loop.finish`. - Events are renamed to `*Started` / `*Finished`: `LLMCallStarted`, `LLMCallFinished`, `ToolCallStarted`, `ToolCallFinished` and `ChatFinished`. The payloads become `LLMCallRequest`, `LLMCallResult` and `LLMCallDecision`; `bub.hooks.interception` keeps the `LlmCall*` names as aliases. Hook names such as `before_llm_call` are unchanged. Runs: - `start` hands its `invoke_agent` span and close callback to `run_on`, which builds the run's only stream. - `run_once` is an async generator that writes usage into the run's `StreamState`. BREAKING CHANGE: install `bub[app]` (e.g. `uv tool install 'bub[app]'`) to get the `bub` command and runtime; plugins should depend on `bub[app]`. The core event classes are renamed, and `Agent.run_once` returns an async generator.
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.
What changed and why
Users who only embed the agent loop had to install all of Bub: the CLI, the Telegram channel, the plugin system, the TUI prompts and their dependencies. Importing the kernel also loaded the framework. This PR makes
bub.coreinstallable and importable on its own, inside the singlebubpackage, and tidies the kernel's API while it is still in alpha.Packaging:
pip install bubinstalls only the kernel (breaking)dependenciesnow holds onlyrepublic,pydantic,loguruandtyping-extensions. Everything else moves to a newappextra:pydantic-settings,pyyaml,pluggy,inquirer-textual,typer,rich,prompt-toolkit,python-telegram-bot,rapidfuzz,aiohttpandhttpx[socks]. The CLI, channels and plugins needbub[app], andlogfireimplies it.bubscript isbub.__main__:main. It readsbub's own metadata, and if anyapprequirement is missing it exits withmissing: …and an install hint instead of anImportError.bub.__main__imports nothing from the extra at import time.devgroup depends onbub[app], souv sync, CI and tox keep a full environment. The Dockerfile (--extra app),install.sh/install.ps1andbub install(uv add --extra app) request the extra.bub/__init__.pyexportsBubFramework,Settings,config,ensure_config,hookimplandtoollazily through one_EXPORTStable, soimport bub.coreworks without the extra..envis now loaded bybub.configure, where settings read the environment, instead ofbub.framework.bub.homestill honors aBUB_HOMEfrom.env;bub.hookimplno longer loads.env.core-only-installCI job builds the wheel and checks two things: a plain install importsbub.corewith no app dependencies, and thebubcommand asks for the extra.Kernel layout (breaking, versus 0.6.0a1 and 0.5.x)
bub.coreholds the agent concepts:agent,commands,context,events,model,recorder,system_prompt,tape,tools,storeandsidecars. The top-level aliasesbub.tape,bub.tools,bub.storeandbub.sidecarsare removed; import frombub.core.*.bub.toolis unchanged.errors,prompt,streaming,tracingandturnstay top-level modules;bub.coreimports them, and they need only the kernel's dependencies.FileTapeStoreintobub.builtin.store.FuzzyFileTapeStore, whichprovide_tape_storereturns. The kernel'sFileTapeStoreuses the plain substring query, sobub.coreneeds neitheryamlnorrapidfuzz, and bub users see no change.Agent events
RunFinishedwithstatus(ok,errororcancelled),steps,elapsed_msanderror. It follows everyRunStartedwhen the run's stream closes, including a stream closed before it is iterated, and is recorded on the tape asloop.finish. Command prompts emit neither, as before.*Started/*Finished:BeforeLlmCall→LLMCallStarted,AfterLlmCall→LLMCallFinished,BeforeToolCall→ToolCallStarted,AfterToolCall→ToolCallFinishedandChatCompleted→ChatFinished. The payloadsLlmCallRequest,LlmCallResultandLlmCallDecisionbecomeLLMCall*. These payloads existed in 0.5.x, so plugins implementingbefore_llm_callthat importLlmCallDecisionmust update. Hook names such asbefore_llm_callare unchanged.Fewer layers in a run
A model run used to pass each event through
AsyncStreamEvents(start) → _trace_events → AsyncStreamEvents(run_on) → _run_steps → AsyncStreamEvents(run_once) → _run_once → _stream_events. It is nowAsyncStreamEvents(run_on) → _run_steps → run_once → _stream_events.starthands itsinvoke_agentspan and tape-closing callback torun_on(new optionalspan/on_closearguments), which builds the run's only stream. A small_RunTrackerrecords the span's output messages and how the run ended.run_onceis an async generator that writes usage into the givenStreamState, replacing the per-step stream whoseusage/errorwere copied into the run's state. It remains the per-step override point.Impacted modules
pyproject.toml,uv.lock,bub/__init__.py,bub.__main__,bub.configure,bub.framework,bub.core.*(now including tape, tools, store, sidecars and events),bub.builtin.store(new),bub.builtin.*,bub.hooks.*,bub.channels.cli,Dockerfile,website/public/install.{sh,ps1},.github/workflows/main.yml, AGENTS.md, README and docs (install, SDK, tools, hooks, types, plugins, observability, deploy, distribution, tape-and-context; en + zh-cn).Verification
make check(lock, prek/ruff, mypy): passeduv run python -m pytest -q: passed. With thetraceextra installed, 645 passed and 1 skipped (dev baseline: 616 passed, 20 skipped without OpenTelemetry). New tests:bub.coreloads no app dependencies.RunFinished: ok, error, early exit, never iterated and commands.bub.coreand the top-level modules work, and no app dependency is importable.bubandpython -m bubexit 1 with thebub[app]hint. Withbub[app]installed,bub --helpworks.Follow-up (not in this PR)
FileTapeStore.fetch_allcallssuper().fetch_all()with the query still attached, so the mixin's JSON substring filter removes typo'd matches before rapidfuzz sees them. That JSON is dumped withensure_ascii=True, so non-ASCII queries never match either.StreamState.erroris never assigned by the agent.🤖 Generated with Claude Code