Skip to content
Open
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
12 changes: 12 additions & 0 deletions components/backend/handlers/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package handlers

import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
Expand Down Expand Up @@ -137,9 +138,20 @@ func GetClusterInfo(c *gin.Context) {
isOpenShift := isOpenShiftCluster()
vertexEnabled := os.Getenv("CLAUDE_CODE_USE_VERTEX") == "1"

var models []map[string]interface{}
if raw := os.Getenv("MODELS_JSON"); raw != "" {
if err := json.Unmarshal([]byte(raw), &models); err != nil {
log.Printf("Warning: failed to parse MODELS_JSON: %v", err)
}
}
if models == nil {
models = []map[string]interface{}{}
}

c.JSON(http.StatusOK, gin.H{
"isOpenShift": isOpenShift,
"vertexEnabled": vertexEnabled,
"models": models,
})
}

Expand Down
12 changes: 10 additions & 2 deletions components/frontend/src/components/create-session-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ import {
import type { CreateAgenticSessionRequest } from "@/types/agentic-session";
import { useCreateSession } from "@/services/queries/use-sessions";
import { useIntegrationsStatus } from "@/services/queries/use-integrations";
import { useClusterInfo } from "@/hooks/use-cluster-info";
import { errorToast } from "@/hooks/use-toast";

const models = [
const fallbackModels = [
{ value: "claude-sonnet-4-5", label: "Claude Sonnet 4.5" },
{ value: "claude-opus-4-6", label: "Claude Opus 4.6" },
{ value: "claude-opus-4-5", label: "Claude Opus 4.5" },
Expand Down Expand Up @@ -69,6 +70,13 @@ export function CreateSessionDialog({
const [open, setOpen] = useState(false);
const router = useRouter();
const createSessionMutation = useCreateSession();
const { models: clusterModels } = useClusterInfo();

const models = clusterModels.length > 0
? clusterModels.map((m) => ({ value: m.name, label: m.displayName }))
: fallbackModels;

const defaultModel = clusterModels.find((m) => m.default)?.name ?? "claude-sonnet-4-5";

const { data: integrationsStatus } = useIntegrationsStatus();

Expand All @@ -81,7 +89,7 @@ export function CreateSessionDialog({
resolver: zodResolver(formSchema),
defaultValues: {
displayName: "",
model: "claude-sonnet-4-5",
model: defaultModel,
temperature: 0.7,
maxTokens: 4000,
timeout: 300,
Expand Down
11 changes: 7 additions & 4 deletions components/frontend/src/hooks/use-cluster-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@
*/

import { useClusterInfo as useClusterInfoQuery } from '@/services/queries/use-cluster';
import type { ModelInfo } from '@/services/api/cluster';

export type ClusterInfo = {
isOpenShift: boolean;
vertexEnabled: boolean;
models: ModelInfo[];
isLoading: boolean;
isError: boolean;
};

/**
* Detects whether the cluster is OpenShift or vanilla Kubernetes
* and whether Vertex AI is enabled
* Calls the /api/cluster-info endpoint which checks for project.openshift.io API group
* and CLAUDE_CODE_USE_VERTEX environment variable
* Detects whether the cluster is OpenShift or vanilla Kubernetes,
* whether Vertex AI is enabled, and available models
* Calls the /api/cluster-info endpoint which checks for project.openshift.io API group,
* CLAUDE_CODE_USE_VERTEX environment variable, and MODELS_JSON ConfigMap
*/
export function useClusterInfo(): ClusterInfo {
const { data, isLoading, isError } = useClusterInfoQuery();

return {
isOpenShift: data?.isOpenShift ?? false,
vertexEnabled: data?.vertexEnabled ?? false,
models: data?.models ?? [],
isLoading,
isError,
};
Expand Down
8 changes: 8 additions & 0 deletions components/frontend/src/services/api/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@

import { apiClient } from './client';

export type ModelInfo = {
name: string;
displayName: string;
vertexId?: string;
default?: boolean;
};

export type ClusterInfo = {
isOpenShift: boolean;
vertexEnabled: boolean;
models: ModelInfo[];
};

/**
Expand Down
6 changes: 6 additions & 0 deletions components/manifests/base/backend-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ spec:
configMapKeyRef:
name: operator-config
key: GOOGLE_APPLICATION_CREDENTIALS
- name: MODELS_JSON
valueFrom:
configMapKeyRef:
name: operator-config
key: MODELS_JSON
optional: true
resources:
requests:
cpu: 100m
Expand Down
6 changes: 6 additions & 0 deletions components/manifests/base/operator-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ spec:
configMapKeyRef:
name: operator-config
key: GOOGLE_APPLICATION_CREDENTIALS
- name: MODELS_JSON
valueFrom:
configMapKeyRef:
name: operator-config
key: MODELS_JSON
optional: true
# Platform-wide Langfuse observability configuration
# All LANGFUSE_* config stored in ambient-admin-langfuse-secret (platform-admin managed)
- name: LANGFUSE_ENABLED
Expand Down
2 changes: 2 additions & 0 deletions components/manifests/minikube/operator-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ data:
CLOUD_ML_REGION: "global"
ANTHROPIC_VERTEX_PROJECT_ID: ""
GOOGLE_APPLICATION_CREDENTIALS: "/app/vertex/ambient-code-key.json"
# Available models for the platform (consumed by backend, operator, runner)
MODELS_JSON: '[{"name":"claude-sonnet-4-5","displayName":"Claude Sonnet 4.5","vertexId":"claude-sonnet-4-5@20250929","default":true},{"name":"claude-opus-4-6","displayName":"Claude Opus 4.6","vertexId":"claude-opus-4-6@20260115"},{"name":"claude-opus-4-5","displayName":"Claude Opus 4.5","vertexId":"claude-opus-4-5@20251101"},{"name":"claude-opus-4-1","displayName":"Claude Opus 4.1","vertexId":"claude-opus-4-1@20250805"},{"name":"claude-haiku-4-5","displayName":"Claude Haiku 4.5","vertexId":"claude-haiku-4-5@20251001"}]'

2 changes: 2 additions & 0 deletions components/manifests/overlays/e2e/operator-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ data:
CLOUD_ML_REGION: ""
ANTHROPIC_VERTEX_PROJECT_ID: ""
GOOGLE_APPLICATION_CREDENTIALS: ""
# Available models for the platform (consumed by backend, operator, runner)
MODELS_JSON: '[{"name":"claude-sonnet-4-5","displayName":"Claude Sonnet 4.5","vertexId":"claude-sonnet-4-5@20250929","default":true},{"name":"claude-opus-4-6","displayName":"Claude Opus 4.6","vertexId":"claude-opus-4-6@20260115"},{"name":"claude-opus-4-5","displayName":"Claude Opus 4.5","vertexId":"claude-opus-4-5@20251101"},{"name":"claude-opus-4-1","displayName":"Claude Opus 4.1","vertexId":"claude-opus-4-1@20250805"},{"name":"claude-haiku-4-5","displayName":"Claude Haiku 4.5","vertexId":"claude-haiku-4-5@20251001"}]'
2 changes: 2 additions & 0 deletions components/manifests/overlays/kind/operator-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ data:
CLOUD_ML_REGION: ""
ANTHROPIC_VERTEX_PROJECT_ID: ""
GOOGLE_APPLICATION_CREDENTIALS: ""
# Available models for the platform (consumed by backend, operator, runner)
MODELS_JSON: '[{"name":"claude-sonnet-4-5","displayName":"Claude Sonnet 4.5","vertexId":"claude-sonnet-4-5@20250929","default":true},{"name":"claude-opus-4-6","displayName":"Claude Opus 4.6","vertexId":"claude-opus-4-6@20260115"},{"name":"claude-opus-4-5","displayName":"Claude Opus 4.5","vertexId":"claude-opus-4-5@20251101"},{"name":"claude-opus-4-1","displayName":"Claude Opus 4.1","vertexId":"claude-opus-4-1@20250805"},{"name":"claude-haiku-4-5","displayName":"Claude Haiku 4.5","vertexId":"claude-haiku-4-5@20251001"}]'
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ data:
CLOUD_ML_REGION: ""
ANTHROPIC_VERTEX_PROJECT_ID: ""
GOOGLE_APPLICATION_CREDENTIALS: ""
# Available models for the platform (consumed by backend, operator, runner)
MODELS_JSON: '[{"name":"claude-sonnet-4-5","displayName":"Claude Sonnet 4.5","vertexId":"claude-sonnet-4-5@20250929","default":true},{"name":"claude-opus-4-6","displayName":"Claude Opus 4.6","vertexId":"claude-opus-4-6@20260115"},{"name":"claude-opus-4-5","displayName":"Claude Opus 4.5","vertexId":"claude-opus-4-5@20251101"},{"name":"claude-opus-4-1","displayName":"Claude Opus 4.1","vertexId":"claude-opus-4-1@20250805"},{"name":"claude-haiku-4-5","displayName":"Claude Haiku 4.5","vertexId":"claude-haiku-4-5@20251001"}]'
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ data:
CLOUD_ML_REGION: "global"
ANTHROPIC_VERTEX_PROJECT_ID: "ambient-code-platform"
GOOGLE_APPLICATION_CREDENTIALS: "/app/vertex/ambient-code-key.json"
# Available models for the platform (consumed by backend, operator, runner)
MODELS_JSON: '[{"name":"claude-sonnet-4-5","displayName":"Claude Sonnet 4.5","vertexId":"claude-sonnet-4-5@20250929","default":true},{"name":"claude-opus-4-6","displayName":"Claude Opus 4.6","vertexId":"claude-opus-4-6@20260115"},{"name":"claude-opus-4-5","displayName":"Claude Opus 4.5","vertexId":"claude-opus-4-5@20251101"},{"name":"claude-opus-4-1","displayName":"Claude Opus 4.1","vertexId":"claude-opus-4-1@20250805"},{"name":"claude-haiku-4-5","displayName":"Claude Haiku 4.5","vertexId":"claude-haiku-4-5@20251001"}]'
5 changes: 5 additions & 0 deletions components/operator/internal/handlers/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,11 @@ func handleAgenticSessionEvent(obj *unstructured.Unstructured) error {
base = append(base, corev1.EnvVar{Name: "CLAUDE_CODE_USE_VERTEX", Value: "0"})
}

// Pass dynamic model configuration to runner
if modelsJSON := os.Getenv("MODELS_JSON"); modelsJSON != "" {
base = append(base, corev1.EnvVar{Name: "MODELS_JSON", Value: modelsJSON})
}

// Add PARENT_SESSION_ID if this is a continuation
if parentSessionID != "" {
base = append(base, corev1.EnvVar{Name: "PARENT_SESSION_ID", Value: parentSessionID})
Expand Down
19 changes: 18 additions & 1 deletion components/runners/claude-code-runner/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,31 @@ def sanitize_user_context(user_id: str, user_name: str) -> tuple[str, str]:
# ---------------------------------------------------------------------------

# Anthropic API → Vertex AI model name mapping
VERTEX_MODEL_MAP: dict[str, str] = {
# Built from MODELS_JSON env var (set via operator-config ConfigMap) if available,
# otherwise falls back to hardcoded defaults.
_HARDCODED_VERTEX_MAP: dict[str, str] = {
"claude-opus-4-5": "claude-opus-4-5@20251101",
"claude-opus-4-1": "claude-opus-4-1@20250805",
"claude-sonnet-4-5": "claude-sonnet-4-5@20250929",
"claude-haiku-4-5": "claude-haiku-4-5@20251001",
}


def _build_vertex_model_map() -> dict[str, str]:
raw = os.environ.get("MODELS_JSON", "")
if not raw:
return dict(_HARDCODED_VERTEX_MAP)
try:
models = _json.loads(raw)
return {m["name"]: m["vertexId"] for m in models if m.get("vertexId")}
except Exception:
logger.warning("Failed to parse MODELS_JSON, using hardcoded map")
return dict(_HARDCODED_VERTEX_MAP)


VERTEX_MODEL_MAP: dict[str, str] = _build_vertex_model_map()


def map_to_vertex_model(model: str) -> str:
"""Map Anthropic API model names to Vertex AI model names."""
return VERTEX_MODEL_MAP.get(model, model)
Expand Down
Loading