diff --git a/agent-server/nodejs/src/api-server.js b/agent-server/nodejs/src/api-server.js index 3773fa6357..8bdf0b3277 100644 --- a/agent-server/nodejs/src/api-server.js +++ b/agent-server/nodejs/src/api-server.js @@ -593,26 +593,33 @@ class APIServer { // 1. Try tier-specific endpoint (e.g., main_model.endpoint) // 2. Fall back to top-level endpoint (e.g., model.endpoint) // 3. Fall back to LITELLM_ENDPOINT env var (for litellm provider) - const getEndpoint = (tierConfig) => { - const explicitEndpoint = tierConfig?.endpoint || requestBody.model.endpoint; + // @param {Object} resolvedConfig - Config after extractModelTierConfig (has defaults applied) + // @param {Object} rawTierConfig - Original tier config from request (may be undefined) + const getEndpoint = (resolvedConfig, rawTierConfig) => { + const explicitEndpoint = rawTierConfig?.endpoint || requestBody.model.endpoint; if (explicitEndpoint) return explicitEndpoint; - // Use env var default for litellm provider - if (tierConfig?.provider === 'litellm') return defaultLiteLLMEndpoint; + // Use env var default for litellm provider (check resolved config for provider) + if (resolvedConfig?.provider === 'litellm') return defaultLiteLLMEndpoint; return undefined; }; + // Extract tier configs first so getEndpoint can use resolved provider + const mainConfig = this.extractModelTierConfig('main', requestBody.model.main_model, defaults); + const miniConfig = this.extractModelTierConfig('mini', requestBody.model.mini_model, defaults); + const nanoConfig = this.extractModelTierConfig('nano', requestBody.model.nano_model, defaults); + return { main_model: { - ...this.extractModelTierConfig('main', requestBody.model.main_model, defaults), - endpoint: getEndpoint(requestBody.model.main_model) + ...mainConfig, + endpoint: getEndpoint(mainConfig, requestBody.model.main_model) }, mini_model: { - ...this.extractModelTierConfig('mini', requestBody.model.mini_model, defaults), - endpoint: getEndpoint(requestBody.model.mini_model) + ...miniConfig, + endpoint: getEndpoint(miniConfig, requestBody.model.mini_model) }, nano_model: { - ...this.extractModelTierConfig('nano', requestBody.model.nano_model, defaults), - endpoint: getEndpoint(requestBody.model.nano_model) + ...nanoConfig, + endpoint: getEndpoint(nanoConfig, requestBody.model.nano_model) } }; } diff --git a/docker/Dockerfile b/docker/Dockerfile index 2caba17628..5305b7f2e3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -47,11 +47,18 @@ RUN git remote add upstream https://github.com/BrowserOperator/browser-operator- RUN git fetch upstream RUN git checkout upstream/main -# Force automated mode (matches parent Dockerfile.devtools approach - rock-solid) -RUN sed -i 's/AUTOMATED_MODE: false/AUTOMATED_MODE: true/' \ - front_end/panels/ai_chat/core/BuildConfig.ts || true - -# Build Browser Operator version with current changes +# Copy local LLM changes on top of the upstream code +# This allows iterative development without breaking BUILD.gn compatibility +COPY front_end/panels/ai_chat/LLM /workspace/devtools/devtools-frontend/front_end/panels/ai_chat/LLM + +# AUTOMATED_MODE: Default true for API mode (Type 2/3). Override with --build-arg AUTOMATED_MODE=false for Type 1. +ARG AUTOMATED_MODE=true +RUN if [ "$AUTOMATED_MODE" = "true" ]; then \ + sed -i 's/AUTOMATED_MODE: false/AUTOMATED_MODE: true/' \ + front_end/panels/ai_chat/core/BuildConfig.ts; \ + fi + +# Build Browser Operator version with local changes RUN npm run build # ============================================================================== diff --git a/docker/README.md b/docker/README.md index 5bf1af6287..af5897c036 100644 --- a/docker/README.md +++ b/docker/README.md @@ -2,6 +2,10 @@ This directory contains Docker configuration files for building and running the Browser Operator DevTools Frontend with integrated Agent Server in a containerized environment. +> **Deployment Types:** This directory supports **Type 1** (DevTools only) and **Type 2** (DevTools + Agent Server). +> For **Type 3** (full dockerized browser), see the [web-agent](https://github.com/BrowserOperator/web-agent) repository. +> Quick reference: [CLAUDE.md](../CLAUDE.md) + ## Overview The Docker setup uses a multi-stage build process: diff --git a/front_end/panels/ai_chat/evaluation/remote/EvaluationAgent.ts b/front_end/panels/ai_chat/evaluation/remote/EvaluationAgent.ts index 04f357fde3..4e313892ae 100644 --- a/front_end/panels/ai_chat/evaluation/remote/EvaluationAgent.ts +++ b/front_end/panels/ai_chat/evaluation/remote/EvaluationAgent.ts @@ -11,7 +11,7 @@ import { ToolRegistry, ConfigurableAgentTool } from '../../agent_framework/Confi import { AgentService } from '../../core/AgentService.js'; import { AIChatPanel } from '../../ui/AIChatPanel.js'; import { createLogger } from '../../core/Logger.js'; -import { createTracingProvider, withTracingContext, isTracingEnabled, getTracingConfig } from '../../tracing/TracingConfig.js'; +import { createTracingProvider, withTracingContext, isTracingEnabled, getTracingConfig, setTracingConfig, refreshTracingProvider } from '../../tracing/TracingConfig.js'; import { AgentDescriptorRegistry, type AgentDescriptor } from '../../core/AgentDescriptorRegistry.js'; import '../../core/BaseOrchestratorAgent.js'; import type { TracingProvider, TracingContext } from '../../tracing/TracingProvider.js'; @@ -433,9 +433,37 @@ export class EvaluationAgent { hasTracing: !!params.tracing, tracingKeys: Object.keys(requestTracing), sessionId: requestTracing.session_id, - traceId: requestTracing.trace_id + traceId: requestTracing.trace_id, + hasLangfuseCredentials: !!(requestTracing.langfuse_endpoint && requestTracing.langfuse_public_key && requestTracing.langfuse_secret_key) }); + // Auto-configure Langfuse tracing from request if credentials provided and not already enabled + if (requestTracing.langfuse_endpoint && + requestTracing.langfuse_public_key && + requestTracing.langfuse_secret_key && + !isTracingEnabled()) { + logger.info('Auto-configuring DevTools Langfuse tracing from request', { + endpoint: requestTracing.langfuse_endpoint, + hasPublicKey: true, + hasSecretKey: true + }); + + setTracingConfig({ + provider: 'langfuse', + endpoint: requestTracing.langfuse_endpoint, + publicKey: requestTracing.langfuse_public_key, + secretKey: requestTracing.langfuse_secret_key + }); + + // Refresh the tracing provider to pick up new configuration + await refreshTracingProvider(); + + // Update this instance's tracing provider + this.tracingProvider = createTracingProvider(); + + logger.info('DevTools Langfuse tracing configured successfully from request'); + } + // Create a trace for this evaluation - use tracing from request if available const traceId = requestTracing.trace_id || `eval-${params.evaluationId}-${Date.now()}`; const sessionId = requestTracing.session_id || `eval-session-${Date.now()}`; diff --git a/front_end/panels/ai_chat/evaluation/remote/EvaluationProtocol.ts b/front_end/panels/ai_chat/evaluation/remote/EvaluationProtocol.ts index dbf100b509..3bf90ed2b9 100644 --- a/front_end/panels/ai_chat/evaluation/remote/EvaluationProtocol.ts +++ b/front_end/panels/ai_chat/evaluation/remote/EvaluationProtocol.ts @@ -113,6 +113,11 @@ export interface EvaluationParams { category?: string; tags?: string[]; trace_name?: string; + // Langfuse credentials for auto-configuration + // When provided, DevTools will auto-configure Langfuse tracing if not already enabled + langfuse_endpoint?: string; + langfuse_public_key?: string; + langfuse_secret_key?: string; }; }