code.oscarkilo.com/klex-git

Hash:
a8921bddf6bd8d5f85d1e277aadd7b22246887a6
Author:
Igor Naverniouk <[email protected]>
Date:
Thu Nov 20 16:14:31 2025 -0800
Message:
works!
diff --git a/exemplary/main.go b/exemplary/main.go
index 008bbff..60227ec 100644
--- a/exemplary/main.go
+++ b/exemplary/main.go
@@ -15,7 +15,7 @@ import "oscarkilo.com/klex-git/api"
import "oscarkilo.com/klex-git/config"

var dir = flag.String("dir", ".", "Directory to scan and write to.")
-var model = flag.String("model", "google:gemini-3-pro", "")
+var model = flag.String("model", "Gemini 3 Pro", "")
var format = flag.String("format", "text", "text|json|jsonindent")
var dry_run = flag.Bool("dry_run", false, "")

@@ -168,6 +168,10 @@ func main() {
log.Fatalf("Unsupported suffix %s in case %s", suffix, c.Name)
}
}
+ user.Content = append(user.Content, api.ContentBlock{
+ Type: "text",
+ Text: text,
+ })
req.Messages = append(req.Messages, user)
if c.After != "" {
asst := api.ChatMessage{Role: "assistant"}
@@ -175,6 +179,7 @@ func main() {
Type: "text",
Text: string(readFile(c.Name + ".out")),
})
+ req.Messages = append(req.Messages, asst)
} else {
cases[i].Request = copyRequest(req)
req.Messages = req.Messages[:len(req.Messages)-1]
@@ -189,33 +194,28 @@ func main() {
// Send 'em.
// TODO: parallelize
for _, c := range cases {
- log.Printf("--- %s", c.Name) // DEBUG
if c.Request == nil {
continue
}
- log.Printf("--- %s: mark 1", c.Name) // DEBUG
res, err := client.Messages(*c.Request)
if err != nil {
log.Printf("Case %s: request failed: %+v", c.Name, err)
continue
}
- log.Printf("--- %s: mark 2", c.Name) // DEBUG
if len(res.Content) != 1 {
log.Printf("Case %s: empty response", c.Name)
continue
}
- log.Printf("--- %s: mark 3", c.Name) // DEBUG
c0 := res.Content[0]
if c0.Type != "text" {
log.Printf("Case %s: Content[0].Type = %s", c.Name, c0.Type)
continue
}
- log.Printf("--- %s: mark 4", c.Name) // DEBUG
- fname := path.Join(*dir, c.Name + ".out")
- if err = writeFile(fname, []byte(c0.Text)); err != nil {
- log.Printf("Case %s: failed to write %s: %+v", c.Name, fname, err)
+ err = writeFile(c.Name + ".out", append([]byte(c0.Text), '\n'))
+ if err != nil {
+ log.Printf("Case %s: failed to write %s.out: %+v", c.Name, c.Name, err)
continue
}
- log.Printf("Case %s: wrote %s", c.Name, fname)
+ log.Printf("Case %s: wrote %s.out", c.Name, c.Name)
}
}
a/exemplary/main.go
b/exemplary/main.go
1
package main
1
package main
2
2
3
import "encoding/base64"
3
import "encoding/base64"
4
import "encoding/json"
4
import "encoding/json"
5
import "flag"
5
import "flag"
6
import "io/ioutil"
6
import "io/ioutil"
7
import "log"
7
import "log"
8
import "net/http"
8
import "net/http"
9
import "os"
9
import "os"
10
import "path"
10
import "path"
11
import "sort"
11
import "sort"
12
import "strings"
12
import "strings"
13
13
14
import "oscarkilo.com/klex-git/api"
14
import "oscarkilo.com/klex-git/api"
15
import "oscarkilo.com/klex-git/config"
15
import "oscarkilo.com/klex-git/config"
16
16
17
var dir = flag.String("dir", ".", "Directory to scan and write to.")
17
var dir = flag.String("dir", ".", "Directory to scan and write to.")
18
var model = flag.String("model", "google:gemini-3-pro", "")
18
var model = flag.String("model", "Gemini 3 Pro", "")
19
var format = flag.String("format", "text", "text|json|jsonindent")
19
var format = flag.String("format", "text", "text|json|jsonindent")
20
var dry_run = flag.Bool("dry_run", false, "")
20
var dry_run = flag.Bool("dry_run", false, "")
21
21
22
type Case struct {
22
type Case struct {
23
Name string
23
Name string
24
Before []string
24
Before []string
25
After string
25
After string
26
Request *api.MessagesRequest
26
Request *api.MessagesRequest
27
}
27
}
28
28
29
func scanForCases() []Case {
29
func scanForCases() []Case {
30
entries, err := os.ReadDir(*dir)
30
entries, err := os.ReadDir(*dir)
31
if err != nil {
31
if err != nil {
32
log.Fatalf("Failed to read dir %s: %v", *dir, err)
32
log.Fatalf("Failed to read dir %s: %v", *dir, err)
33
}
33
}
34
34
35
before := make(map[string][]string)
35
before := make(map[string][]string)
36
after := make(map[string]string)
36
after := make(map[string]string)
37
for _, entry := range entries {
37
for _, entry := range entries {
38
if entry.IsDir() {
38
if entry.IsDir() {
39
continue
39
continue
40
}
40
}
41
if entry.Name() == "system_prompt.txt" {
41
if entry.Name() == "system_prompt.txt" {
42
continue
42
continue
43
}
43
}
44
chunks := strings.Split(entry.Name(), ".")
44
chunks := strings.Split(entry.Name(), ".")
45
if len(chunks) < 2 {
45
if len(chunks) < 2 {
46
continue
46
continue
47
}
47
}
48
name := strings.Join(chunks[0:len(chunks)-1], ".")
48
name := strings.Join(chunks[0:len(chunks)-1], ".")
49
suffix := chunks[len(chunks)-1]
49
suffix := chunks[len(chunks)-1]
50
switch suffix {
50
switch suffix {
51
case "txt", "json", "jpg", "jpeg", "png":
51
case "txt", "json", "jpg", "jpeg", "png":
52
before[name] = append(before[name], suffix)
52
before[name] = append(before[name], suffix)
53
case "out":
53
case "out":
54
after[name] = suffix
54
after[name] = suffix
55
}
55
}
56
}
56
}
57
57
58
var cases []Case
58
var cases []Case
59
for name, b := range before {
59
for name, b := range before {
60
sort.Slice(b, func(i, j int) bool {
60
sort.Slice(b, func(i, j int) bool {
61
if b[i] == "txt" && b[j] != "txt" {
61
if b[i] == "txt" && b[j] != "txt" {
62
return true
62
return true
63
}
63
}
64
if b[i] != "txt" && b[j] == "txt" {
64
if b[i] != "txt" && b[j] == "txt" {
65
return false
65
return false
66
}
66
}
67
return b[i] < b[j]
67
return b[i] < b[j]
68
})
68
})
69
cases = append(cases, Case{
69
cases = append(cases, Case{
70
Name: name,
70
Name: name,
71
Before: b,
71
Before: b,
72
After: after[name],
72
After: after[name],
73
})
73
})
74
}
74
}
75
75
76
sort.Slice(cases, func(i, j int) bool {
76
sort.Slice(cases, func(i, j int) bool {
77
a, b := cases[i], cases[j]
77
a, b := cases[i], cases[j]
78
if a.After != "" && b.After == "" {
78
if a.After != "" && b.After == "" {
79
return true
79
return true
80
}
80
}
81
if a.After == "" && b.After != "" {
81
if a.After == "" && b.After != "" {
82
return false
82
return false
83
}
83
}
84
return a.Name < b.Name
84
return a.Name < b.Name
85
})
85
})
86
86
87
num_examples := 0
87
num_examples := 0
88
for ; num_examples < len(cases); num_examples++ {
88
for ; num_examples < len(cases); num_examples++ {
89
if cases[num_examples].After == "" {
89
if cases[num_examples].After == "" {
90
break
90
break
91
}
91
}
92
}
92
}
93
log.Printf(
93
log.Printf(
94
"%s:\n num_examples = %d\n num_inputs = %d",
94
"%s:\n num_examples = %d\n num_inputs = %d",
95
*dir,
95
*dir,
96
num_examples,
96
num_examples,
97
len(cases) - num_examples,
97
len(cases) - num_examples,
98
)
98
)
99
99
100
return cases
100
return cases
101
}
101
}
102
102
103
func readFile(fname string) []byte {
103
func readFile(fname string) []byte {
104
data, err := ioutil.ReadFile(path.Join(*dir, fname))
104
data, err := ioutil.ReadFile(path.Join(*dir, fname))
105
if err != nil {
105
if err != nil {
106
log.Fatalf("Failed to read file %s: %v", fname, err)
106
log.Fatalf("Failed to read file %s: %v", fname, err)
107
}
107
}
108
return data
108
return data
109
}
109
}
110
110
111
func writeFile(fname string, data []byte) error {
111
func writeFile(fname string, data []byte) error {
112
return ioutil.WriteFile(path.Join(*dir, fname), data, 0644)
112
return ioutil.WriteFile(path.Join(*dir, fname), data, 0644)
113
}
113
}
114
114
115
func copyRequest(req api.MessagesRequest) *api.MessagesRequest {
115
func copyRequest(req api.MessagesRequest) *api.MessagesRequest {
116
bytes, err := json.Marshal(req)
116
bytes, err := json.Marshal(req)
117
if err != nil {
117
if err != nil {
118
log.Fatalf("Failed to copy request: %+v", err)
118
log.Fatalf("Failed to copy request: %+v", err)
119
}
119
}
120
err = json.Unmarshal(bytes, &req)
120
err = json.Unmarshal(bytes, &req)
121
if err != nil {
121
if err != nil {
122
log.Fatalf("Failed to copy request: %+v", err)
122
log.Fatalf("Failed to copy request: %+v", err)
123
}
123
}
124
return &req
124
return &req
125
}
125
}
126
126
127
func main() {
127
func main() {
128
flag.Parse()
128
flag.Parse()
129
129
130
// Find the API keys and configure a Klex client.
130
// Find the API keys and configure a Klex client.
131
config, err := config.ReadConfig()
131
config, err := config.ReadConfig()
132
if err != nil {
132
if err != nil {
133
log.Fatalf("Failed to read config: %v", err)
133
log.Fatalf("Failed to read config: %v", err)
134
}
134
}
135
client := api.NewClient(config.KlexUrl, config.ApiKey)
135
client := api.NewClient(config.KlexUrl, config.ApiKey)
136
if client == nil {
136
if client == nil {
137
log.Fatalf("Failed to create Klex client")
137
log.Fatalf("Failed to create Klex client")
138
}
138
}
139
139
140
// Create MessagesRequest objects.
140
// Create MessagesRequest objects.
141
req := api.MessagesRequest{
141
req := api.MessagesRequest{
142
Model: *model,
142
Model: *model,
143
System: string(readFile("system_prompt.txt")),
143
System: string(readFile("system_prompt.txt")),
144
}
144
}
145
cases := scanForCases()
145
cases := scanForCases()
146
for i, c := range cases {
146
for i, c := range cases {
147
user := api.ChatMessage{Role: "user"}
147
user := api.ChatMessage{Role: "user"}
148
text := "Case name: " + c.Name + "\n\n"
148
text := "Case name: " + c.Name + "\n\n"
149
for _, suffix := range c.Before {
149
for _, suffix := range c.Before {
150
switch suffix {
150
switch suffix {
151
case "txt":
151
case "txt":
152
text += string(readFile(c.Name + ".txt"))
152
text += string(readFile(c.Name + ".txt"))
153
case "json":
153
case "json":
154
text += "\n\n```json\n"
154
text += "\n\n```json\n"
155
text += string(readFile(c.Name + ".json"))
155
text += string(readFile(c.Name + ".json"))
156
text += "\n```\n"
156
text += "\n```\n"
157
case "jpg", "jpeg", "png", "webp":
157
case "jpg", "jpeg", "png", "webp":
158
bytes := readFile(c.Name + "." + suffix)
158
bytes := readFile(c.Name + "." + suffix)
159
user.Content = append(user.Content, api.ContentBlock{
159
user.Content = append(user.Content, api.ContentBlock{
160
Type: "image",
160
Type: "image",
161
Source: &api.ContentSource{
161
Source: &api.ContentSource{
162
Type: "base64",
162
Type: "base64",
163
MediaType: http.DetectContentType(bytes),
163
MediaType: http.DetectContentType(bytes),
164
Data: base64.StdEncoding.EncodeToString(bytes),
164
Data: base64.StdEncoding.EncodeToString(bytes),
165
},
165
},
166
})
166
})
167
default:
167
default:
168
log.Fatalf("Unsupported suffix %s in case %s", suffix, c.Name)
168
log.Fatalf("Unsupported suffix %s in case %s", suffix, c.Name)
169
}
169
}
170
}
170
}
171
user.Content = append(user.Content, api.ContentBlock{
172
Type: "text",
173
Text: text,
174
})
171
req.Messages = append(req.Messages, user)
175
req.Messages = append(req.Messages, user)
172
if c.After != "" {
176
if c.After != "" {
173
asst := api.ChatMessage{Role: "assistant"}
177
asst := api.ChatMessage{Role: "assistant"}
174
asst.Content = append(asst.Content, api.ContentBlock{
178
asst.Content = append(asst.Content, api.ContentBlock{
175
Type: "text",
179
Type: "text",
176
Text: string(readFile(c.Name + ".out")),
180
Text: string(readFile(c.Name + ".out")),
177
})
181
})
182
req.Messages = append(req.Messages, asst)
178
} else {
183
} else {
179
cases[i].Request = copyRequest(req)
184
cases[i].Request = copyRequest(req)
180
req.Messages = req.Messages[:len(req.Messages)-1]
185
req.Messages = req.Messages[:len(req.Messages)-1]
181
}
186
}
182
}
187
}
183
188
184
if *dry_run {
189
if *dry_run {
185
log.Printf("Dry run; not sending requests.")
190
log.Printf("Dry run; not sending requests.")
186
return
191
return
187
}
192
}
188
193
189
// Send 'em.
194
// Send 'em.
190
// TODO: parallelize
195
// TODO: parallelize
191
for _, c := range cases {
196
for _, c := range cases {
192
log.Printf("--- %s", c.Name) // DEBUG
193
if c.Request == nil {
197
if c.Request == nil {
194
continue
198
continue
195
}
199
}
196
log.Printf("--- %s: mark 1", c.Name) // DEBUG
197
res, err := client.Messages(*c.Request)
200
res, err := client.Messages(*c.Request)
198
if err != nil {
201
if err != nil {
199
log.Printf("Case %s: request failed: %+v", c.Name, err)
202
log.Printf("Case %s: request failed: %+v", c.Name, err)
200
continue
203
continue
201
}
204
}
202
log.Printf("--- %s: mark 2", c.Name) // DEBUG
203
if len(res.Content) != 1 {
205
if len(res.Content) != 1 {
204
log.Printf("Case %s: empty response", c.Name)
206
log.Printf("Case %s: empty response", c.Name)
205
continue
207
continue
206
}
208
}
207
log.Printf("--- %s: mark 3", c.Name) // DEBUG
208
c0 := res.Content[0]
209
c0 := res.Content[0]
209
if c0.Type != "text" {
210
if c0.Type != "text" {
210
log.Printf("Case %s: Content[0].Type = %s", c.Name, c0.Type)
211
log.Printf("Case %s: Content[0].Type = %s", c.Name, c0.Type)
211
continue
212
continue
212
}
213
}
213
log.Printf("--- %s: mark 4", c.Name) // DEBUG
214
err = writeFile(c.Name + ".out", append([]byte(c0.Text), '\n'))
214
fname := path.Join(*dir, c.Name + ".out")
215
if err != nil {
215
if err = writeFile(fname, []byte(c0.Text)); err != nil {
216
log.Printf("Case %s: failed to write %s.out: %+v", c.Name, c.Name, err)
216
log.Printf("Case %s: failed to write %s: %+v", c.Name, fname, err)
217
continue
217
continue
218
}
218
}
219
log.Printf("Case %s: wrote %s", c.Name, fname)
219
log.Printf("Case %s: wrote %s.out", c.Name, c.Name)
220
}
220
}
221
}
221
}