mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-25 12:22:44 +00:00
170 lines
4.9 KiB
Go
170 lines
4.9 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/store"
|
|
)
|
|
|
|
type traceDataForCLI = store.TraceData
|
|
type spanDataForCLI = store.SpanData
|
|
type timelineItemForCLI = store.RunTimelineItem
|
|
|
|
type traceListOptions struct {
|
|
Query string
|
|
AgentID string
|
|
UserID string
|
|
SessionKey string
|
|
Status string
|
|
Channel string
|
|
AgentQuery string
|
|
ChannelQuery string
|
|
ToolName string
|
|
From string
|
|
To string
|
|
Since string
|
|
Until string
|
|
HasToolCalls string
|
|
MinInputTokens int
|
|
MaxInputTokens int
|
|
MinOutputTokens int
|
|
MaxOutputTokens int
|
|
MinToolCalls int
|
|
MaxToolCalls int
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
type traceFollowOptions struct {
|
|
AgentID string
|
|
SessionKey string
|
|
Status string
|
|
Channel string
|
|
UserID string
|
|
Since string
|
|
Limit int
|
|
IncludeSpans bool
|
|
}
|
|
|
|
type traceTimelineOptions struct {
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
type traceListResponse struct {
|
|
Traces []traceDataForCLI `json:"traces"`
|
|
Total int `json:"total"`
|
|
Limit int `json:"limit"`
|
|
Offset int `json:"offset"`
|
|
}
|
|
|
|
type traceDetailResponse struct {
|
|
Trace traceDataForCLI `json:"trace"`
|
|
Spans []spanDataForCLI `json:"spans"`
|
|
}
|
|
|
|
type traceFollowResponse struct {
|
|
Traces []traceDataForCLI `json:"traces"`
|
|
SpansByTraceID map[string][]spanDataForCLI `json:"spans_by_trace_id"`
|
|
ServerTime string `json:"server_time"`
|
|
NextSince string `json:"next_since"`
|
|
Limit int `json:"limit"`
|
|
}
|
|
|
|
type traceTimelineResponse struct {
|
|
RunID string `json:"run_id"`
|
|
SessionKey string `json:"session_key"`
|
|
Items []timelineItemForCLI `json:"items"`
|
|
Limit int `json:"limit"`
|
|
Offset int `json:"offset"`
|
|
}
|
|
|
|
func buildTraceListPath(opts traceListOptions) string {
|
|
values := url.Values{}
|
|
addQuery(values, "q", opts.Query)
|
|
addQuery(values, "agent_id", opts.AgentID)
|
|
addQuery(values, "user_id", opts.UserID)
|
|
addQuery(values, "session_key", opts.SessionKey)
|
|
addQuery(values, "status", opts.Status)
|
|
addQuery(values, "channel", opts.Channel)
|
|
addQuery(values, "agent", opts.AgentQuery)
|
|
addQuery(values, "channel_query", opts.ChannelQuery)
|
|
addQuery(values, "tool_name", opts.ToolName)
|
|
addQuery(values, "from", firstNonEmpty(opts.From, opts.Since))
|
|
addQuery(values, "to", firstNonEmpty(opts.To, opts.Until))
|
|
addQuery(values, "has_tool_calls", opts.HasToolCalls)
|
|
addIntQuery(values, "min_input_tokens", opts.MinInputTokens)
|
|
addIntQuery(values, "max_input_tokens", opts.MaxInputTokens)
|
|
addIntQuery(values, "min_output_tokens", opts.MinOutputTokens)
|
|
addIntQuery(values, "max_output_tokens", opts.MaxOutputTokens)
|
|
addIntQuery(values, "min_tool_calls", opts.MinToolCalls)
|
|
addIntQuery(values, "max_tool_calls", opts.MaxToolCalls)
|
|
addIntQuery(values, "limit", opts.Limit)
|
|
addIntQuery(values, "offset", opts.Offset)
|
|
return pathWithQuery("/v1/traces", values)
|
|
}
|
|
|
|
func buildTraceFollowPath(opts traceFollowOptions) (string, error) {
|
|
if strings.TrimSpace(opts.SessionKey) == "" && strings.TrimSpace(opts.AgentID) == "" {
|
|
return "", fmt.Errorf("traces follow requires --session or --agent-id")
|
|
}
|
|
values := url.Values{}
|
|
addQuery(values, "agent_id", opts.AgentID)
|
|
addQuery(values, "session_key", opts.SessionKey)
|
|
addQuery(values, "status", opts.Status)
|
|
addQuery(values, "channel", opts.Channel)
|
|
addQuery(values, "user_id", opts.UserID)
|
|
addQuery(values, "since", opts.Since)
|
|
addIntQuery(values, "limit", opts.Limit)
|
|
if opts.IncludeSpans {
|
|
values.Set("include_spans", "true")
|
|
}
|
|
return pathWithQuery("/v1/traces/follow", values), nil
|
|
}
|
|
|
|
func buildTraceTimelinePath(runID, sessionKey string, opts traceTimelineOptions) string {
|
|
values := url.Values{}
|
|
addQuery(values, "session_key", sessionKey)
|
|
addIntQuery(values, "limit", opts.Limit)
|
|
addIntQuery(values, "offset", opts.Offset)
|
|
return pathWithQuery("/v1/runs/"+url.PathEscape(runID)+"/timeline", values)
|
|
}
|
|
|
|
func traceDetailPath(traceID string) string {
|
|
return "/v1/traces/" + url.PathEscape(traceID)
|
|
}
|
|
|
|
func traceExportPath(traceID string) string {
|
|
return "/v1/traces/" + url.PathEscape(traceID) + "/export"
|
|
}
|
|
|
|
func traceRunIDFromDetail(detail traceDetailResponse) (string, error) {
|
|
runID := strings.TrimSpace(detail.Trace.RunID)
|
|
if runID == "" {
|
|
return "", fmt.Errorf("trace has no run_id; timeline is unavailable")
|
|
}
|
|
return runID, nil
|
|
}
|
|
|
|
func addQuery(values url.Values, key, value string) {
|
|
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
|
values.Set(key, trimmed)
|
|
}
|
|
}
|
|
|
|
func addIntQuery(values url.Values, key string, value int) {
|
|
if value > 0 {
|
|
values.Set(key, strconv.Itoa(value))
|
|
}
|
|
}
|
|
|
|
func pathWithQuery(path string, values url.Values) string {
|
|
if len(values) == 0 {
|
|
return path
|
|
}
|
|
return path + "?" + values.Encode()
|
|
}
|