Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion workspaces/boost/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ Agents, tools, models, MCP servers, and vector stores are Backstage catalog enti
| Package | Purpose |
| --------------------------------- | -------------------------------------------------------------------- |
| `boost-frontend` | Chat UI, agent gallery, admin panels, composable routable extensions |
| `boost-common` | Shared types, permissions, `boostAiProviderServiceRef` |
| `boost-common` | Shared types, permissions (browser-safe, `common-library` role) |
| `boost-node` | `boostAiProviderServiceRef`, extension points (`node-library` role) |
| `boost-backend` | Core routes, services, middleware, ProviderManager |
| `boost-backend-module-llamastack` | Llama Stack provider module |
| `boost-backend-module-kagenti` | Kagenti provider module |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Boost ships as a set of modular RHDH dynamic plugins (OCI) and Backstage static
| Package | Purpose |
| --------------------------------- | ----------------------------------------------------------------- |
| `boost-frontend` | Chat UI, agent gallery, admin panels, composable extensions |
| `boost-common` | Shared types, permissions, service refs |
| `boost-common` | Shared types, permissions (browser-safe, `common-library` role) |
| `boost-node` | Service refs, extension points (`node-library` role) |
| `boost-backend` | Core routes, services, middleware, cross-cutting entity providers |
| `boost-backend-module-llamastack` | Llama Stack agentic provider |
| `boost-backend-module-kagenti` | Kagenti agentic provider |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Deploy Boost as a traditional Backstage plugin with npm packages.

#### Scenario: Static plugin installation

- **WHEN** the developer installs `@boost/plugin-boost`, `@boost/plugin-boost-backend`, and `@boost/plugin-boost-common`
- **WHEN** the developer installs `@boost/plugin-boost`, `@boost/plugin-boost-backend`, `@boost/plugin-boost-common`, and `@boost/plugin-boost-node`
- **THEN** frontend route, sidebar entry, icon, and backend plugin are registered manually
- **AND** `app-config.yaml` is configured
- **AND** the application is rebuilt and deployed
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,45 @@ Boost implements the provider abstraction as modular RHDH dynamic plugins from t

## Decisions

### Decision 1: serviceRef lives in boost-common
### Decision 1: serviceRef lives in boost-node, types live in boost-common

The `boostAiProviderServiceRef` is exported from `boost-common` alongside the `AgenticProvider` interface types. This allows both backend consumers and frontend type consumers to reference the interface from a single package without depending on the full backend.
The `boostAiProviderServiceRef` is exported from `boost-node` (`backstage.role: node-library`), not `boost-common`. The `AgenticProvider` interface types remain in `boost-common` (`backstage.role: common-library`).

This follows the Backstage convention established by the catalog plugin family:

| Package | Role | Contains |
| ----------------------- | ---------------- | ------------------------------------------- |
| `plugin-catalog-common` | `common-library` | Types, permissions — no backend API imports |
| `plugin-catalog-node` | `node-library` | `catalogServiceRef`, extension points |

A `common-library` package must be safe for browser bundling. `createServiceRef` is a runtime value from `@backstage/backend-plugin-api` — importing it into a `common-library` package forces the bundler to pull in the entire backend API, which fails in browser environments. Backstage's CLI enforces this role boundary.

```typescript
// plugins/boost-common/src/services.ts
// plugins/boost-node/src/services.ts
import { createServiceRef } from '@backstage/backend-plugin-api';
import type { AgenticProvider } from './types';
import type { AgenticProvider } from '@boost/plugin-boost-common';

export const boostAiProviderServiceRef = createServiceRef<AgenticProvider>({
id: 'boost.ai-provider',
scope: 'plugin',
});
```

The dependency graph:

```
boost-common (common-library) ←── boost-node (node-library) ←── boost-backend
│ ↑
│ boost-backend-module-llamastack
│ boost-backend-module-kagenti
boost-frontend (imports types only)
```

`boost-common` exports: `AgenticProvider`, `ProviderDescriptor`, `ProviderCapabilities`, `NormalizedStreamEvent`, conversation types, permission definitions.

`boost-node` exports: `boostAiProviderServiceRef`, `boostProviderExtensionPoint`, and re-exports types from `boost-common` for backend convenience.

The core `boost-backend` plugin registers the default factory via `createServiceFactory` that resolves to the `ProviderManager`'s active provider.

