Skip to content

Commit 1ae2cdc

Browse files
committed
making progress on openai tool use
1 parent 825573c commit 1ae2cdc

12 files changed

Lines changed: 254 additions & 182 deletions

File tree

cmd/testai/main-testai.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ func getToolDefinitions() []uctypes.ToolDefinition {
101101
}
102102

103103
func testOpenAI(ctx context.Context, model, message string, tools []uctypes.ToolDefinition) {
104-
apiKey := os.Getenv("OPENAI_API_KEY")
104+
apiKey := os.Getenv("OPENAI_APIKEY")
105105
if apiKey == "" {
106-
fmt.Println("Error: OPENAI_API_KEY environment variable not set")
106+
fmt.Println("Error: OPENAI_APIKEY environment variable not set")
107107
os.Exit(1)
108108
}
109109

@@ -139,9 +139,10 @@ func testOpenAI(ctx context.Context, model, message string, tools []uctypes.Tool
139139
defer sseHandler.Close()
140140

141141
chatOpts := uctypes.WaveChatOpts{
142-
ChatId: chatID,
143-
Config: *opts,
144-
Tools: tools,
142+
ChatId: chatID,
143+
ClientId: uuid.New().String(),
144+
Config: *opts,
145+
Tools: tools,
145146
}
146147
err := aiusechat.WaveAIPostMessageWrap(ctx, sseHandler, aiMessage, chatOpts)
147148
if err != nil {
@@ -150,9 +151,9 @@ func testOpenAI(ctx context.Context, model, message string, tools []uctypes.Tool
150151
}
151152

152153
func testAnthropic(ctx context.Context, model, message string, tools []uctypes.ToolDefinition) {
153-
apiKey := os.Getenv("ANTHROPIC_API_KEY")
154+
apiKey := os.Getenv("ANTHROPIC_APIKEY")
154155
if apiKey == "" {
155-
fmt.Println("Error: ANTHROPIC_API_KEY environment variable not set")
156+
fmt.Println("Error: ANTHROPIC_APIKEY environment variable not set")
156157
os.Exit(1)
157158
}
158159

@@ -188,9 +189,10 @@ func testAnthropic(ctx context.Context, model, message string, tools []uctypes.T
188189
defer sseHandler.Close()
189190

190191
chatOpts := uctypes.WaveChatOpts{
191-
ChatId: chatID,
192-
Config: *opts,
193-
Tools: tools,
192+
ChatId: chatID,
193+
ClientId: uuid.New().String(),
194+
Config: *opts,
195+
Tools: tools,
194196
}
195197
err := aiusechat.WaveAIPostMessageWrap(ctx, sseHandler, aiMessage, chatOpts)
196198
if err != nil {
@@ -204,6 +206,12 @@ func testT1(ctx context.Context) {
204206
testAnthropic(ctx, "claude-sonnet-4-20250514", "what is 2+2, use the provider adder tool", tools)
205207
}
206208

209+
func testT2(ctx context.Context) {
210+
tool := aiusechat.GetAdderToolDefinition()
211+
tools := []uctypes.ToolDefinition{tool}
212+
testOpenAI(ctx, "gpt-5", "what is 2+2, use the provider adder tool", tools)
213+
}
214+
207215
func printUsage() {
208216
fmt.Println("Usage: go run main-testai.go [--anthropic] [--tools] [--model <model>] [message]")
209217
fmt.Println("Examples:")
@@ -218,18 +226,19 @@ func printUsage() {
218226
fmt.Println(" Anthropic: claude-sonnet-4-20250514")
219227
fmt.Println("")
220228
fmt.Println("Environment variables:")
221-
fmt.Println(" OPENAI_API_KEY (for OpenAI models)")
222-
fmt.Println(" ANTHROPIC_API_KEY (for Anthropic models)")
229+
fmt.Println(" OPENAI_APIKEY (for OpenAI models)")
230+
fmt.Println(" ANTHROPIC_APIKEY (for Anthropic models)")
223231
}
224232

225233
func main() {
226-
var anthropic, tools, help, t1 bool
234+
var anthropic, tools, help, t1, t2 bool
227235
var model string
228236
flag.BoolVar(&anthropic, "anthropic", false, "Use Anthropic API instead of OpenAI")
229237
flag.BoolVar(&tools, "tools", false, "Enable GitHub Actions Monitor tools for testing")
230238
flag.StringVar(&model, "model", "", "AI model to use (defaults: gpt-5 for OpenAI, claude-sonnet-4-20250514 for Anthropic)")
231239
flag.BoolVar(&help, "help", false, "Show usage information")
232240
flag.BoolVar(&t1, "t1", false, "Run preset T1 test (claude-sonnet-4-20250514 with 'what is 2+2')")
241+
flag.BoolVar(&t2, "t2", false, "Run preset T2 test (gpt-5 with 'what is 2+2')")
233242
flag.Parse()
234243

235244
if help {
@@ -244,6 +253,10 @@ func main() {
244253
testT1(ctx)
245254
return
246255
}
256+
if t2 {
257+
testT2(ctx)
258+
return
259+
}
247260

248261
// Set default model based on API type if not provided
249262
if model == "" {

cmd/testopenai/main-testopenai.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ import (
1515
"os"
1616
"time"
1717

18+
"github.com/wavetermdev/waveterm/pkg/aiusechat"
1819
"github.com/wavetermdev/waveterm/pkg/aiusechat/openai"
1920
)
2021

21-
func makeOpenAIRequest(ctx context.Context, apiKey, model, message string) error {
22+
func makeOpenAIRequest(ctx context.Context, apiKey, model, message string, tools bool) error {
2223
reqBody := openai.OpenAIRequest{
2324
Model: model,
2425
Input: []openai.OpenAIMessage{
@@ -36,6 +37,11 @@ func makeOpenAIRequest(ctx context.Context, apiKey, model, message string) error
3637
StreamOptions: &openai.StreamOptionsType{IncludeObfuscation: false},
3738
Reasoning: &openai.ReasoningType{Effort: "medium"},
3839
}
40+
if tools {
41+
reqBody.Tools = []openai.OpenAIRequestTool{
42+
openai.ConvertToolDefinitionToOpenAI(aiusechat.GetAdderToolDefinition()),
43+
}
44+
}
3945

4046
jsonData, err := json.Marshal(reqBody)
4147
if err != nil {
@@ -103,10 +109,11 @@ func processSSEStream(reader io.Reader) error {
103109
}
104110

105111
func printUsage() {
106-
fmt.Println("Usage: go run main-testopenai.go [--model <model>] [message]")
112+
fmt.Println("Usage: go run main-testopenai.go [--model <model>] [--tools] [message]")
107113
fmt.Println("Examples:")
108114
fmt.Println(" go run main-testopenai.go 'Stream me a limerick about gophers coding in Go.'")
109115
fmt.Println(" go run main-testopenai.go --model gpt-4 'What is 2+2?'")
116+
fmt.Println(" go run main-testopenai.go --tools 'What is 2+2? Use the adder tool.'")
110117
fmt.Println("")
111118
fmt.Println("Default model: gpt-5-mini")
112119
fmt.Println("")
@@ -117,9 +124,11 @@ func printUsage() {
117124
func main() {
118125
var model string
119126
var showHelp bool
127+
var tools bool
120128

121129
flag.StringVar(&model, "model", "gpt-5-mini", "OpenAI model to use")
122130
flag.BoolVar(&showHelp, "help", false, "Show usage information")
131+
flag.BoolVar(&tools, "tools", false, "Enable tools for testing")
123132
flag.Parse()
124133

125134
if showHelp {
@@ -148,7 +157,7 @@ func main() {
148157
fmt.Printf("Message: %s\n", message)
149158
fmt.Println("===")
150159

151-
if err := makeOpenAIRequest(ctx, apiKey, model, message); err != nil {
160+
if err := makeOpenAIRequest(ctx, apiKey, model, message, tools); err != nil {
152161
fmt.Printf("Error: %v\n", err)
153162
os.Exit(1)
154163
}

pkg/aiusechat/anthropic/anthropic-backend.go

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -448,79 +448,6 @@ func RunAnthropicChatStep(
448448
return stopReason, rtnMessage, nil
449449
}
450450

451-
// returns (nil, err) before we start streaming
452-
// returns (stopReason, nil) after we start streaming
453-
func StreamAnthropicResponses(
454-
ctx context.Context,
455-
sse *sse.SSEHandlerCh,
456-
opts *uctypes.AIOptsType,
457-
messages []uctypes.UIMessage,
458-
tools []uctypes.ToolDefinition,
459-
cont *uctypes.WaveContinueResponse,
460-
) (*uctypes.WaveStopReason, error) {
461-
if sse == nil {
462-
return nil, errors.New("sse handler is nil")
463-
}
464-
// Context with timeout if provided.
465-
if opts.TimeoutMs > 0 {
466-
var cancel context.CancelFunc
467-
ctx, cancel = context.WithTimeout(ctx, time.Duration(opts.TimeoutMs)*time.Millisecond)
468-
defer cancel()
469-
}
470-
471-
// Convert UIMessages to anthropicInputMessages
472-
var anthropicMsgs []anthropicInputMessage
473-
for _, m := range messages {
474-
aim := anthropicInputMessage{Role: m.Role}
475-
blocks, err := convertPartsToAnthropicBlocks(m.Parts, m.Role)
476-
if err != nil {
477-
return nil, fmt.Errorf("invalid message parts: %w", err)
478-
}
479-
aim.Content = blocks
480-
anthropicMsgs = append(anthropicMsgs, aim)
481-
}
482-
483-
req, err := buildAnthropicHTTPRequest(ctx, anthropicMsgs, uctypes.WaveChatOpts{Config: *opts, Tools: tools})
484-
if err != nil {
485-
return nil, err
486-
}
487-
488-
httpClient := &http.Client{
489-
Timeout: 0, // rely on ctx; streaming can be long
490-
}
491-
// Proxy support
492-
if opts.ProxyURL != "" {
493-
pURL, perr := url.Parse(opts.ProxyURL)
494-
if perr != nil {
495-
return nil, fmt.Errorf("invalid proxy URL: %w", perr)
496-
}
497-
httpClient.Transport = &http.Transport{
498-
Proxy: http.ProxyURL(pURL),
499-
}
500-
}
501-
502-
resp, err := httpClient.Do(req)
503-
if err != nil {
504-
return nil, err
505-
}
506-
defer resp.Body.Close()
507-
508-
ct := resp.Header.Get("Content-Type")
509-
if resp.StatusCode != http.StatusOK || !strings.HasPrefix(ct, "text/event-stream") {
510-
return nil, parseAnthropicHTTPError(resp)
511-
}
512-
513-
// At this point we have a valid SSE stream, so setup SSE handling
514-
// From here on, errors must be returned through the SSE stream
515-
sse.SetupSSE()
516-
517-
// Use eventsource decoder for proper SSE parsing
518-
decoder := eventsource.NewDecoder(resp.Body)
519-
520-
stopReason, _ := handleAnthropicStreamingResp(ctx, sse, decoder, cont)
521-
return stopReason, nil
522-
}
523-
524451
// handleAnthropicStreamingResp processes the SSE stream after HTTP setup is complete
525452
func handleAnthropicStreamingResp(
526453
ctx context.Context,

pkg/aiusechat/anthropic/anthropic-convertmessage.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ import (
1818
"github.com/google/uuid"
1919
"github.com/wavetermdev/waveterm/pkg/aiusechat/uctypes"
2020
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
21-
"github.com/wavetermdev/waveterm/pkg/waveobj"
22-
"github.com/wavetermdev/waveterm/pkg/wstore"
2321
)
2422

2523
// these conversions are based off the anthropic spec
@@ -31,6 +29,9 @@ func buildAnthropicHTTPRequest(ctx context.Context, msgs []anthropicInputMessage
3129
if opts.Model == "" {
3230
return nil, errors.New("opts.model is required")
3331
}
32+
if chatOpts.ClientId == "" {
33+
return nil, errors.New("chatOpts.ClientId is required")
34+
}
3435

3536
// Set defaults
3637
endpoint := opts.BaseURL
@@ -135,18 +136,13 @@ func buildAnthropicHTTPRequest(ctx context.Context, msgs []anthropicInputMessage
135136
if err != nil {
136137
return nil, err
137138
}
138-
client, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
139-
if err != nil {
140-
return nil, fmt.Errorf("error getting client for Wave AI request: %w", err)
141-
}
142-
143139
req.Header.Set("content-type", "application/json")
144140
if opts.APIToken != "" {
145141
req.Header.Set("x-api-key", opts.APIToken)
146142
}
147143
req.Header.Set("anthropic-version", apiVersion)
148144
req.Header.Set("accept", "text/event-stream")
149-
req.Header.Set("X-Wave-ClientId", client.OID)
145+
req.Header.Set("X-Wave-ClientId", chatOpts.ClientId)
150146
req.Header.Set("X-Wave-APIType", "anthropic")
151147

152148
return req, nil

0 commit comments

Comments
 (0)