waveterm/pkg/aiusechat/tools_web.go
Mike Sawka d272a4ec03
New AIPanel (#2370)
Massive PR, over 13k LOC updated, 128 commits to implement the first pass at the new Wave AI panel.  Two backend adapters (OpenAI and Anthropic), layout changes to support the panel, keyboard shortcuts, and a huge focus/layout change to integrate the panel seamlessly into the UI.

Also fixes some small issues found during the Wave AI journey (zoom fixes, documentation, more scss removal, circular dependency issues, settings, etc)
2025-10-07 13:32:10 -07:00

113 lines
No EOL
2.8 KiB
Go

// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package aiusechat
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/wavetermdev/waveterm/pkg/aiusechat/uctypes"
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wcore"
"github.com/wavetermdev/waveterm/pkg/wstore"
)
type WebNavigateToolInput struct {
WidgetId string `json:"widget_id"`
Url string `json:"url"`
}
func parseWebNavigateInput(input any) (*WebNavigateToolInput, error) {
result := &WebNavigateToolInput{}
if input == nil {
return nil, fmt.Errorf("input is required")
}
inputBytes, err := json.Marshal(input)
if err != nil {
return nil, fmt.Errorf("failed to marshal input: %w", err)
}
if err := json.Unmarshal(inputBytes, result); err != nil {
return nil, fmt.Errorf("failed to unmarshal input: %w", err)
}
if result.WidgetId == "" {
return nil, fmt.Errorf("widget_id is required")
}
if result.Url == "" {
return nil, fmt.Errorf("url is required")
}
return result, nil
}
func GetWebNavigateToolDefinition(tabId string) uctypes.ToolDefinition {
return uctypes.ToolDefinition{
Name: "web_navigate",
DisplayName: "Navigate Web Widget",
Description: "Navigate a web browser widget to a new URL",
ToolLogName: "web:navigate",
Strict: true,
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"widget_id": map[string]any{
"type": "string",
"description": "8-character widget ID of the web browser widget",
},
"url": map[string]any{
"type": "string",
"description": "URL to navigate to",
},
},
"required": []string{"widget_id", "url"},
"additionalProperties": false,
},
ToolInputDesc: func(input any) string {
parsed, err := parseWebNavigateInput(input)
if err != nil {
return fmt.Sprintf("error parsing input: %v", err)
}
return fmt.Sprintf("navigating web widget %s to %q", parsed.WidgetId, parsed.Url)
},
ToolAnyCallback: func(input any) (any, error) {
parsed, err := parseWebNavigateInput(input)
if err != nil {
return nil, err
}
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()
tab, err := wstore.DBMustGet[*waveobj.Tab](ctx, tabId)
if err != nil {
return nil, fmt.Errorf("error getting tab: %w", err)
}
fullBlockId, err := resolveBlockIdFromPrefix(tab, parsed.WidgetId)
if err != nil {
return nil, err
}
blockORef := waveobj.MakeORef(waveobj.OType_Block, fullBlockId)
meta := map[string]any{
"url": parsed.Url,
}
err = wstore.UpdateObjectMeta(ctx, blockORef, meta, false)
if err != nil {
return nil, fmt.Errorf("failed to update web block URL: %w", err)
}
wcore.SendWaveObjUpdate(blockORef)
return true, nil
},
}
}