### Decision 2: Providers as backend modules, not separate plugins
Expand Down Expand Up @@ -82,9 +106,9 @@ const hasNamespaceScoping = capabilities?.namespaceScoping === true;

### Decision 5: Provider-specific types stay in their modules

Provider-specific types (e.g., Kagenti-specific interfaces) live in their respective provider modules. Only shared interfaces (`AgenticProvider`, `ProviderDescriptor`, `ProviderCapabilities`, conversation types, `NormalizedStreamEvent`) live in `boost-common`.
Provider-specific types (e.g., Kagenti-specific interfaces) live in their respective provider modules. Only shared interfaces (`AgenticProvider`, `ProviderDescriptor`, `ProviderCapabilities`, conversation types, `NormalizedStreamEvent`) live in `boost-common`. The `boostAiProviderServiceRef` and `boostProviderExtensionPoint` live in `boost-node`.

## Risks

- **Cache key collisions:** Mitigated by using `cache.withOptions()` which namespace-scopes keys per plugin/module.
- **Provider module interdependency:** Provider modules must not import from each other. Shared utilities live in `boost-common` or standalone packages. Boost enforces this from the start.
- **Provider module interdependency:** Provider modules must not import from each other. Shared utilities live in `boost-common` or `boost-node`, and standalone packages. Boost enforces this from the start.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Boost builds this as modular RHDH dynamic plugins from the start: each provider
- `AgenticProvider` interface with required (`chat`, `chatStream`) and optional capabilities
- `ProviderDescriptor` declares ID, name, and capability matrix
- Backstage extension point (`boostProviderExtensionPoint`) for provider registration
- `boostAiProviderServiceRef` in `boost-common` for cross-plugin AI provider consumption
- `boostAiProviderServiceRef` in `boost-node` for cross-plugin AI provider consumption

### Runtime Hot-Swap

Expand Down Expand Up @@ -42,12 +42,13 @@ Two built-in provider modules, each as an independent `createBackendModule`:
- **Providers as modules, not monoliths** — each provider is independently installable and removable
- **Capability checks over identity checks** — `capabilities.agentCatalog` instead of `providerId === 'kagenti'`
- **`cacheService` everywhere** — all provider caches use Backstage `cacheService` with namespace isolation via `cache.withOptions()`
- **Clean type boundaries** — provider-specific types stay in their modules; only shared interfaces in `boost-common`
- **Clean type boundaries** — provider-specific types stay in their modules; shared interfaces in `boost-common`, service refs and extension points in `boost-node`

## Impact

- `plugins/boost-common/` — `AgenticProvider`, `NormalizedStreamEvent`, `boostAiProviderServiceRef`
- `plugins/boost-backend/src/plugin.ts` — serviceRef registration, ProviderManager
- `plugins/boost-common/` — `AgenticProvider`, `NormalizedStreamEvent`, permission definitions
- `plugins/boost-node/` — `boostAiProviderServiceRef`, `boostProviderExtensionPoint`
- `plugins/boost-backend/src/plugin.ts` — serviceRef factory registration, ProviderManager
- `plugins/boost-backend-module-llamastack/` — Llama Stack provider module
- `plugins/boost-backend-module-kagenti/` — Kagenti provider module
- `plugins/boost-frontend/src/` — capability-based rendering throughout
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ Other Backstage plugins must be able to consume the active AI provider via Backs
- **AND** it can call `chat()`, `chatStream()`, and any declared optional capabilities
- **AND** if the active provider is hot-swapped, the consuming plugin receives the new provider on next request

#### Scenario: Service ref declared in boost-common
#### Scenario: Service ref declared in boost-node

- **WHEN** the `boostAiProviderServiceRef` is created via `createServiceRef` from `@backstage/backend-plugin-api`
- **THEN** it is exported from the `boost-common` package (not `boost-backend`)
- **AND** its type parameter is the `AgenticProvider` interface
- **THEN** it is exported from the `boost-node` package (`backstage.role: node-library`), not `boost-common` or `boost-backend`
- **AND** its type parameter is the `AgenticProvider` interface (imported from `boost-common`)
- **AND** its ID follows the pattern `boost.ai-provider`
- **AND** `boost-common` (`backstage.role: common-library`) has no dependency on `@backstage/backend-plugin-api` — keeping it safe for browser bundling

