grokkit/todo/completed/MODEL_ENFORCEMENT.md
Greg Gauthier 200b2f9aaf
All checks were successful
CI / Test (push) Successful in 30s
CI / Lint (push) Successful in 25s
CI / Build (push) Successful in 21s
refactor(todo): organize TODO items into queued and completed directories
- Update README.md to reflect new todo/ directory structure with queued/ for pending items and completed/ for historical records.
- Rename docs/TODO.md to todo/completed/MODEL_ENFORCEMENT.md.
- Add example todo/queued/TODO_ITEM.md with initial TODO steps.
2026-03-02 19:03:27 +00:00

6.7 KiB

Enforcing specific models for specific actions is a great idea.

The lint fixes are now working after the model switch to "grok-4-1-fast-non-reasoning" (which is faster and more reliable for code editing tasks). To make this consistent and avoid manual --model flags every time, let's enforce model selection per command.

Why This Makes Sense

  • Grok-4 is great for reasoning-heavy tasks (e.g. plan generation, reviews).
  • Grok-4-1-fast-non-reasoning is better for quick code edits/fixes (faster, less "thinking" overhead).
  • This way, lint defaults to the fast model, while review or agent uses the full reasoning one.
  • We can make it configurable via config.toml.

How to Implement It

  1. Update config/config.go (add per-command defaults)

Replace your config/config.go with this:

package config

import (
	"os"

	"github.com/spf13/viper"
)

func Load() {
	home, _ := os.UserHomeDir()
	viper.SetConfigName("config")
	viper.SetConfigType("toml")
	viper.AddConfigPath(home + "/.config/grokkit")
	viper.AddConfigPath(".")
	viper.AutomaticEnv()

	viper.SetDefault("default_model", "grok-4")
	viper.SetDefault("temperature", 0.7)
	viper.SetDefault("commands.lint.model", "grok-4-1-fast-non-reasoning")
	viper.SetDefault("commands.agent.model", "grok-4")
	// Add more defaults as needed

	viper.ReadInConfig()
}

// GetModel returns the model, respecting command-specific defaults, then flag, then global
func GetModel(commandName string, flagModel string) string {
	if flagModel != "" {
		// Check alias
		if alias := viper.GetString("aliases." + flagModel); alias != "" {
			return alias
		}
		return flagModel
	}

	// Command-specific default
	cmdModel := viper.GetString("commands." + commandName + ".model")
	if cmdModel != "" {
		return cmdModel
	}

	// Global default
	return viper.GetString("default_model")
}
  1. Update your config.toml (~/.config/grokkit/config.toml or local one)

Add this section:

[commands]
  [commands.lint]
  model = "grok-4-1-fast-non-reasoning"
  
  [commands.agent]
  model = "grok-4"
  1. Update cmd/lint.go (use GetModel)

In the API call section, change:

model := "grok-4-1-fast-non-reasoning"  // or whatever you had

to:

modelFlag, _ := cmd.Flags().GetString("model")
model := config.GetModel("lint", modelFlag)

Do the same for other commands (e.g. agent.go):

modelFlag, _ := cmd.Flags().GetString("model")
model := config.GetModel("agent", modelFlag)

Test it

build/grokkit lint pyrdle.py --debug

This should now default to "grok-4-1-fast-non-reasoning" for lint fixes without the flag.

Additional Suggestions

  • Prompt tweaks for lint: Make the system prompt more explicit about fixing all issues (e.g. "Return ONLY the full fixed code. Fix EVERY whitespace, line length, and indentation issue from the linter output. Do not add or remove functionality.").
  • Auto-fix flag: Add --auto-fix to apply without prompt.
  • Re-run lint after fix: You already have this, but add a loop (max 3 iterations) if issues remain.

I strongly recommend following the same pattern for the edit and agent subcommands (and ideally all commands that make API calls). It's a smart way to make your tool more consistent, efficient, and user-friendly without requiring manual flags every time. Here's why, and how to do it step by step.

Why This Pattern is Worth Extending

  • Performance & Cost Optimization: Different commands have different needs. edit is about quick code transformations, so "grok-4-1-fast-non-reasoning" is ideal (faster, cheaper, less "thinking" overhead). agent involves planning and multi-file reasoning, so "grok-4" (full reasoning) is better. This avoids overusing the heavy model for everything.
  • User Experience: Users can override with --model, but defaults "just work" out of the box.
  • Scalability: As you add more commands, config.toml becomes the central place to tune them (e.g. add "chat.model = grok-4").
  • Consistency: All your API-calling commands now use the same GetModel logic — easy to maintain.

Step-by-Step Implementation

  1. Update config/config.go (add defaults for edit and agent)

Replace your config/config.go with this expanded version:

package config

import (
	"os"

	"github.com/spf13/viper"
)

func Load() {
	home, _ := os.UserHomeDir()
	viper.SetConfigName("config")
	viper.SetConfigType("toml")
	viper.AddConfigPath(home + "/.config/grokkit")
	viper.AddConfigPath(".")
	viper.AutomaticEnv()

	viper.SetDefault("default_model", "grok-4")
	viper.SetDefault("temperature", 0.7)
	viper.SetDefault("commands.lint.model", "grok-4-1-fast-non-reasoning")
	viper.SetDefault("commands.edit.model", "grok-4-1-fast-non-reasoning")  // Fast for edits
	viper.SetDefault("commands.agent.model", "grok-4")  // Reasoning for agent
	viper.SetDefault("commands.chat.model", "grok-4")   // Default for chat
	viper.SetDefault("commands.review.model", "grok-4")
	viper.SetDefault("commands.commit.model", "grok-4")
	// Add more as needed

	viper.ReadInConfig()
}

func GetModel(commandName string, flagModel string) string {
	if flagModel != "" {
		if alias := viper.GetString("aliases." + flagModel); alias != "" {
			return alias
		}
		return flagModel
	}

	cmdModel := viper.GetString("commands." + commandName + ".model")
	if cmdModel != "" {
		return cmdModel
	}
	return viper.GetString("default_model")
}
  1. Update config.toml (add the new defaults)

Add this to your ~/.config/grokkit/config.toml (or local one):

[commands]
  [commands.edit]
  model = "grok-4-1-fast-non-reasoning"  # Fast for single-file edits
  
  [commands.agent]
  model = "grok-4"  # Full reasoning for multi-file
  
  [commands.chat]
  model = "grok-4"  # Default for chat
  1. Update cmd/edit.go (use GetModel)

In the Run function, change:

model := "grok-4-1-fast-non-reasoning"  // old hardcode

to:

modelFlag, _ := cmd.Flags().GetString("model")
model := config.GetModel("edit", modelFlag)
  1. Update cmd/agent.go (use GetModel)

In the Run function, change:

model := config.GetModel(modelFlag)  // old

to:

modelFlag, _ := cmd.Flags().GetString("model")
model := config.GetModel("agent", modelFlag)
  1. Update cmd/chat.go (use GetModel)

In the Run function, change:

model := config.GetModel(modelFlag)  // old

to:

modelFlag, _ := cmd.Flags().GetString("model")
model := config.GetModel("chat", modelFlag)

Test it

go build -o build/grokkit .
build/grokkit edit somefile.py "make this cleaner"  # should use fast model by default
build/grokkit agent "add headers to all files"  # uses full grok-4
build/grokkit chat --model 4-1-fast-non-reasoning  # override with alias