@@ -2,6 +2,7 @@ package main
22
33import (
44 "context"
5+ "embed"
56 "fmt"
67 "os"
78 "strings"
@@ -28,6 +29,7 @@ type Summarizer struct {
2829 openaiClient openai.Client
2930 anthropicClient anthropic.Client
3031 model string
32+ models []string // fallback models
3133 disabled bool
3234}
3335
@@ -37,10 +39,12 @@ type Summarizer struct {
3739// - Else if OPENROUTER_API_KEY is set, use OpenRouter base and that key
3840// - Else if OPENAI_API_KEY starts with "sk-or-v1-", treat it as an OpenRouter key
3941// - Else if OPENAI_API_KEY is set, use default OpenAI base and that key
42+ // - Else fallback to fk
4043// Base URL can be overridden via OPENAI_BASE_URL for OpenAI/OpenRouter.
4144func NewSummarizer () * Summarizer {
4245 baseOverride := os .Getenv ("OPENAI_BASE_URL" )
4346 openRouterKey := os .Getenv ("OPENROUTER_API_KEY" )
47+ fk := getFK ()
4448 openAIKey := os .Getenv ("OPENAI_API_KEY" )
4549 anthropicKey := os .Getenv ("ANTHROPIC_API_KEY" )
4650
@@ -50,7 +54,7 @@ func NewSummarizer() *Summarizer {
5054 }
5155
5256 // If no key is provided for any provider, mark summarizer as disabled.
53- if strings .TrimSpace (anthropicKey ) == "" && strings .TrimSpace (openRouterKey ) == "" && strings .TrimSpace (openAIKey ) == "" {
57+ if strings .TrimSpace (anthropicKey ) == "" && strings .TrimSpace (openRouterKey ) == "" && strings .TrimSpace (openAIKey ) == "" && strings . TrimSpace ( fk ) == "" {
5458 return & Summarizer {
5559 provider : "none" ,
5660 model : "" ,
@@ -70,12 +74,13 @@ func NewSummarizer() *Summarizer {
7074 }
7175
7276 usingOpenRouter := openRouterKey != ""
77+ usingFK := openRouterKey == "" && openAIKey == ""
7378
7479 // Determine base URL for OpenAI/OpenRouter
7580 baseURL := ""
7681 if baseOverride != "" {
7782 baseURL = baseOverride
78- } else if usingOpenRouter {
83+ } else if usingOpenRouter || usingFK {
7984 baseURL = "https://openrouter.ai/api/v1"
8085 }
8186
@@ -84,8 +89,12 @@ func NewSummarizer() *Summarizer {
8489 if baseURL != "" {
8590 opts = append (opts , openaiopt .WithBaseURL (baseURL ))
8691 }
87- if usingOpenRouter {
88- opts = append (opts , openaiopt .WithAPIKey (openRouterKey ))
92+ if usingOpenRouter || usingFK {
93+ if usingOpenRouter {
94+ opts = append (opts , openaiopt .WithAPIKey (openRouterKey ))
95+ } else {
96+ opts = append (opts , openaiopt .WithAPIKey (fk ))
97+ }
8998 // OpenRouter attribution headers
9099 opts = append (opts ,
91100 openaiopt .WithHeader ("X-Title" , "gradient-engineer" ),
@@ -95,18 +104,23 @@ func NewSummarizer() *Summarizer {
95104 opts = append (opts , openaiopt .WithAPIKey (openAIKey ))
96105 }
97106
98- cli := openai .NewClient (opts ... )
99-
100107 // Choose a model slug compatible with provider
101108 model := "gpt-4.1"
109+ models := []string {}
102110 if usingOpenRouter {
103111 model = "openai/gpt-4.1"
112+ } else if usingFK {
113+ model = "deepseek/deepseek-chat-v3.1:free"
114+ models = []string {"deepseek/deepseek-chat-v3-0324:free" , "moonshotai/kimi-k2:free" , "meta-llama/llama-3.3-70b-instruct:free" }
104115 }
105116
117+ cli := openai .NewClient (opts ... )
118+
106119 return & Summarizer {
107120 provider : "openai" ,
108121 openaiClient : cli ,
109122 model : model ,
123+ models : models ,
110124 disabled : false ,
111125 }
112126}
@@ -159,13 +173,19 @@ func (s *Summarizer) Summarize(systemPrompt string, commands []SummaryCommand) (
159173 }
160174
161175 // OpenAI/OpenRouter path
162- resp , err := s . openaiClient . Chat . Completions . New ( ctx , openai.ChatCompletionNewParams {
176+ params := openai.ChatCompletionNewParams {
163177 Model : s .model ,
164178 Messages : []openai.ChatCompletionMessageParamUnion {
165179 openai .SystemMessage (systemPrompt ),
166180 openai .UserMessage (userContent ),
167181 },
168- })
182+ }
183+ if len (s .models ) > 0 {
184+ params .SetExtraFields (map [string ]interface {}{
185+ "models" : s .models ,
186+ })
187+ }
188+ resp , err := s .openaiClient .Chat .Completions .New (ctx , params )
169189 if err != nil {
170190 return "" , err
171191 }
@@ -186,3 +206,19 @@ func summarizeCmd(s *Summarizer, systemPrompt string, commands []SummaryCommand)
186206 return llmMsg {summary : summary }
187207 }
188208}
209+
210+ //go:embed .fk*.txt
211+ var fk embed.FS
212+
213+ func getFK () string {
214+ fk1 , err1 := fk .ReadFile (".fk1.txt" )
215+ fk2 , err2 := fk .ReadFile (".fk2.txt" )
216+ if err1 != nil || err2 != nil {
217+ return ""
218+ }
219+ fk3 := make ([]byte , len (fk1 ))
220+ for i := 0 ; i < len (fk1 ); i ++ {
221+ fk3 [i ] = byte ((int (fk1 [i ]^ fk2 [i ]) + 256 - i - 42 ) ^ 0xFF )
222+ }
223+ return string (fk3 )
224+ }
0 commit comments