ADFA-5096 | Split the AI backends into standalone plugins - #65
Conversation
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
141696d to
af8028c
Compare
638fcf8 to
4e1d431
Compare
25b8ed5 to
b983b3b
Compare
4e1d431 to
2e66175
Compare
b983b3b to
78316c7
Compare
|
This depends on appdevforall/CodeOnTheGo#1660 landing first |
2e66175 to
4f97f44
Compare
e1c3ffc to
3304742
Compare
3304742 to
37c4398
Compare
Extract the Gemini and local-LLM backends out of ai-assistant into their own plugins, each owning its settings behind a registry in ai-core, so a backend can be installed and enabled without dragging the others in.
Merge the chat UI, tool layer and view models into ai-core and delete the ai-assistant project, so the agent ships as one plugin.
Replace cross-plugin reads of ai-core's AgentSettings with GeminiPreferences and LocalLlmPreferences, reading the host's LlamaPrefs and every legacy store so unordered plugin activation no longer strands the model path. Move getConfigSpecs/getSettingsFragmentClassName onto ConfigurableBackend, declare supportsHistory, and close the backend on deactivate so disabling one no longer orphans a live instance.
Layer the plugin packages, tag logs per plugin, align both modules with the standard toolchain, and point the llama.cpp submodule at its final path.
Route generateStreamingWithTools by ToolCallingBackend/HistoryCapableBackend instead of the dropped supportsTools/supportsHistory flags, and delete getConfigSpecs from both backends now that each mounts its own settings fragment. Handle the API's new nullability and Role.TOOL: prompt builders omit the tool-call section when the caller parses no envelope, and ChatViewModel reads a completed response's text once.
Remove the ai-core lifecycle listener in dispose(), repoint proguard at the layered packages, raise min_ide_version to 26.32, gate create/update writes on PathGuard, and drop stale ai-assistant references from docs and tooltips.
plugin-api now annotates LlmResponse.text as @nullable, so code-suggestions and speech-to-text no longer compile against it. Fall back to an empty completion and to the existing null return respectively.
1a3b533 to
4cfed82
Compare
There was a problem hiding this comment.
These were reviewed statically and have not been verified on a device.
Three comments land at file level rather than inline because their target lines fall outside the diff hunks (two files are renames, one target is unchanged context); each names its line in the first line of the comment.
Resolve the default backend one way everywhere through AiBackend.preferredId, so opening Agent settings no longer silently persists the alphabetically-first one and the selector, status line and availability check cannot disagree. Keep a single availability-check job, publish backend and currentBackendId volatile, resolve a restored pane by which loader can see the class, restore the Material-transition guard, guard the no-arg listModels against a closed scope, wire the simple-prompt preference into getSystemPrompt, anchor both plugin tooltips so their guides are reachable, and repoint ai-core's proguard rules.
hal-eisen-adfa
left a comment
There was a problem hiding this comment.
Review notes on the backend split. Ten findings, filed inline. Roughly ordered by how much they'd cost to hit:
- F3 — the status label and the routing decision resolve the preferred backend from differently ordered lists, so they can disagree.
- F4 — the settings screen reads the backend registry once in
onCreatewith no retry, so a late-activating backend is permanently invisible. - F6 —
removeAllViews()races the child FragmentManager's pending commit. - F10 — three of the four
LlmInferenceServiceImplentry points hand the backendconfig.backendId == "auto". - F11 —
CancellationExceptionis converted into a catalog failure, defeating the rethrow guard directly above it. - F7, F13 — two migrations that cannot fire on any real device.
- F12 — ai-core's help text hardcodes the two first-party providers, contradicting the "ai-core names no provider" invariant.
- F14, F15 — duplicated constants with hand-maintained sync contracts, plus stale KDoc and dead imports.
Everything below was checked against the source on 29a4224; each comment says what it was verified against.
…plit Resolve the preferred backend through one ordering, refresh the settings selector on resume, and drop the migrations, constants and text that never held.
|
Workflow from this PR branch and build against COGO https://github.com/appdevforall/plugin-examples/actions/runs/32145335224
|

Description
Reshapes the AI plugin set from
ai-core+ai-assistantinto one agent plugin plus one plugin per inference backend, so a user installs only the engine they want and a new backend can ship without touching this repo.ai-core— the Agent chat (tool layer, view models, UI, all folded in fromai-assistant, which is deleted) plus theLlmInferenceServicerouter. Ships no model; discovers installed engines throughBackendRegistryand names no provider anywhere in its code.ai-agent-llm— on-device.ggufbackend (bundled llama.cpp AAR,native.code), registers aslocal.ai-agent-gemini— Gemini API backend (network.access), registers asgemini.Along the way: each backend owns its preferences instead of reading
ai-core'sAgentSettings; each contributes its own settings pane, system prompt and tests; the ADFA-5095LlmBackendsplit is adopted (getConfigSpecs/settingsFragmentClassNamemoved ontoConfigurableBackend,supportsHistorydeclared, backend closed ondeactivate()so disabling no longer orphans a live instance); and every plugin's sources are laid out by layer (plugin/,backend/,preferences/,services/,logging/…) withutils/anddata/gone and logs tagged per plugin.Because plugins load in parallel with no ordering guarantee, settings had to stop depending on who activated first. Each backend now reads the host's legacy store directly and migrates once from every prior location (old plugin id from before the
ai-backend-*→ai-agent-*rename,ai-core,ai-assistant, the host'sLlamaPrefs), guarded by amigrated_*flag so a value changed since is never clobbered. A device configured on the old build stays configured.Details
7 commits, 233 files, mostly moves/renames:
ai-assistant/ai-coreai-backend-local/→ai-agent-llm/plugin.id, label (submodule path in.gitmodulesfollows)ai-backend-gemini/→ai-agent-gemini/plugin.id, labelPlugin ids after the rename:
…plugins.aicore,…plugins.aiagentllm,…plugins.aiagentgemini. README's examples table rewritten to state the new dependency shape (AI Core is mandatory; install at least one backend).Tests moved with the code they cover and now sit under each owning plugin — 31 test files across the three plugins, all passing.
document_5179206860728698887.mp4
Ticket
ADFA-5096