package chat import ( "fmt" "strings" "sync" ) // Renderer renders messages into a single prompt string. // //nolint:revive // exported API type Renderer interface { Render(messages []Message, opts Options) (string, error) } var ( mu sync.RWMutex renderers = map[string]Renderer{} ) // Register registers a renderer for a model architecture (e.g. "qwen3", "llama"). func Register(architecture string, r Renderer) { mu.Lock() defer mu.Unlock() renderers[strings.ToLower(architecture)] = r } // RendererForArchitecture returns the renderer for the given architecture. func RendererForArchitecture(architecture string) (Renderer, bool) { mu.RLock() defer mu.RUnlock() r, ok := renderers[strings.ToLower(architecture)] return r, ok } // RenderForArchitecture renders chat messages for the given architecture. func RenderForArchitecture(architecture string, messages []Message, opts Options) (string, error) { if r, ok := RendererForArchitecture(architecture); ok { return r.Render(messages, opts) } return (&Qwen3Renderer{}).Render(messages, opts) } // MustRenderForArchitecture is a convenience helper. func MustRenderForArchitecture(architecture string, messages []Message, opts Options) string { out, err := RenderForArchitecture(architecture, messages, opts) if err != nil { panic(fmt.Errorf("render chat template: %w", err)) } return out }