Files
httprunner/uixt/sdk.go
2025-05-26 08:49:06 +08:00

114 lines
3.1 KiB
Go

package uixt
import (
"context"
"fmt"
"github.com/httprunner/httprunner/v5/uixt/ai"
"github.com/httprunner/httprunner/v5/uixt/option"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/mcp"
"github.com/rs/zerolog/log"
)
func NewXTDriver(driver IDriver, opts ...option.AIServiceOption) (*XTDriver, error) {
driverExt := &XTDriver{
IDriver: driver,
Client: &MCPClient4XTDriver{
Server: NewMCPServer(),
},
}
services := option.NewAIServiceOptions(opts...)
var err error
if services.CVService != "" {
driverExt.CVService, err = ai.NewCVService(services.CVService)
if err != nil {
log.Error().Err(err).Msg("init vedem image service failed")
return nil, err
}
}
if services.LLMService != "" {
driverExt.LLMService, err = ai.NewLLMService(services.LLMService)
if err != nil {
log.Error().Err(err).Msg("init llm service failed")
return nil, err
}
}
return driverExt, nil
}
// XTDriver = IDriver + AI
type XTDriver struct {
IDriver
CVService ai.ICVService // OCR/CV
LLMService ai.ILLMService // LLM
Client *MCPClient4XTDriver // MCP Client
}
// MCPClient4XTDriver is a mock MCP client that only implements the methods used by the host
type MCPClient4XTDriver struct {
client.MCPClient
Server *MCPServer4XTDriver
}
func (c *MCPClient4XTDriver) ListTools(ctx context.Context, req mcp.ListToolsRequest) (*mcp.ListToolsResult, error) {
tools := c.Server.ListTools()
return &mcp.ListToolsResult{Tools: tools}, nil
}
func (c *MCPClient4XTDriver) CallTool(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
actionTool := c.Server.GetToolByAction(option.ActionMethod(req.Params.Name))
if actionTool == nil {
return mcp.NewToolResultError(fmt.Sprintf("action %s for tool not found", req.Params.Name)), nil
}
handler := actionTool.Implement()
return handler(ctx, req)
}
func (c *MCPClient4XTDriver) Initialize(ctx context.Context, req mcp.InitializeRequest) (*mcp.InitializeResult, error) {
// no need to initialize for local server
return &mcp.InitializeResult{}, nil
}
func (c *MCPClient4XTDriver) Close() error {
// no need to close for local server
return nil
}
func (dExt *XTDriver) ExecuteAction(action MobileAction) (err error) {
// Find the corresponding tool for this action method
tool := dExt.Client.Server.GetToolByAction(action.Method)
if tool == nil {
return fmt.Errorf("no tool found for action method: %s", action.Method)
}
// Use the tool's own conversion method
req, err := tool.ConvertActionToCallToolRequest(action)
if err != nil {
return fmt.Errorf("failed to convert action to MCP tool call: %w", err)
}
// Execute via MCP tool
result, err := dExt.Client.CallTool(context.Background(), req)
if err != nil {
return fmt.Errorf("MCP tool call failed: %w", err)
}
// Check if the tool execution had business logic errors
if result.IsError {
if len(result.Content) > 0 {
return fmt.Errorf("tool execution failed: %s", result.Content[0])
}
return fmt.Errorf("tool execution failed")
}
log.Debug().Str("method", string(action.Method)).
Str("tool", string(tool.Name())).
Msg("executed action via MCP tool")
return nil
}