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
1 change: 0 additions & 1 deletion .github/workflows/smoke-copilot-auto.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions cmd/gh-aw/compile_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ func TestCompileCommandShortFlags(t *testing.T) {
t.Fatalf("expected --logical-repo shorthand to be -l, got -%s", logicalRepoFlag.Shorthand)
}

noModelsDevLookupFlag := compileCmd.Flags().Lookup("no-models-dev-lookup")
if noModelsDevLookupFlag == nil {
t.Fatal("expected --no-models-dev-lookup flag on compile command")
}
if noModelsDevLookupFlag.DefValue != "false" {
t.Fatalf("expected --no-models-dev-lookup default to be false, got %s", noModelsDevLookupFlag.DefValue)
}

grantFlag := compileCmd.Flags().Lookup("grant")
if grantFlag == nil {
t.Fatal("expected --grant flag on compile command")
Expand Down
3 changes: 0 additions & 3 deletions cmd/gh-aw/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,6 @@ Unlike ` + "`gh aw upgrade`" + `, ` + "`gh aw compile`" + ` only applies codemod
staged, _ := cmd.Flags().GetBool("staged")
approve, _ := cmd.Flags().GetBool("approve")
validateImages, _ := cmd.Flags().GetBool("validate-images")
disableModelsDevLookup, _ := cmd.Flags().GetBool("no-models-dev-lookup")
priorManifestFile, _ := cmd.Flags().GetString("prior-manifest-file")
ghes, _ := cmd.Flags().GetBool("ghes")
verbose, _ := cmd.Flags().GetBool("verbose")
Expand Down Expand Up @@ -403,7 +402,6 @@ Unlike ` + "`gh aw upgrade`" + `, ` + "`gh aw compile`" + ` only applies codemod
Staged: staged,
Approve: approve,
ValidateImages: validateImages,
DisableModelsDevLookup: disableModelsDevLookup,
PriorManifestFile: priorManifestFile,
GHESCompat: ghes,
UseSamples: useSamples,
Expand Down Expand Up @@ -767,7 +765,6 @@ Use "` + string(constants.CLIExtensionPrefix) + ` help all" to show help for all
compileCmd.Flags().Bool("staged", false, "Force all safe-outputs into staged mode")
compileCmd.Flags().Bool("approve", false, "Approve all safe update changes. When strict mode is active (the default), the compiler emits warnings for new restricted secrets or unapproved action additions/removals not present in the existing gh-aw-manifest. Use this flag to approve and skip safe update enforcement")
compileCmd.Flags().Bool("validate-images", false, "Require Docker to be available for container image validation. Without this flag, container image validation is silently skipped when Docker is not installed or the daemon is not running")
compileCmd.Flags().Bool("no-models-dev-lookup", false, "Disable compile-time models.dev pricing lookup for models missing from the embedded catalog")
compileCmd.Flags().String("prior-manifest-file", "", "Path to a JSON file containing pre-cached gh-aw-manifests (map[lockFile]*GHAWManifest); used by the MCP server to supply a tamper-proof manifest baseline captured at startup")
compileCmd.Flags().Bool("ghes", false, "Enable GitHub Enterprise Server (GHES) compatibility mode. Artifact actions continue using latest non-v3 pins (v3 is deprecated). Overrides the aw.json ghes field")
if err := compileCmd.Flags().MarkHidden("prior-manifest-file"); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/setup/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ If the repository root contains an [`aw.yml` manifest](/gh-aw/reference/aw-yml-p

Unlike `gh aw upgrade`, `gh aw compile` does not run codemods unless you pass `--fix`.

**Options:** `--action-mode`, `--action-tag`, `--actionlint`, `--actions-repo`, `--allow-action-refs`, `--approve`, `--dependabot`, `--dir/-d`, `--engine/-e`, `--fail-fast`, `--fix`, `--force/-f`, `--force-refresh-action-pins`, `--gh-aw-ref`, `--ghes`, `--grant`, `--grype`, `--json/-j`, `--logical-repo/-l`, `--no-check-update`, `--no-emit`, `--no-models-dev-lookup`, `--poutine`, `--purge`, `--refresh-stop-time`, `--runner-guard`, `--schedule-seed`, `--show-all`, `--staged`, `--stats`, `--strict`, `--syft`, `--trial`, `--validate`, `--validate-images`, `--watch/-w`, `--yamllint`, `--zizmor`
**Options:** `--action-mode`, `--action-tag`, `--actionlint`, `--actions-repo`, `--allow-action-refs`, `--approve`, `--dependabot`, `--dir/-d`, `--engine/-e`, `--fail-fast`, `--fix`, `--force/-f`, `--force-refresh-action-pins`, `--gh-aw-ref`, `--ghes`, `--grant`, `--grype`, `--json/-j`, `--logical-repo/-l`, `--no-check-update`, `--no-emit`, `--poutine`, `--purge`, `--refresh-stop-time`, `--runner-guard`, `--schedule-seed`, `--show-all`, `--staged`, `--stats`, `--strict`, `--syft`, `--trial`, `--validate`, `--validate-images`, `--watch/-w`, `--yamllint`, `--zizmor`

**`--gh-aw-ref` flag:** Convenience alias for `--action-mode release --action-tag <ref>`. Accepts a branch name, tag, or commit SHA targeting the `github/gh-aw` repository. Branch and tag names are resolved to their full commit SHA at compile time, so the baked-in reference is immutable and reproducible. Useful for E2E-testing workflows compiled against a specific gh-aw revision.

Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/cli_consistency_help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestUpdateDocsIncludeCoolDownOption(t *testing.T) {
assert.Contains(t, updateSection, "`--cool-down`", "update docs options should include --cool-down")
}

func TestCompileDocsIncludeNoModelsDevLookupOption(t *testing.T) {
func TestCompileDocsReflectCurrentOptions(t *testing.T) {
_, currentFile, _, ok := runtime.Caller(0)
require.True(t, ok, "should resolve current test file path")

Expand All @@ -63,7 +63,7 @@ func TestCompileDocsIncludeNoModelsDevLookupOption(t *testing.T) {
require.NotEqual(t, -1, compileIndex, "CLI setup docs should contain the compile section")

compileSection := text[compileIndex:]
assert.Contains(t, compileSection, "`--no-models-dev-lookup`", "compile docs options should include --no-models-dev-lookup")
assert.NotContains(t, compileSection, "`--no-models-dev-lookup`", "compile docs options should not include removed --no-models-dev-lookup")

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.

The --no-models-dev-lookup flag controlled the now-removed models.dev pricing resolver. With the resolver gone, the flag has no associated functionality and was removed intentionally. Any scripts using it would have had no effect after the resolver was removed, so removing the flag keeps the CLI surface clean and avoids a dead option.

assert.Contains(t, compileSection, "does not run codemods unless you pass `--fix`", "compile docs should explain --fix opt-in behavior")
}

Expand Down
8 changes: 0 additions & 8 deletions pkg/cli/compile_compiler_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,6 @@ func createAndConfigureCompiler(config CompileConfig) *workflow.Compiler {
// Set up repository context
setupRepositoryContext(compiler, config)

if config.DisableModelsDevLookup {
compileCompilerSetupLog.Print("models.dev pricing lookup disabled via --no-models-dev-lookup")
} else {
// Register the models.dev pricing resolver so the compiler can inject pricing for
// models absent from the embedded catalog into GH_AW_INFO_MODEL_COSTS in the lock.yml.
compiler.SetModelPricingResolver(FindOrFetchModelPricing)
}

return compiler
}

Expand Down
11 changes: 2 additions & 9 deletions pkg/cli/compile_compiler_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,10 @@ func hasModelPricingResolver(compiler *workflow.Compiler) bool {
return !reflect.ValueOf(compiler).Elem().FieldByName("modelPricingResolver").IsNil()
}

func TestCreateAndConfigureCompiler_RegistersModelPricingResolverByDefault(t *testing.T) {
func TestCreateAndConfigureCompiler_DoesNotRegisterModelPricingResolverByDefault(t *testing.T) {
compiler := createAndConfigureCompiler(CompileConfig{})
if !hasModelPricingResolver(compiler) {
t.Fatal("expected model pricing resolver to be registered by default")
}
}

func TestCreateAndConfigureCompiler_SkipsModelPricingResolverWhenDisabled(t *testing.T) {
compiler := createAndConfigureCompiler(CompileConfig{DisableModelsDevLookup: true})
if hasModelPricingResolver(compiler) {

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.

The test name TestCreateAndConfigureCompiler_RegistersModelPricingResolverByDefault now asserts the opposite of what it says — the test checks that the resolver is nil. Please rename to something like TestCreateAndConfigureCompiler_HasNoModelPricingResolverByDefault to avoid misleading future readers.

@copilot please address this.

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.

Renamed to TestCreateAndConfigureCompiler_DoesNotRegisterModelPricingResolverByDefault in the latest commit.

t.Fatal("expected model pricing resolver to be nil when models.dev lookup is disabled")
t.Fatal("expected model pricing resolver to be nil by default")

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.

This was a deliberate change per the explicit user request to "remove dynamic pricing lookup all together." Removing the resolver entirely — not just skipping it for auto — was the stated goal. Concrete models that are missing from the embedded catalog will fall back to the embedded data only, which is the intended deterministic behavior.

}
}

Expand Down
1 change: 0 additions & 1 deletion pkg/cli/compile_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ type CompileConfig struct {
ValidateImages bool // Require Docker to be available for container image validation (fail instead of skipping when Docker is unavailable)
PriorManifestFile string // Path to a JSON file containing pre-cached manifests (map[lockFile]*GHAWManifest) collected at MCP server startup; takes precedence over git HEAD / filesystem reads for safe update enforcement
GHESCompat bool // Enable GHES compatibility mode (overrides aw.json ghes field); artifact actions still use latest non-v3 pins
DisableModelsDevLookup bool // Disable compile-time models.dev pricing lookup for models missing from the embedded catalog
}

// CompileValidationError represents a single validation error or warning
Expand Down
19 changes: 0 additions & 19 deletions pkg/cli/model_costs.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cli

import (
"context"
_ "embed"
"encoding/json"
"strconv"
Expand Down Expand Up @@ -183,21 +182,3 @@ func computeModelInferenceCostUSD(provider, model string, inputTokens, outputTok
func computeModelInferenceAIC(provider, model string, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, reasoningTokens int) float64 {
return usdToAIC(computeModelInferenceCostUSD(provider, model, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, reasoningTokens))
}

// FindOrFetchModelPricing resolves per-token pricing for the given provider/model.
// It checks the embedded catalog first; for models absent from the embedded catalog it
// attempts to download the models.dev catalog (at most once per process) and queries
// that. Returns (nil, false) when the model is found in the embedded catalog (runtime
// already has it) or when no pricing can be resolved.
//
// This function is intended for compile-time use: the workflow compiler calls it to
// inject pricing for unknown models into GH_AW_INFO_MODEL_COSTS in the compiled lock.yml
// so that the agent job can perform cost accounting without a live catalog download.
func FindOrFetchModelPricing(ctx context.Context, provider, model string) (map[string]float64, bool) {
if _, ok := findModelPricing(provider, model); ok {
// Model is already in the embedded catalog; the runtime actions/setup/js/models.json
// will supply the pricing — no need to inject it into the lock.yml overlay.
return nil, false
}
return modelsdev.FindPricing(ctx, provider, model)
}
9 changes: 0 additions & 9 deletions pkg/cli/model_costs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package cli

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -99,11 +98,3 @@ func TestComputeModelInferenceAICGitHubCopilotNoCacheRead(t *testing.T) {
assert.InDelta(t, aicViaAnthropic, aicViaGitHubCopilot, 1e-9,
"zero cache reads must not alter the charged input token count")
}

func TestFindOrFetchModelPricing_EmbeddedModelReturnsNil(t *testing.T) {
// claude-sonnet-4.6 is in the embedded catalog; FindOrFetchModelPricing should return
// (nil, false) so the lock.yml overlay does not duplicate what models.json already has.
pricing, ok := FindOrFetchModelPricing(context.Background(), "anthropic", "claude-sonnet-4.6")
assert.False(t, ok)
assert.Nil(t, pricing)
}
8 changes: 8 additions & 0 deletions pkg/workflow/compiler_model_pricing.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,17 @@ func resolveProviderAndModelForPricing(workflowData *WorkflowData) (string, stri
if provider == "" || model == "" {
return "", "", false
}
if isDynamicModelAliasForPricing(model) {
compilerModelPricingLog.Printf("Skipping external pricing lookup for dynamic model alias %q", model)
return "", "", false
}
return provider, model, true
}

func isDynamicModelAliasForPricing(model string) bool {
return strings.EqualFold(strings.TrimSpace(model), "auto")
}

// modelCostsHasPricingFor reports whether the ModelCosts overlay already contains a models
// entry for the given provider/model. An empty provider matches any provider entry.
func modelCostsHasPricingFor(modelCosts map[string]any, provider, model string) bool {
Expand Down
19 changes: 19 additions & 0 deletions pkg/workflow/compiler_model_pricing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,22 @@ func TestResolveModelPricingIfMissing_SkipsMalformedQualifiedModel(t *testing.T)
assert.Nil(t, c.resolveModelPricingIfMissing(nil, &WorkflowData{Model: "openai/", EngineConfig: &EngineConfig{}}))
assert.False(t, called)
}

func TestResolveModelPricingIfMissing_SkipsDynamicAutoModelAlias(t *testing.T) {
c := &Compiler{}
called := false
c.SetModelPricingResolver(func(_ context.Context, _, _ string) (map[string]float64, bool) {
called = true
return map[string]float64{"input": 1e-06}, true
})

assert.Nil(t, c.resolveModelPricingIfMissing(nil, &WorkflowData{
Model: "auto",
EngineConfig: &EngineConfig{ID: "copilot"},
}))
assert.Nil(t, c.resolveModelPricingIfMissing(nil, &WorkflowData{
Model: "github_models/auto",
EngineConfig: &EngineConfig{ID: "copilot"},
}))
assert.False(t, called)
}