diff --git a/.github/chat.png b/.github/chat.png index 98890f4..b5af642 100644 Binary files a/.github/chat.png and b/.github/chat.png differ diff --git a/README.md b/README.md index 33c16e4..1733750 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ whiskr is a private, self-hosted web chat interface for interacting with AI mode - Tags indicate if a model supports **tools**, **vision**, or **reasoning** - Search field with fuzzy matching to quickly find models - Models are listed newest -> oldest +- Reasoning effort control ## TODO -- Reasoning effort control - Retry button for assistant messages - Import and export of chats - Web search tool diff --git a/chat.go b/chat.go index 6a3397b..5d4bc8c 100644 --- a/chat.go +++ b/chat.go @@ -16,10 +16,16 @@ type Message struct { Text string `json:"text"` } +type Reasoning struct { + Effort string `json:"effort"` + Tokens int `json:"tokens"` +} + type Request struct { Prompt string `json:"prompt"` Model string `json:"model"` Temperature float64 `json:"temperature"` + Reasoning Reasoning `json:"reasoning"` Messages []Message `json:"messages"` } @@ -39,6 +45,21 @@ func (r *Request) Parse() (*openrouter.ChatCompletionRequest, error) { request.Temperature = float32(r.Temperature) + if model.Reasoning { + request.Reasoning = &openrouter.ChatCompletionReasoning{} + + switch r.Reasoning.Effort { + case "high", "medium", "low": + request.Reasoning.Effort = &r.Reasoning.Effort + default: + if r.Reasoning.Tokens <= 0 || r.Reasoning.Tokens > 1024*1024 { + return nil, fmt.Errorf("invalid reasoning tokens (1-1048576): %d", r.Reasoning.Tokens) + } + + request.Reasoning.MaxTokens = &r.Reasoning.Tokens + } + } + prompt, err := BuildPrompt(r.Prompt, model) if err != nil { return nil, err @@ -61,12 +82,6 @@ func (r *Request) Parse() (*openrouter.ChatCompletionRequest, error) { }) } - h := "high" - - request.Reasoning = &openrouter.ChatCompletionReasoning{ - Effort: &h, - } - return &request, nil } diff --git a/models.go b/models.go index e3f6daf..3e759e2 100644 --- a/models.go +++ b/models.go @@ -13,6 +13,8 @@ type Model struct { Name string `json:"name"` Description string `json:"description"` Tags []string `json:"tags,omitempty"` + + Reasoning bool `json:"-"` } var ModelMap = make(map[string]*Model) @@ -38,11 +40,15 @@ func LoadModels() ([]*Model, error) { name = name[index+2:] } + tags, reasoning := GetModelTags(model) + m := &Model{ ID: model.ID, Name: name, Description: model.Description, - Tags: GetModelTags(model), + Tags: tags, + + Reasoning: reasoning, } models[index] = m @@ -53,10 +59,17 @@ func LoadModels() ([]*Model, error) { return models, nil } -func GetModelTags(model openrouter.Model) []string { - var tags []string +func GetModelTags(model openrouter.Model) ([]string, bool) { + var ( + reasoning bool + tags []string + ) for _, parameter := range model.SupportedParameters { + if parameter == "reasoning" { + reasoning = true + } + if parameter == "reasoning" || parameter == "tools" { tags = append(tags, parameter) } @@ -70,5 +83,5 @@ func GetModelTags(model openrouter.Model) []string { sort.Strings(tags) - return tags + return tags, reasoning } diff --git a/static/css/chat.css b/static/css/chat.css index e3185a2..b40afbd 100644 --- a/static/css/chat.css +++ b/static/css/chat.css @@ -87,6 +87,10 @@ body { pointer-events: none !important; } +.none { + display: none !important; +} + #messages { display: flex; flex-direction: column; @@ -432,9 +436,10 @@ select { padding: 2px 4px; } +#reasoning-tokens, #temperature { appearance: textfield; - width: 50px; + width: 48px; padding: 2px 4px; text-align: right; } @@ -455,6 +460,14 @@ label[for="temperature"] { background-image: url(icons/temperature.svg); } +label[for="reasoning-effort"] { + background-image: url(icons/reasoning.svg); +} + +label[for="reasoning-tokens"] { + background-image: url(icons/amount.svg); +} + #bottom { top: -38px; left: 50%; diff --git a/static/css/icons/amount.svg b/static/css/icons/amount.svg new file mode 100644 index 0000000..6713d4b --- /dev/null +++ b/static/css/icons/amount.svg @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/static/index.html b/static/index.html index 914ddab..bce0e4c 100644 --- a/static/index.html +++ b/static/index.html @@ -23,12 +23,12 @@ - - + +