### Requirement: Shared Types in Common Package

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Tasks: Pluggable AI Platform Architecture

## 1. Types and Service Ref (P0)
## 1. Types, Service Ref, and Package Scaffolding (P0)

- [ ] 1.1 Define `AgenticProvider`, `ProviderDescriptor`, `ProviderCapabilities` interfaces in `boost-common`
- [ ] 1.2 Define `NormalizedStreamEvent` union type in `boost-common`
- [ ] 1.3 Define `ConversationSummary`, `ConversationDetails`, `InputItem` conversation types in `boost-common`
- [ ] 1.4 Create `boostAiProviderServiceRef` in `boost-common` via `createServiceRef`
- [ ] 1.4 Create `boost-node` package (`backstage.role: node-library`) with `boostAiProviderServiceRef` via `createServiceRef` — serviceRef must NOT live in `boost-common` (see Decision 1)
- [ ] 1.5 Register default service factory in `boost-backend/plugin.ts` resolving to `ProviderManager.getActiveProvider()`
- [ ] 1.6 Verify no provider-specific types in common package — only shared interfaces
- [ ] 1.7 Verify `boost-common` has no dependency on `@backstage/backend-plugin-api`

## 2. Provider Module Packages (P0)

Expand Down
7 changes: 4 additions & 3 deletions workspaces/boost/specifications/boost-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,16 @@ All packages live at `rhdh-plugins/workspaces/boost/plugins/`:
```
workspace/boost/plugins/
├── boost-frontend — Chat UI, agent gallery, admin panels, composable extensions
├── boost-common — Shared types, permissions, boostAiProviderServiceRef
├── boost-common — Shared types, permissions (browser-safe, common-library role)
├── boost-node — Service refs, extension points (node-library role)
├── boost-backend — Core routes, services, middleware, ProviderManager, cross-cutting entity providers
├── boost-backend-module-llamastack — Llama Stack agentic provider (composes llamastack-entity-provider)
├── boost-backend-module-kagenti — Kagenti agentic provider (composes kagenti-entity-provider)
├── llamastack-entity-provider — Backstage backend service: Llama Stack catalog entities (independently deployable)
└── kagenti-entity-provider — Backstage backend service: Kagenti catalog entities (independently deployable)
```

The core three packages (`boost-frontend`, `boost-common`, `boost-backend`) mirror augment's structure. Provider modules and entity providers are additive — deployers install only what they need. Entity providers are independently deployable as RHDH dynamic plugins for catalog-only use cases.
The core packages (`boost-frontend`, `boost-common`, `boost-node`, `boost-backend`) mirror augment's structure with the addition of `boost-node` for service refs and extension points (following the Backstage `plugin-catalog-common`/`plugin-catalog-node` pattern). Provider modules and entity providers are additive — deployers install only what they need. Entity providers are independently deployable as RHDH dynamic plugins for catalog-only use cases.

## Design Principles (Learned from Augment)

Expand All @@ -60,7 +61,7 @@ _Augment lesson: 2,132 lines of custom governance code implementing 12 authoriza

### 3. Providers as Independent RHDH Dynamic Plugins

Each AI platform provider is a separate `createBackendModule` packaged as an RHDH dynamic plugin. Provider types live in the common package. Cross-plugin consumption via `boostAiProviderServiceRef`.
Each AI platform provider is a separate `createBackendModule` packaged as an RHDH dynamic plugin. Provider types live in `boost-common`; `boostAiProviderServiceRef` and the extension point live in `boost-node`. Cross-plugin consumption via the service ref.

_Augment lesson: Monolithic plugin with providers locked inside. No serviceRef for cross-plugin consumption. 559 lines of Kagenti-specific types polluting the common package. 13+ provider ID string checks coupling frontend to specific providers._

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,16 @@ Admin panels, branding controls, RAG configuration, and onboarding flows must al

All packages live at `rhdh-plugins/workspaces/boost/plugins/`:

