diff --git a/pkg/constants/engine_constants.go b/pkg/constants/engine_constants.go index 1e0603d35af..49c38c2c422 100644 --- a/pkg/constants/engine_constants.go +++ b/pkg/constants/engine_constants.go @@ -195,6 +195,11 @@ const ( // isolated in the AWF API proxy sidecar. CopilotBYOKDummyAPIKey = "dummy-byok-key-for-offline-mode" + // CopilotBYOKDefaultModel is the explicit fallback model for Copilot BYOK mode. + // BYOK providers require a non-empty model, so this value is used when the + // corresponding GH_AW_MODEL_*_COPILOT variable is unset. + CopilotBYOKDefaultModel = "claude-sonnet-4.6" + // ClaudeCLIModelEnvVar is the native environment variable name supported by the Claude Code CLI // for selecting the model. Setting this env var is equivalent to passing --model to the CLI. ClaudeCLIModelEnvVar = "ANTHROPIC_MODEL" diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index c142223b741..9f5556c54df 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -392,12 +392,17 @@ touch %s // When model is explicitly configured, use its value directly. // When model is not configured, map the GitHub org variable to COPILOT_MODEL so users can set // a default via GitHub Actions variables without requiring per-workflow frontmatter changes. + // In byok-copilot mode, use a non-empty fallback because BYOK providers require an explicit model. if modelConfigured { copilotExecLog.Printf("Setting %s env var for model: %s", constants.CopilotCLIModelEnvVar, workflowData.EngineConfig.Model) env[constants.CopilotCLIModelEnvVar] = workflowData.EngineConfig.Model } else { - // No model configured - map org variable to native COPILOT_MODEL env var - env[constants.CopilotCLIModelEnvVar] = fmt.Sprintf("${{ vars.%s || '' }}", modelEnvVar) + if isFeatureEnabled(constants.ByokCopilotFeatureFlag, workflowData) { + env[constants.CopilotCLIModelEnvVar] = fmt.Sprintf("${{ vars.%s || '%s' }}", modelEnvVar, constants.CopilotBYOKDefaultModel) + } else { + // No model configured - map org variable to native COPILOT_MODEL env var + env[constants.CopilotCLIModelEnvVar] = fmt.Sprintf("${{ vars.%s || '' }}", modelEnvVar) + } } // Add custom environment variables from engine config diff --git a/pkg/workflow/model_env_vars_test.go b/pkg/workflow/model_env_vars_test.go index 8532dc0aabf..49aa9bc8350 100644 --- a/pkg/workflow/model_env_vars_test.go +++ b/pkg/workflow/model_env_vars_test.go @@ -218,16 +218,29 @@ func TestCopilotFallbackModelMapsToNativeEnvVar(t *testing.T) { name string safeOutputs *SafeOutputsConfig expectedOrgVar string + features map[string]any + expectedTail string }{ { name: "Agent job maps GH_AW_MODEL_AGENT_COPILOT to COPILOT_MODEL", safeOutputs: &SafeOutputsConfig{}, expectedOrgVar: constants.EnvVarModelAgentCopilot, + expectedTail: "''", + }, + { + name: "Agent job with byok-copilot uses non-empty COPILOT_MODEL fallback", + safeOutputs: &SafeOutputsConfig{}, + features: map[string]any{ + string(constants.ByokCopilotFeatureFlag): true, + }, + expectedOrgVar: constants.EnvVarModelAgentCopilot, + expectedTail: "'" + constants.CopilotBYOKDefaultModel + "'", }, { name: "Detection job maps GH_AW_MODEL_DETECTION_COPILOT to COPILOT_MODEL", safeOutputs: nil, expectedOrgVar: constants.EnvVarModelDetectionCopilot, + expectedTail: "''", }, } @@ -240,6 +253,7 @@ func TestCopilotFallbackModelMapsToNativeEnvVar(t *testing.T) { "bash": []any{"echo"}, }, SafeOutputs: tt.safeOutputs, + Features: tt.features, } engine, err := GetGlobalEngineRegistry().GetEngine("copilot") @@ -259,7 +273,7 @@ func TestCopilotFallbackModelMapsToNativeEnvVar(t *testing.T) { stepsContent := stepsStr.String() // The model must be passed via COPILOT_MODEL env var pointing to the org variable - expectedEnvLine := constants.CopilotCLIModelEnvVar + ": ${{ vars." + tt.expectedOrgVar + " || '' }}" + expectedEnvLine := constants.CopilotCLIModelEnvVar + ": ${{ vars." + tt.expectedOrgVar + " || " + tt.expectedTail + " }}" if !strings.Contains(stepsContent, expectedEnvLine) { t.Errorf("Expected env line '%s' not found in steps:\n%s", expectedEnvLine, stepsContent) }