Skip to content

feat(frontend): Add AI Gateway integration for dynamic provider and model selection#2175

Merged
alenkacz merged 12 commits into
masterfrom
av/ai-gateway-integration-ui
Jan 28, 2026
Merged

feat(frontend): Add AI Gateway integration for dynamic provider and model selection#2175
alenkacz merged 12 commits into
masterfrom
av/ai-gateway-integration-ui

Conversation

@alenkacz
Copy link
Copy Markdown
Contributor

Summary

Adds comprehensive AI Gateway integration to Console UI, enabling dynamic LLM provider and model selection from the AI Gateway API instead of hardcoded lists.

Key Features

  • Gateway Selection: Fetch and display available AI Gateways from AI Gateway API

    • Auto-select first available gateway when creating agents
    • Display gateway ID in agent configuration details
    • Gateway required when available
  • Dynamic Provider Loading: Replace hardcoded provider list with API data

    • Fetch from ListModelProviders API
    • Display provider logos from API response
    • Loading states during fetch
  • Dynamic Model Loading: Fetch models based on selected provider

    • Filter models using provider = "{providerId}" query
    • Auto-select first model when provider changes
    • Show model metadata (context window, description)
  • Dual-Mode Support:

    • AI Gateway Mode: Use gateway for auth, dynamic providers/models, hide API key field
    • Legacy Mode: Use API key, hardcoded providers/models (backward compatible)
    • Edit screen automatically detects mode based on agent's gatewayId
  • Feature Flag: enableApiKeyConfigurationAgent

    • false (default): AI Gateway mode with dynamic data
    • true: Legacy API key mode with hardcoded providers
    • Controlled via Console constants or Cloud UI LaunchDarkly

Technical Implementation

API Integration:

  • Added buf.build/redpandadata/ai-gateway dependency to buf.yaml
  • Generated TypeScript types for Gateway, ModelProviders, and Models services
  • Created React Query hooks: useListGatewaysQuery, useListModelProvidersQuery, useListModelsQuery
  • Custom transport with bearer token authentication for AI Gateway

Proxy Configuration:

  • Dev server proxy for /.redpanda/api/redpanda.api.aigateway.v1 → AI Gateway service
  • Configured in both Console UI and Cloud UI (via Module Federation)
  • CORS handled via server-side proxy

Validation Updates:

  • API key optional when gateway is configured (proto updated with ignore = IGNORE_IF_ZERO_VALUE)
  • Gateway validation: required when available, optional otherwise
  • Conditional validation based on selected mode

UX Improvements:

  • No layout shift: gateway field always visible (disabled when unavailable)
  • Smooth loading states for all dropdowns
  • Clear validation error handling
  • Provider icons in selected state and dropdown list

Files Changed