| Package | Type | Description |
| --------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------- |
| `boost-frontend` | Frontend plugin | Chat UI, agent gallery, admin panels, composable extensions |
| `boost-common` | Common library | Shared types (`AgenticProvider`, `NormalizedStreamEvent`, permissions), `boostAiProviderServiceRef` |
| `boost-backend` | Backend plugin | Core routes, services, middleware, `ProviderManager`, cross-cutting entity providers (MCP, vector stores) |
| `boost-backend-module-llamastack` | Backend module | Llama Stack agentic provider (composes `llamastack-entity-provider`) |
| `boost-backend-module-kagenti` | Backend module | Kagenti agentic provider (composes `kagenti-entity-provider`) |
| `llamastack-entity-provider` | Backend service | Llama Stack model + agent catalog entities (independently deployable) |
| `kagenti-entity-provider` | Backend service | Kagenti agent + tool catalog entities (independently deployable) |
| Package | Type | Description |
| --------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------- |
| `boost-frontend` | Frontend plugin | Chat UI, agent gallery, admin panels, composable extensions |
| `boost-common` | Common library | Shared types (`AgenticProvider`, `NormalizedStreamEvent`, permissions) — browser-safe, no backend API imports |
| `boost-node` | Node library | `boostAiProviderServiceRef`, `boostProviderExtensionPoint` — service refs and extension points |
| `boost-backend` | Backend plugin | Core routes, services, middleware, `ProviderManager`, cross-cutting entity providers (MCP, vector stores) |
| `boost-backend-module-llamastack` | Backend module | Llama Stack agentic provider (composes `llamastack-entity-provider`) |
| `boost-backend-module-kagenti` | Backend module | Kagenti agentic provider (composes `kagenti-entity-provider`) |
| `llamastack-entity-provider` | Backend service | Llama Stack model + agent catalog entities (independently deployable) |
| `kagenti-entity-provider` | Backend service | Kagenti agent + tool catalog entities (independently deployable) |

**RHDH — Dynamic Plugin (recommended):**

Expand All @@ -81,13 +82,13 @@ All packages live at `rhdh-plugins/workspaces/boost/plugins/`:
4. RHDH loads plugins dynamically via Scalprum — no code changes or rebuilds
5. Boost appears as a sidebar entry in RHDH

**Full portal deployment** installs `boost-frontend`, `boost-common`, `boost-backend`, plus one or both provider modules. Provider modules compose their entity providers internally.
**Full portal deployment** installs `boost-frontend`, `boost-common`, `boost-node`, `boost-backend`, plus one or both provider modules. Provider modules compose their entity providers internally.

**Entity-provider-only deployment** installs `llamastack-entity-provider` or `kagenti-entity-provider` as standalone dynamic plugins — gets AI domain objects in the Backstage catalog without the rest of boost.

**Backstage — Static Plugin:**

1. Install npm packages: `@boost/plugin-boost-frontend`, `@boost/plugin-boost-backend`, `@boost/plugin-boost-common`
1. Install npm packages: `@boost/plugin-boost-frontend`, `@boost/plugin-boost-backend`, `@boost/plugin-boost-common`, `@boost/plugin-boost-node`
2. Optionally install provider modules: `@boost/plugin-boost-backend-module-llamastack`, `@boost/plugin-boost-backend-module-kagenti`
3. Register frontend route, sidebar entry, and icon
4. Register backend plugin and provider modules in backend startup
Expand Down Expand Up @@ -247,7 +248,7 @@ Backend services + Frontend via admin API

```
RHDH Dynamic Plugin (full portal):
boost-frontend + boost-common + boost-backend + provider module(s)
boost-frontend + boost-common + boost-node + boost-backend + provider module(s)
OCI images → dynamic-plugins.override.yaml → Scalprum → sidebar entry
No code changes, no rebuilds

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ Hot-swaps between configured providers at runtime. Monitors capability differenc
- Provider modules have IDs: `llamastack` and `kagenti`
- Both are exported as OCI images for RHDH dynamic plugin loading
- Deployers install only the providers they need — no unused provider code loaded
- Provider modules depend on `boost-common` for shared types and `boostAiProviderServiceRef`
- Provider modules depend on `boost-common` for shared types and `boost-node` for `boostAiProviderServiceRef`
- Boost ships modular from day one — no monolithic fallback needed

**RHDH deployment example:**
Expand Down Expand Up @@ -245,7 +245,7 @@ AgenticProvider (in boost-common)
├── evaluation? — optional
└── conversation? — optional

boostAiProviderServiceRef (in boost-common)
boostAiProviderServiceRef (in boost-node)
└── enables cross-plugin consumption of the active provider

ProviderManager
Expand Down
Loading