MCPcopy
hub / github.com/docker/docker-agent / Generate

Method Generate

pkg/sessiontitle/generator.go:65–132  ·  view source on GitHub ↗

Generate produces a title for a session based on the provided user messages. It performs one-shot LLM calls directly via the provider's CreateChatCompletionStream, avoiding the overhead of spinning up a nested runtime, and falls back to the next model on failure. Returns an empty string if no models

(ctx context.Context, sessionID string, userMessages []string)

Source from the content-addressed store, hash-verified

63// runtime, and falls back to the next model on failure.
64// Returns an empty string if no models or messages are configured.
65func (g *Generator) Generate(ctx context.Context, sessionID string, userMessages []string) (title string, err error) {
66 if g == nil || len(g.models) == 0 || len(userMessages) == 0 {
67 return "", nil
68 }
69
70 // Title generation runs outside the run loop, so the session ID
71 // is not yet on ctx. Stamp it here so the gateway-bound LLM calls
72 // below carry `X-Cagent-Session-Id` and remain attributable to
73 // the originating session.
74 ctx = httpclient.ContextWithSessionID(ctx, sessionID)
75
76 // Wrap the whole title-generation in a span so the boundary is
77 // visible on the session timeline. The inner per-attempt LLM
78 // calls each get their own `chat {model}` CLIENT child span via
79 // the provider decorator.
80 ctx, span := otel.Tracer("github.com/docker/docker-agent/pkg/sessiontitle").Start(
81 ctx,
82 "sessiontitle.generate",
83 trace.WithSpanKind(trace.SpanKindInternal),
84 trace.WithAttributes(
85 attribute.String(genai.AttrConversationID, sessionID),
86 attribute.Int("cagent.sessiontitle.candidate_count", len(g.models)),
87 ),
88 )
89 defer func() {
90 if err != nil {
91 span.RecordError(err)
92 span.SetStatus(codes.Error, err.Error())
93 }
94 span.End()
95 }()
96
97 // Apply timeout to prevent hanging on slow or unresponsive models.
98 ctx, cancel := context.WithTimeout(ctx, titleGenerationTimeout)
99 defer cancel()
100
101 slog.DebugContext(ctx, "Generating title for session", "session_id", sessionID, "message_count", len(userMessages))
102
103 messages := buildPrompt(userMessages)
104
105 var errs []error
106 for idx, baseModel := range g.models {
107 // Assign to the named-return `err` so a context cancellation
108 // is observed by the deferred span closure as a recorded
109 // error rather than silently slipping through.
110 if err = ctx.Err(); err != nil { //nolint:gocritic // assigns to named return `err` for deferred span observability
111 return "", err
112 }
113
114 title, err := generateOnce(ctx, baseModel, messages)
115 if err == nil {
116 slog.DebugContext(ctx, "Generated session title", "session_id", sessionID, "title", title, "model", baseModel.ID())
117 return title, nil
118 }
119
120 errs = append(errs, err)
121 // Per-attempt failures are logged at Debug because we still have
122 // fallbacks; the final error joins every attempt so callers see

Calls 10

ContextWithSessionIDFunction · 0.92
buildPromptFunction · 0.85
generateOnceFunction · 0.85
StartMethod · 0.65
RecordErrorMethod · 0.65
ErrMethod · 0.65
IDMethod · 0.65
StringMethod · 0.45
ErrorMethod · 0.45
EndMethod · 0.45