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
5 changes: 5 additions & 0 deletions pkg/constants/engine_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 7 additions & 2 deletions pkg/workflow/copilot_engine_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion pkg/workflow/model_env_vars_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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: "''",
},
}

Expand All @@ -240,6 +253,7 @@ func TestCopilotFallbackModelMapsToNativeEnvVar(t *testing.T) {
"bash": []any{"echo"},
},
SafeOutputs: tt.safeOutputs,
Features: tt.features,
}

engine, err := GetGlobalEngineRegistry().GetEngine("copilot")
Expand All @@ -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)
}
Expand Down
Loading