返回 DeepSeek-Reasonix
default_model.go
根目录 / internal / cli / default_model.go
1 package cli
2
3 import (
4 "fmt"
5 "strings"
6
7 "reasonix/internal/config"
8 "reasonix/internal/extension/providerext"
9 )
10
11 // resolveModelForCLI picks the chat model a CLI subcommand should boot on. A
12 // keyless default falls through to the first configured chat provider, while
13 // explicit model choices remain strict.
14 //
15 // Semantics:
16 //
17 // - explicitRef == "": Config.ResolveNewSessionChatModel supplies the shared
18 // default/fallback policy used by boot and desktop. Non-chat models are
19 // never selected as a fallback.
20 //
21 // - explicitRef != "": the caller asked for this exact ref (--model flag,
22 // ACP session param, etc.). The ref must resolve AND be configured.
23 // There is no silent fallback — explicit choices that misfire must
24 // fail loudly so the user is not quietly rerouted onto a model they
25 // did not ask for. Plugin-namespaced refs (plugin/<plugin>/<provider>/
26 // <model>) pass through unresolved: they belong to extension sidecars,
27 // not the config catalog, and boot's merged resolver is their only gate.
28 func resolveModelForCLI(explicitRef string, cfg *config.Config) (ref string, fallback bool, err error) {
29 explicitRef = strings.TrimSpace(explicitRef)
30 if explicitRef != "" {
31 if providerext.PluginRefOwner(explicitRef) != "" {
32 return explicitRef, false, nil
33 }
34 entry, ok := cfg.ResolveModel(explicitRef)
35 if !ok {
36 return "", false, fmt.Errorf("unknown model %q", explicitRef)
37 }
38 if !entry.Configured() {
39 // Provider keys resolve only from the Reasonix credentials file, so
40 // naming the variable alone sends users to their shell environment.
41 return "", false, fmt.Errorf("model %q has no API key: add %s=<key> to %s or run `reasonix setup` (shell environment variables are not read)", explicitRef, entry.APIKeyEnv, config.UserCredentialsPath())
42 }
43 return entry.Name + "/" + entry.Model, false, nil
44 }
45 ref, fallback, _ = cfg.ResolveNewSessionChatModel()
46 return ref, fallback, nil
47 }
48
49 // resolveServeModel keeps serve's implicit model scoped to the user config.
50 // A project reasonix.toml may configure the served workspace, but it must not
51 // replace the account-level model used for new serve sessions.
52 func resolveServeModel(modelName string) string {
53 if strings.TrimSpace(modelName) != "" {
54 return modelName
55 }
56 cfg := config.LoadForEdit(config.UserConfigPath())
57 if resolved, _, ok := cfg.ResolveNewSessionChatModel(); ok {
58 return resolved
59 }
60 return modelName
61 }
62
62 lines GO