blob: 0b2da4e3770252c9fb4bc7a75dd2f632bda593cb [file]
package data
import (
"bytes"
"context"
"encoding/json"
"net/http"
"time"
"github.com/mark3labs/mcp-go/mcp"
"go.skia.org/infra/mcp/common"
"go.skia.org/infra/perf/go/types"
)
// toolProvider is a struct for providing tool handlers.
type toolProvider struct {
perfURL string
httpClient *http.Client
}
const (
GetPerfDataDescription = `Retrieves the performance data for the traces (also referred to as performance tests)
specified by the query given a begin and end time. The query consists of multiple key value pairs, where the key
is the name of the parameter and the value is a list of parameter values. For example "test":["test1", "test2"].
The begin and end times are provided to narrow the time frame in which to search for the raw data. The response
data is provided in JSON representation of the GetTraceDataResponse struct. This struct contains the following information.
- commitNumbers: This is an array of commit numbers uniquely identifying the commit.
- commitTimestamps: This is an array of commit timestamps for the corresponding commit numbers.
- traceset: This is a map where the key is a unique identifier for a trace and the value is an array of floating point numbers.
The way to interpret this data is as follows:
- Each element in the value array inside a traceset specifies the value of that trace for tests run on the corresponding commit.
inside the header.
- All the arrays are created in such a way that we can use index positions to determine the relation between the data point and the commit for that data point.
By default, users generally will not want to view the commit details other than the commit number in the response. A good way of representing
the data is a table containing the timestamp, commit number and the value for each trace in the response. Do not display the raw response json to the user.
`
GetPerfParamsDescription = `Retrieves the available parameters that can be used to query for performance data.
The response is a JSON object where keys are parameter names and values are lists of all possible values for that parameter.
The keys and values supplied here are to be used to form the query string for the GetPerfData tool. The parameters returned by this tool
are the only valid combinations of keys and values available to query the data on.`
)
// GetTraceDataResponse is a struct containing the Perf data to send back to the tool caller.
type GetTraceDataResponse struct {
TraceSet types.TraceSet `json:"traceset"`
CommitNumbers []types.CommitNumber `json:"commitNumbers"`
CommitTimestamps []int64 `json:"commitTimestamps"`
}
// GetTools returns the tools for the perf data domain.
func GetTools(perfUrl string, httpClient *http.Client) []common.Tool {
toolProvider := &toolProvider{
perfURL: perfUrl,
httpClient: httpClient,
}
return []common.Tool{
{
Name: "GetPerfData",
Description: GetPerfDataDescription,
Arguments: []common.ToolArgument{
{
Name: "begin",
ArgumentType: common.StringArgument,
Required: true,
Description: "Timestamp string that defines the start or begin time to query for the performance data. Use RFC3339 format.",
},
{
Name: "end",
ArgumentType: common.StringArgument,
Required: true,
Description: "Timestamp string that defines the end time to query for the performance data. Use RFC3339 format.",
},
{
Name: "query",
ArgumentType: common.StringArgument,
Required: true,
Description: "Query string which is a url escaped string defining parameter name and parameter values to query for the performance data",
},
},
Handler: toolProvider.GetPerfDataHandler,
},
{
Name: "GetPerfParams",
Description: GetPerfParamsDescription,
Handler: toolProvider.GetPerfParamsHandler,
},
}
}
// GetPerfParamsHandler is the handler for returning the paramsets for the configured perf instance.
func (t *toolProvider) GetPerfParamsHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client := NewPerfClient(t.httpClient, t.perfURL)
resp, err := client.GetParamSet(ctx)
if err != nil {
return mcp.NewToolResultError(err.Error()), err
}
var b bytes.Buffer
err = json.NewEncoder(&b).Encode(resp)
if err != nil {
return mcp.NewToolResultError(err.Error()), err
}
return mcp.NewToolResultText(b.String()), nil
}
func (t *toolProvider) GetPerfDataHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
begin, err := request.RequireString("begin")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
beginTime, err := time.ParseInLocation(time.RFC3339, begin, time.UTC)
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
end, err := request.RequireString("end")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
endTime, err := time.ParseInLocation(time.RFC3339, end, time.UTC)
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
query, err := request.RequireString("query")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
perfReq := GetTraceDataRequest{
Begin: beginTime,
End: endTime,
Query: query,
}
client := NewPerfClient(t.httpClient, t.perfURL)
resp, err := client.GetTraceData(ctx, perfReq)
if err != nil {
return mcp.NewToolResultError(err.Error()), err
}
responseData := GetTraceDataResponse{
TraceSet: resp.TraceSet,
}
for _, h := range resp.Header {
responseData.CommitNumbers = append(responseData.CommitNumbers, h.Offset)
responseData.CommitTimestamps = append(responseData.CommitTimestamps, int64(h.Timestamp))
}
var b bytes.Buffer
err = json.NewEncoder(&b).Encode(responseData)
if err != nil {
return mcp.NewToolResultError(err.Error()), err
}
return mcp.NewToolResultText(b.String()), nil
}