New Files:

  • frontend/src/react-query/api/ai-gateway.tsx - API integration hooks
  • frontend/src/hooks/use-ai-gateway-transport.ts - Custom transport for AI Gateway
  • frontend/src/protogen/redpanda/api/aigateway/v1/* - Generated TypeScript types (120+ files)
  • backend/pkg/protogen/redpanda/api/aigateway/v1/* - Generated Go types (100+ files)

Modified Files:

  • frontend/src/components/ui/ai-agent/llm-config-section.tsx - Gateway/provider/model UI
  • frontend/src/components/pages/agents/create/ai-agent-create-page.tsx - Gateway detection & API calls
  • frontend/src/components/pages/agents/create/schemas.ts - Validation updates
  • frontend/src/components/pages/agents/details/ai-agent-configuration-tab.tsx - Edit screen dual-mode support
  • frontend/src/components/constants.ts - Feature flag definition
  • frontend/rsbuild.config.ts - Proxy configuration
  • buf.yaml - AI Gateway dependency
  • taskfiles/proto.yaml - Proto generation for AI Gateway

Testing Checklist

  • Create agent with gateway: Gateway dropdown appears, providers/models load from API
  • Create agent in legacy mode: Feature flag enabled, API key field visible
  • Edit agent with gateway: Gateway dropdown editable, dynamic providers/models
  • Edit agent without gateway: Legacy mode, API key field visible
  • No layout shifts during loading
  • No validation errors during transitions
  • Provider icons displayed correctly
  • Models filter by selected provider

Dependencies

Requires backend to be deployed with proto change from commit 817620329551a2fff2264b766ab4c88b2166c5d6 where api_key field has ignore = IGNORE_IF_ZERO_VALUE to allow empty API key when gateway is configured.

🤖 Generated with Claude Code

…odel selection

Add comprehensive AI Gateway integration to Console UI with the following features:

- **Gateway Selection**: Fetch and display available AI Gateways from AI Gateway API
  - Auto-select first available gateway when creating agents
  - Show gateway ID in agent configuration details

- **Dynamic Provider Loading**: Fetch LLM providers from AI Gateway ListModelProviders API
  - Replace hardcoded provider list with dynamic data
  - Display provider logos from API response

- **Dynamic Model Loading**: Fetch models from AI Gateway ListModels API
  - Filter models by selected provider
  - Auto-select first model when provider changes
  - Show model metadata (context window, description)

- **Dual-Mode Support**:
  - AI Gateway mode: Use gateway for auth, dynamic providers/models, hide API key field
  - Legacy mode: Use API key, hardcoded providers/models (backward compatible)

- **Feature Flag**: enable-api-key-configuration-agent
  - false (default): AI Gateway mode with dynamic data
  - true: Legacy mode with hardcoded providers and API key requirement

- **API Integration**:
  - Add buf.build/redpandadata/ai-gateway dependency
  - Generate TypeScript types for Gateway, ModelProviders, and Models services
  - Create React Query hooks with proper caching and authentication
  - Configure dev server proxy for AI Gateway API (/.redpanda/api/redpanda.api.aigateway.v1)

- **Validation**:
  - API key optional when gateway is configured (proto updated with ignore = IGNORE_IF_ZERO_VALUE)
  - Gateway required when available
  - Conditional validation based on mode

- **UX Improvements**:
  - No layout shift: gateway field always visible (disabled when unavailable)
  - Loading states for gateway/provider/model dropdowns
  - No validation errors during loading transitions
  - Icons displayed for providers in both selected state and dropdown

Technical details:
- Proto generation includes AI Gateway v1 API from buf.build
- Proxy configuration in both Console UI and Cloud UI to handle CORS
- Custom transport for AI Gateway with bearer token authentication
- React Query caching with staleTime to prevent excessive refetching

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
@alenkacz alenkacz force-pushed the av/ai-gateway-integration-ui branch from 87fce9c to 12894ca Compare January 23, 2026 11:11
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 23, 2026

The latest Buf updates on your PR. Results from workflow Buf CI / validate (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedJan 27, 2026, 8:14 AM

Comment on lines +56 to +80
proxy: [
// AI Gateway API - proxy to separate AI Gateway service
// Matches: /.redpanda/api/redpanda.api.aigateway.v1.*
// Proto package is: redpanda.api.aigateway.v1 (includes .api)
// AI Gateway now expects the full path with .api
...(process.env.AI_GATEWAY_URL
? [
{
context: ['/.redpanda/api/redpanda.api.aigateway.v1'],
target: process.env.AI_GATEWAY_URL,
changeOrigin: true,
secure: false,
logLevel: 'debug',
// No pathRewrite - AI Gateway expects full path with .api
},
]
: []),
// All other APIs - proxy to Console backend
{
context: ['/api', '/redpanda.api', '/auth', '/logout'],
target: process.env.PROXY_TARGET || 'http://localhost:9090',
changeOrigin: !!process.env.PROXY_TARGET,
secure: process.env.PROXY_TARGET ? false : undefined,
},
],
Copy link
Copy Markdown
Contributor

@malinskibeniamin malinskibeniamin Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we be able to proxy both Console backend and the new Gateway API? I think currently if AI_GATEWAY_URL environment variable is set, then there is no proxy established for Console backend. Shouldn't we allow both at the same time?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both works now 🤔 definitely works when I run it from local

// Gateway detection and list query (using v1 API from ai-gateway module)
// Only fetch when NOT in legacy mode
const { data: gatewaysData, isLoading: isLoadingGateways } = useListGatewaysQuery(
{ pageSize: 1000 }, // Get all gateways (max 1000)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it really 1000? usually it's either 100 or 500

@@ -72,6 +74,38 @@ export const AIAgentCreatePage = () => {
skipInvalidation: true,
});

// Feature flag: when true, use legacy API key mode (hardcoded providers)
const useLegacyApiKeyMode = isFeatureFlagEnabled('enableApiKeyConfigurationAgent');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's refrain from using use prefix for non-hooks - let's call it isLegacyApiKeyMode or similar.

Comment on lines +49 to +50
staleTime: 60000, // 1 minute - prevent excessive refetching
gcTime: 300000, // 5 minutes cache
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this. staleTime, gcTime etc. would be handled by the query client on a global level.

@malinskibeniamin
Copy link
Copy Markdown
Contributor

CI is failing due to isLoadingGateways

$ tsgo --noEmit --incremental --p tsconfig.base.json
src/components/pages/agents/create/ai-agent-create-page.tsx(522,21): error TS2322: Type '{ availableGateways: { id: string; displayName: string; description: string; }[]; availableSecrets: { id: string; name: string; }[]; fieldNames: { provider: string; model: string; apiKeySecret: string; baseUrl: string; maxIterations: string; gatewayId: string; }; ... 6 more ...; showMaxIterations: true; }' is not assignable to type 'IntrinsicAttributes & LLMConfigSectionProps'.
  Property 'isLoadingGateways' does not exist on type 'IntrinsicAttributes & LLMConfigSectionProps'.
src/components/pages/agents/create/schemas.ts(163,3): error TS1117: An object literal cannot have multiple properties with the same name.

alenkacz and others added 7 commits January 23, 2026 12:52
…d in initialValues

- Add isLoadingGateways to LLMConfigSectionProps interface
- Remove duplicate gatewayId property from initialValues object

Fixes TypeScript compilation errors.
- Run Biome formatter to fix import ordering and code style
- Change pageSize from 1000 to 100 for gateway queries
- Rename useLegacyApiKeyMode to isLegacyApiKeyMode (non-hook variable)
- Clarify proxy configuration supports both Console backend and AI Gateway
- Update useEffect dependencies for exhaustive deps check
…gateway display

Add comprehensive AI Gateway support across agent lifecycle:
- Display gateway info on agent details page (read-only field)
- Load providers and models from AI Gateway API on edit screen
- Hide API token field when agent uses gateway (create and edit)
- Strip API-specific prefixes from provider and model names
- Auto-select first enabled provider and model in create mode
- Configure console dev server on port 3004 for module federation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove staleTime, gcTime, refetchOnWindowFocus, refetchOnMount, and refetchOnReconnect options from AI Gateway query hooks as these should be configured at the global query client level per code review feedback.
When enableApiKeyConfigurationAgent feature flag is enabled, the edit screen will now use legacy behavior (hardcoded providers/models, no AI Gateway API calls) even if the agent was originally created with a gateway. This ensures consistent behavior across create and edit screens when the feature flag is enabled.
- Add isLoadingGateways to gateway Select disabled state and placeholder
- Remove unused detectProvider import
- Add non-null assertions for aiAgent in useMemo and handleSave
- Fix provider type checking with explicit null check
- Convert selectedProvider to boolean with !! in hasNoModels

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
These auto-generated protobuf files were importing from the wrong package
(go.panda.dev/redpanda-aigw instead of local console imports), causing
golangci-lint to fail. These files belong to the redpanda-aigw repository,
not the console repository.

The frontend AI Gateway integration uses TypeScript protobuf files generated
separately and doesn't need these backend Go files.

Fixes golangci-lint error:
  no export data for "go.panda.dev/redpanda-aigw/protos/gen/redpanda/api/aigateway/v1"

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Comment thread frontend/rsbuild.config.ts Outdated
},
},
server: {
port: 3004,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this wouldn't always be the case - you should either run bun run start for enterprise or bun run start2 --port=3004

Comment on lines +91 to +92
filter: input?.filter ?? '',
orderBy: input?.orderBy ?? '',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is nothing to filter by, we shouldn't need to provide it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do something like

const listModelsFilter = create(ListModelsRequestSchema_Filter, { input?.filter ? ...input?.filter : undefined })

Comment thread frontend/src/react-query/api/ai-gateway.tsx
Comment thread frontend/src/components/ui/ai-agent/llm-config-section.tsx
Comment on lines +122 to +135
if (isUsingGateway && modelsData?.models) {
// Map gateway models to our format (already filtered for enabled at API level)
return modelsData.models.map((model) => {
// Strip "models/provider/" prefix from model name
// e.g., "models/openai/gpt-4o-mini" -> "gpt-4o-mini"
const modelName = model.name.split('/').pop() || model.name;

return {
value: modelName,
name: model.displayName || modelName,
description: model.description || '',
};
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] We could add transform option to our useQuery directly, that way we wouldn't need to handle the data parsing in the component layer. Can you please use the transform property from connect/tanstack query framework to fetch data and parse it into the expected format directly in the useListModelsQuery hook?

// Map gateway providers to our format (already filtered for enabled at API level)
return providersData.modelProviders.map((provider) => {
// Strip "model_providers/" prefix from provider name
const providerName = provider.name.replace(/^model_providers\//, '');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to create a const PROVIDER_REGEX = new RegExp(/^model_providers\//g); or similar.

const { data: modelsData, isLoading: isLoadingModels } = useListModelsQuery(
{
filter: selectedProvider
? `provider = "${selectedProvider}" AND enabled = "true"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need any special query parsing for the filters?

alenkacz and others added 4 commits January 26, 2026 16:32
Addresses code review comments from @malinskibeniamin:

1. [MAJOR] Add select/transform to query hooks
   - Add transformModelProviders with PROVIDER_PREFIX_REGEX to strip "model_providers/" prefix
   - Add transformModels with MODEL_PREFIX_REGEX to strip "models/provider/" prefix
   - Move data parsing from component layer to query hooks using select option
   - Update return types to use ReturnType<typeof transform> for type safety

2. Make filter parameter optional
   - Use conditional spread: ...(input?.filter && { filter: input.filter })
   - Don't provide filter/orderBy if not specified
   - Applies to both listModelProviders and listModels requests

3. Simplify component data mapping
   - Remove duplicate prefix stripping logic from llm-config-section.tsx
   - Use transformed data directly from query hooks
   - Cleaner separation of concerns: queries handle data transformation, components handle presentation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit addresses all code review comments on PR #2175:

1. Revert rsbuild port configuration (frontend/rsbuild.config.ts:51)
   - Removed hardcoded port: 3004
   - Use `bun run start` for enterprise or `bun run start2 --port=3004` for Module Federation

2. Split ai-gateway.tsx into separate files (frontend/src/react-query/api/ai-gateway.tsx:1)
   - Created ai-gateway/model-providers.tsx for useListModelProvidersQuery
   - Created ai-gateway/models.tsx for useListModelsQuery
   - Kept ai-gateway.tsx for useListGatewaysQuery only
   - Added documentation about AI Gateway transport requirement at top of each file

3. Remove pageSize override in component (frontend/src/components/pages/agents/create/ai-agent-create-page.tsx:84)
   - Removed pageSize: 100 from useListGatewaysQuery call
   - Now uses AI_GATEWAY_DEFAULT_PAGE_SIZE (50) from hook

4. Update imports in consuming components
   - Updated llm-config-section.tsx to import from separate files
   - Updated ai-agent-configuration-tab.tsx to import from separate files

Note: The [nit] comment about nested ternary operators (line 326) is acknowledged
and will be addressed in a subsequent PR as suggested.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When switching providers (e.g., OpenAI to Anthropic), the model dropdown
briefly showed a blank value while new models were loading. This happened
because the old model value remained in the form field but didn't match
any models in the empty filteredModels array during loading.

Fix: Clear the model field when provider changes and models are loading
or unavailable. This ensures the "Loading models..." placeholder is
visible instead of a blank dropdown, providing better UX feedback during
provider switching.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Biome formatting fixes:
- Fix line breaks for long ternary expressions
- Convert double quotes to single quotes for consistency
- Format multi-line object property access

UX improvements:
- Clear model field when provider changes to show "Loading models..." placeholder
- Use undefined instead of empty string for Select value to properly trigger placeholder
- Prevents blank dropdown when switching providers during model loading

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
const routeApi = getRouteApi('/agents/$id');

import { CLOUD_MANAGED_TAG_KEYS, isCloudManagedTagKey } from 'components/constants';
import { isFeatureFlagEnabled } from 'config';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Biome] reported by reviewdog 🐶

Suggested change
import { isFeatureFlagEnabled } from 'config';


// Get gateway display name
const gatewayDisplayName = useMemo(() => {
if (!aiAgentData?.aiAgent?.gateway?.virtualGatewayId || !gatewaysData?.gateways) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Biome] reported by reviewdog 🐶

Suggested change
if (!aiAgentData?.aiAgent?.gateway?.virtualGatewayId || !gatewaysData?.gateways) {
if (!(aiAgentData?.aiAgent?.gateway?.virtualGatewayId && gatewaysData?.gateways)) {

}, [aiAgentData, mcpServersData]);

if (!aiAgentData?.aiAgent) {
if (!aiAgentData?.aiAgent || !displayData) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Biome] reported by reviewdog 🐶

Suggested change
if (!aiAgentData?.aiAgent || !displayData) {
if (!(aiAgentData?.aiAgent && displayData)) {

@alenkacz alenkacz merged commit 6841e46 into master Jan 28, 2026
15 checks passed
@alenkacz alenkacz deleted the av/ai-gateway-integration-ui branch January 28, 2026 10:42
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