package chat import ( "context" "errors" "fmt" "io" "strings" ) const ( chatRoleUser = "user" chatRoleAssistant = "assistant" ) type chatMessage struct { Role string Content string } type chatSession struct { client chatClient model string models []string messages []chatMessage } func newChatSession(ctx context.Context, client chatClient, requestedModel string) (*chatSession, error) { models, err := client.ListModels(ctx) if err != nil { return nil, fmt.Errorf("list models: %w", err) } model, err := resolveChatModel(requestedModel, models) if err != nil { return nil, err } return &chatSession{ client: client, model: model, models: models, }, nil } func (s *chatSession) CurrentModel() string { return s.model } func (s *chatSession) Models() []string { models := make([]string, len(s.models)) copy(models, s.models) return models } func (s *chatSession) Clear() { s.messages = nil } func (s *chatSession) SwitchModel(model string) error { if !modelExists(s.models, model) { return fmt.Errorf("model %q is not available. Use /models to see installed models", model) } s.model = model s.Clear() return nil } func (s *chatSession) Send(ctx context.Context, prompt string, out io.Writer) error { s.messages = append(s.messages, chatMessage{ Role: chatRoleUser, Content: prompt, }) answer, err := s.client.StreamChat(ctx, s.model, s.messages, out) if err != nil { return err } s.messages = append(s.messages, chatMessage{ Role: chatRoleAssistant, Content: answer, }) return nil } func resolveChatModel(requested string, models []string) (string, error) { switch { case requested == "" && len(models) == 0: return "", errors.New(`no chat models are installed. Install a model first, for example: local-ai models list local-ai models install local-ai run Then start a chat session: local-ai chat --model `) case requested == "" && len(models) == 1: return models[0], nil case requested == "" && len(models) > 1: var b strings.Builder b.WriteString("multiple models are available; choose one with --model:\n") b.WriteString(formatChatModelList(models, "")) return "", errors.New(b.String()) case !modelExists(models, requested): return "", fmt.Errorf("model %q is not available. Use `local-ai models list` and `local-ai models install `, or pass an installed model with --model", requested) default: return requested, nil } } func modelExists(models []string, name string) bool { for _, model := range models { if model == name { return true } } return false }