| 1 | package agent |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "strings" |
| 6 | |
| 7 | "reasonix/internal/provider" |
| 8 | ) |
| 9 | |
| 10 | // responseFormatContextKey carries a per-turn structured-output request |
| 11 | // through Run's context (same pattern as runStepLimitContextKey). The agent |
| 12 | // reads it when building the provider.Request so a turn can ask for |
| 13 | // json_object output without changing the agent's persistent state. |
| 14 | type responseFormatContextKey struct{} |
| 15 | |
| 16 | // WithResponseFormat attaches a structured-output format to a turn context. |
| 17 | // Empty format is a no-op returning ctx unchanged (byte-stable default path). |
| 18 | func WithResponseFormat(ctx context.Context, format string) context.Context { |
| 19 | if strings.TrimSpace(format) == "" { |
| 20 | return ctx |
| 21 | } |
| 22 | return context.WithValue(ctx, responseFormatContextKey{}, strings.TrimSpace(format)) |
| 23 | } |
| 24 | |
| 25 | // responseFormatFromContext returns the turn-scoped structured-output format |
| 26 | // (e.g. "json_object") or "" when none was requested. |
| 27 | func responseFormatFromContext(ctx context.Context) string { |
| 28 | if ctx == nil { |
| 29 | return "" |
| 30 | } |
| 31 | f, _ := ctx.Value(responseFormatContextKey{}).(string) |
| 32 | return f |
| 33 | } |
| 34 | |
| 35 | // ResponseFormatFromRequest is the exported form of responseFormatFromRequest: |
| 36 | // control tests assert the turn-bound format actually reaches the agent |
| 37 | // request path (review #7234 — format bound to turn, not global slot). |
| 38 | func ResponseFormatFromRequest(ctx context.Context) *provider.ResponseFormat { |
| 39 | return responseFormatFromRequest(ctx) |
| 40 | } |
| 41 | |
| 42 | // responseFormatFromRequest returns the turn-scoped structured-output format |
| 43 | // (e.g. "json_object") as a Request field, or nil when none was requested |
| 44 | // (nil keeps the wire byte-stable for prompt caching). |
| 45 | func responseFormatFromRequest(ctx context.Context) *provider.ResponseFormat { |
| 46 | if f := responseFormatFromContext(ctx); f != "" { |
| 47 | return &provider.ResponseFormat{Type: f} |
| 48 | } |
| 49 | return nil |
| 50 | } |
| 51 |