From eb44b1e4b244e5a93e7d465b14df39819d8dfaba Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Fri, 16 Jan 2026 16:53:19 +0300 Subject: Feat: impl attempt --- models/models.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index 912f72b..88ba144 100644 --- a/models/models.go +++ b/models/models.go @@ -93,6 +93,7 @@ type RoleMsg struct { Content string `json:"-"` ContentParts []interface{} `json:"-"` ToolCallID string `json:"tool_call_id,omitempty"` // For tool response messages + KnownTo []string `json:"known_to,omitempty"` hasContentParts bool // Flag to indicate which content type to marshal } @@ -104,10 +105,12 @@ func (m RoleMsg) MarshalJSON() ([]byte, error) { Role string `json:"role"` Content []interface{} `json:"content"` ToolCallID string `json:"tool_call_id,omitempty"` + KnownTo []string `json:"known_to,omitempty"` }{ Role: m.Role, Content: m.ContentParts, ToolCallID: m.ToolCallID, + KnownTo: m.KnownTo, } return json.Marshal(aux) } else { @@ -116,10 +119,12 @@ func (m RoleMsg) MarshalJSON() ([]byte, error) { Role string `json:"role"` Content string `json:"content"` ToolCallID string `json:"tool_call_id,omitempty"` + KnownTo []string `json:"known_to,omitempty"` }{ Role: m.Role, Content: m.Content, ToolCallID: m.ToolCallID, + KnownTo: m.KnownTo, } return json.Marshal(aux) } @@ -132,11 +137,13 @@ func (m *RoleMsg) UnmarshalJSON(data []byte) error { Role string `json:"role"` Content []interface{} `json:"content"` ToolCallID string `json:"tool_call_id,omitempty"` + KnownTo []string `json:"known_to,omitempty"` } if err := json.Unmarshal(data, &structured); err == nil && len(structured.Content) > 0 { m.Role = structured.Role m.ContentParts = structured.Content m.ToolCallID = structured.ToolCallID + m.KnownTo = structured.KnownTo m.hasContentParts = true return nil } @@ -146,6 +153,7 @@ func (m *RoleMsg) UnmarshalJSON(data []byte) error { Role string `json:"role"` Content string `json:"content"` ToolCallID string `json:"tool_call_id,omitempty"` + KnownTo []string `json:"known_to,omitempty"` } if err := json.Unmarshal(data, &simple); err != nil { return err @@ -153,6 +161,7 @@ func (m *RoleMsg) UnmarshalJSON(data []byte) error { m.Role = simple.Role m.Content = simple.Content m.ToolCallID = simple.ToolCallID + m.KnownTo = simple.KnownTo m.hasContentParts = false return nil } @@ -363,7 +372,8 @@ func (cb *ChatBody) MakeStopSlice() []string { for _, m := range cb.Messages { namesMap[m.Role] = struct{}{} } - ss := []string{"<|im_end|>"} + ss := make([]string, 0, 1+len(namesMap)) + ss = append(ss, "<|im_end|>") for k := range namesMap { ss = append(ss, k+":\n") } @@ -523,7 +533,7 @@ type LCPModels struct { } func (lcp *LCPModels) ListModels() []string { - resp := []string{} + resp := make([]string, 0, len(lcp.Data)) for _, model := range lcp.Data { resp = append(resp, model.ID) } -- cgit v1.2.3 From 0fb59210045792433a7a3796046c8383f2bb8824 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sat, 17 Jan 2026 12:44:18 +0300 Subject: Fix: copy with knownto --- models/models.go | 1 + 1 file changed, 1 insertion(+) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index 88ba144..69bdf02 100644 --- a/models/models.go +++ b/models/models.go @@ -267,6 +267,7 @@ func (m RoleMsg) Copy() RoleMsg { Content: m.Content, ContentParts: m.ContentParts, ToolCallID: m.ToolCallID, + KnownTo: m.KnownTo, hasContentParts: m.hasContentParts, } } -- cgit v1.2.3 From 3a11210f52a850f84771e1642cafcc3027b85075 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sat, 31 Jan 2026 12:57:53 +0300 Subject: Enha: avoid recursion in llm calls --- models/models.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index 69bdf02..76ef183 100644 --- a/models/models.go +++ b/models/models.go @@ -116,9 +116,9 @@ func (m RoleMsg) MarshalJSON() ([]byte, error) { } else { // Use simple content format aux := struct { - Role string `json:"role"` - Content string `json:"content"` - ToolCallID string `json:"tool_call_id,omitempty"` + Role string `json:"role"` + Content string `json:"content"` + ToolCallID string `json:"tool_call_id,omitempty"` KnownTo []string `json:"known_to,omitempty"` }{ Role: m.Role, @@ -150,9 +150,9 @@ func (m *RoleMsg) UnmarshalJSON(data []byte) error { // Otherwise, unmarshal as simple content format var simple struct { - Role string `json:"role"` - Content string `json:"content"` - ToolCallID string `json:"tool_call_id,omitempty"` + Role string `json:"role"` + Content string `json:"content"` + ToolCallID string `json:"tool_call_id,omitempty"` KnownTo []string `json:"known_to,omitempty"` } if err := json.Unmarshal(data, &simple); err != nil { @@ -540,3 +540,10 @@ func (lcp *LCPModels) ListModels() []string { } return resp } + +type ChatRoundReq struct { + UserMsg string + Role string + Regen bool + Resume bool +} -- cgit v1.2.3 From 76f14ce4a376bbbb99c79cc2090c067b5ba28484 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Tue, 3 Feb 2026 16:56:31 +0300 Subject: Enha: detailed error --- models/models.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index 76ef183..340cb42 100644 --- a/models/models.go +++ b/models/models.go @@ -369,14 +369,22 @@ func (cb *ChatBody) ListRoles() []string { } func (cb *ChatBody) MakeStopSlice() []string { - namesMap := make(map[string]struct{}) - for _, m := range cb.Messages { - namesMap[m.Role] = struct{}{} - } - ss := make([]string, 0, 1+len(namesMap)) - ss = append(ss, "<|im_end|>") - for k := range namesMap { - ss = append(ss, k+":\n") + return cb.MakeStopSliceExcluding("", cb.ListRoles()) +} + +func (cb *ChatBody) MakeStopSliceExcluding( + excludeRole string, roleList []string, +) []string { + ss := []string{} + for _, role := range roleList { + // Skip the excluded role (typically the current speaker) + if role == excludeRole { + continue + } + // Add multiple variations to catch different formatting + ss = append(ss, role+":\n") // Most common: role with newline + ss = append(ss, role+":") // Role with colon but no newline + ss = append(ss, role+": ") // Role with colon and space } return ss } -- cgit v1.2.3 From 7187df509fe9cc506695a1036b840e03eeb25cff Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Wed, 4 Feb 2026 12:47:54 +0300 Subject: Enha: stricter stop string --- models/models.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index 340cb42..e99832a 100644 --- a/models/models.go +++ b/models/models.go @@ -382,9 +382,12 @@ func (cb *ChatBody) MakeStopSliceExcluding( continue } // Add multiple variations to catch different formatting - ss = append(ss, role+":\n") // Most common: role with newline - ss = append(ss, role+":") // Role with colon but no newline - ss = append(ss, role+": ") // Role with colon and space + ss = append(ss, role+":\n") // Most common: role with newline + ss = append(ss, role+":") // Role with colon but no newline + ss = append(ss, role+": ") // Role with colon and single space + ss = append(ss, role+": ") // Role with colon and double space (common tokenization) + ss = append(ss, role+": \n") // Role with colon and double space (common tokenization) + ss = append(ss, role+": ") // Role with colon and triple space } return ss } -- cgit v1.2.3 From 478a505869bf26b15dcbc77feb2c09c1f2ff4aac Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Fri, 6 Feb 2026 11:32:06 +0300 Subject: Enha: client stop string for completion only --- models/models.go | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index e99832a..4133a7c 100644 --- a/models/models.go +++ b/models/models.go @@ -558,3 +558,10 @@ type ChatRoundReq struct { Regen bool Resume bool } + +type APIType int + +const ( + APITypeChat APIType = iota + APITypeCompletion +) -- cgit v1.2.3 From 4af866079c3f21eab12b02c3158567539ca40c50 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Fri, 6 Feb 2026 12:42:06 +0300 Subject: Chore: linter complaints --- models/models.go | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index 4133a7c..34e3dcf 100644 --- a/models/models.go +++ b/models/models.go @@ -98,7 +98,7 @@ type RoleMsg struct { } // MarshalJSON implements custom JSON marshaling for RoleMsg -func (m RoleMsg) MarshalJSON() ([]byte, error) { +func (m *RoleMsg) MarshalJSON() ([]byte, error) { if m.hasContentParts { // Use structured content format aux := struct { @@ -166,11 +166,11 @@ func (m *RoleMsg) UnmarshalJSON(data []byte) error { return nil } -func (m RoleMsg) ToText(i int) string { +func (m *RoleMsg) ToText(i int) string { icon := fmt.Sprintf("(%d)", i) // Convert content to string representation - contentStr := "" + var contentStr string if !m.hasContentParts { contentStr = m.Content } else { @@ -198,8 +198,8 @@ func (m RoleMsg) ToText(i int) string { return strings.ReplaceAll(textMsg, "\n\n", "\n") } -func (m RoleMsg) ToPrompt() string { - contentStr := "" +func (m *RoleMsg) ToPrompt() string { + var contentStr string if !m.hasContentParts { contentStr = m.Content } else { @@ -240,7 +240,7 @@ func NewMultimodalMsg(role string, contentParts []interface{}) RoleMsg { } // HasContent returns true if the message has either string content or structured content parts -func (m RoleMsg) HasContent() bool { +func (m *RoleMsg) HasContent() bool { if m.Content != "" { return true } @@ -251,17 +251,17 @@ func (m RoleMsg) HasContent() bool { } // IsContentParts returns true if the message uses structured content parts -func (m RoleMsg) IsContentParts() bool { +func (m *RoleMsg) IsContentParts() bool { return m.hasContentParts } // GetContentParts returns the content parts of the message -func (m RoleMsg) GetContentParts() []interface{} { +func (m *RoleMsg) GetContentParts() []interface{} { return m.ContentParts } // Copy creates a copy of the RoleMsg with all fields -func (m RoleMsg) Copy() RoleMsg { +func (m *RoleMsg) Copy() RoleMsg { return RoleMsg{ Role: m.Role, Content: m.Content, @@ -382,12 +382,14 @@ func (cb *ChatBody) MakeStopSliceExcluding( continue } // Add multiple variations to catch different formatting - ss = append(ss, role+":\n") // Most common: role with newline - ss = append(ss, role+":") // Role with colon but no newline - ss = append(ss, role+": ") // Role with colon and single space - ss = append(ss, role+": ") // Role with colon and double space (common tokenization) - ss = append(ss, role+": \n") // Role with colon and double space (common tokenization) - ss = append(ss, role+": ") // Role with colon and triple space + ss = append(ss, + role+":\n", // Most common: role with newline + role+":", // Role with colon but no newline + role+": ", // Role with colon and single space + role+": ", // Role with colon and double space (common tokenization) + role+": \n", // Role with colon and double space (common tokenization) + role+": ", // Role with colon and triple space + ) } return ss } -- cgit v1.2.3 From c04e120ddbec870348b0340e0fbb41556812c3f5 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Mon, 9 Feb 2026 10:39:27 +0300 Subject: Chore: interface{} -> any --- models/models.go | 62 +++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index 34e3dcf..36ec88f 100644 --- a/models/models.go +++ b/models/models.go @@ -89,12 +89,12 @@ type ImageContentPart struct { // RoleMsg represents a message with content that can be either a simple string or structured content parts type RoleMsg struct { - Role string `json:"role"` - Content string `json:"-"` - ContentParts []interface{} `json:"-"` - ToolCallID string `json:"tool_call_id,omitempty"` // For tool response messages - KnownTo []string `json:"known_to,omitempty"` - hasContentParts bool // Flag to indicate which content type to marshal + Role string `json:"role"` + Content string `json:"-"` + ContentParts []any `json:"-"` + ToolCallID string `json:"tool_call_id,omitempty"` // For tool response messages + KnownTo []string `json:"known_to,omitempty"` + hasContentParts bool // Flag to indicate which content type to marshal } // MarshalJSON implements custom JSON marshaling for RoleMsg @@ -102,10 +102,10 @@ func (m *RoleMsg) MarshalJSON() ([]byte, error) { if m.hasContentParts { // Use structured content format aux := struct { - Role string `json:"role"` - Content []interface{} `json:"content"` - ToolCallID string `json:"tool_call_id,omitempty"` - KnownTo []string `json:"known_to,omitempty"` + Role string `json:"role"` + Content []any `json:"content"` + ToolCallID string `json:"tool_call_id,omitempty"` + KnownTo []string `json:"known_to,omitempty"` }{ Role: m.Role, Content: m.ContentParts, @@ -134,10 +134,10 @@ func (m *RoleMsg) MarshalJSON() ([]byte, error) { func (m *RoleMsg) UnmarshalJSON(data []byte) error { // First, try to unmarshal as structured content format var structured struct { - Role string `json:"role"` - Content []interface{} `json:"content"` - ToolCallID string `json:"tool_call_id,omitempty"` - KnownTo []string `json:"known_to,omitempty"` + Role string `json:"role"` + Content []any `json:"content"` + ToolCallID string `json:"tool_call_id,omitempty"` + KnownTo []string `json:"known_to,omitempty"` } if err := json.Unmarshal(data, &structured); err == nil && len(structured.Content) > 0 { m.Role = structured.Role @@ -168,7 +168,6 @@ func (m *RoleMsg) UnmarshalJSON(data []byte) error { func (m *RoleMsg) ToText(i int) string { icon := fmt.Sprintf("(%d)", i) - // Convert content to string representation var contentStr string if !m.hasContentParts { @@ -177,7 +176,7 @@ func (m *RoleMsg) ToText(i int) string { // For structured content, just take the text parts var textParts []string for _, part := range m.ContentParts { - if partMap, ok := part.(map[string]interface{}); ok { + if partMap, ok := part.(map[string]any); ok { if partType, exists := partMap["type"]; exists && partType == "text" { if textVal, textExists := partMap["text"]; textExists { if textStr, isStr := textVal.(string); isStr { @@ -189,7 +188,6 @@ func (m *RoleMsg) ToText(i int) string { } contentStr = strings.Join(textParts, " ") + " " } - // check if already has role annotation (/completion makes them) if !strings.HasPrefix(contentStr, m.Role+":") { icon = fmt.Sprintf("(%d) <%s>: ", i, m.Role) @@ -206,7 +204,7 @@ func (m *RoleMsg) ToPrompt() string { // For structured content, just take the text parts var textParts []string for _, part := range m.ContentParts { - if partMap, ok := part.(map[string]interface{}); ok { + if partMap, ok := part.(map[string]any); ok { if partType, exists := partMap["type"]; exists && partType == "text" { if textVal, textExists := partMap["text"]; textExists { if textStr, isStr := textVal.(string); isStr { @@ -231,7 +229,7 @@ func NewRoleMsg(role, content string) RoleMsg { } // NewMultimodalMsg creates a RoleMsg with structured content parts (text and images) -func NewMultimodalMsg(role string, contentParts []interface{}) RoleMsg { +func NewMultimodalMsg(role string, contentParts []any) RoleMsg { return RoleMsg{ Role: role, ContentParts: contentParts, @@ -256,7 +254,7 @@ func (m *RoleMsg) IsContentParts() bool { } // GetContentParts returns the content parts of the message -func (m *RoleMsg) GetContentParts() []interface{} { +func (m *RoleMsg) GetContentParts() []any { return m.ContentParts } @@ -277,9 +275,9 @@ func (m *RoleMsg) AddTextPart(text string) { if !m.hasContentParts { // Convert to content parts format if m.Content != "" { - m.ContentParts = []interface{}{TextContentPart{Type: "text", Text: m.Content}} + m.ContentParts = []any{TextContentPart{Type: "text", Text: m.Content}} } else { - m.ContentParts = []interface{}{} + m.ContentParts = []any{} } m.hasContentParts = true } @@ -293,9 +291,9 @@ func (m *RoleMsg) AddImagePart(imageURL string) { if !m.hasContentParts { // Convert to content parts format if m.Content != "" { - m.ContentParts = []interface{}{TextContentPart{Type: "text", Text: m.Content}} + m.ContentParts = []any{TextContentPart{Type: "text", Text: m.Content}} } else { - m.ContentParts = []interface{}{} + m.ContentParts = []any{} } m.hasContentParts = true } @@ -382,7 +380,7 @@ func (cb *ChatBody) MakeStopSliceExcluding( continue } // Add multiple variations to catch different formatting - ss = append(ss, + ss = append(ss, role+":\n", // Most common: role with newline role+":", // Role with colon but no newline role+": ", // Role with colon and single space @@ -467,12 +465,12 @@ type LlamaCPPReq struct { Stream bool `json:"stream"` // For multimodal requests, prompt should be an object with prompt_string and multimodal_data // For regular requests, prompt is a string - Prompt interface{} `json:"prompt"` // Can be string or object with prompt_string and multimodal_data - Temperature float32 `json:"temperature"` - DryMultiplier float32 `json:"dry_multiplier"` - Stop []string `json:"stop"` - MinP float32 `json:"min_p"` - NPredict int32 `json:"n_predict"` + Prompt any `json:"prompt"` // Can be string or object with prompt_string and multimodal_data + Temperature float32 `json:"temperature"` + DryMultiplier float32 `json:"dry_multiplier"` + Stop []string `json:"stop"` + MinP float32 `json:"min_p"` + NPredict int32 `json:"n_predict"` // MaxTokens int `json:"max_tokens"` // DryBase float64 `json:"dry_base"` // DryAllowedLength int `json:"dry_allowed_length"` @@ -500,7 +498,7 @@ type PromptObject struct { } func NewLCPReq(prompt, model string, multimodalData []string, props map[string]float32, stopStrings []string) LlamaCPPReq { - var finalPrompt interface{} + var finalPrompt any if len(multimodalData) > 0 { // When multimodal data is present, use the object format as per Python example: // { "prompt": { "prompt_string": "...", "multimodal_data": [...] } } -- cgit v1.2.3 From 2cd3956f6a0f1806a13ae6d9e05d86b82fcb4f1c Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Tue, 10 Feb 2026 08:54:47 +0300 Subject: Chore: make debug; icon fix --- models/models.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'models/models.go') diff --git a/models/models.go b/models/models.go index 36ec88f..30ec5f5 100644 --- a/models/models.go +++ b/models/models.go @@ -189,9 +189,12 @@ func (m *RoleMsg) ToText(i int) string { contentStr = strings.Join(textParts, " ") + " " } // check if already has role annotation (/completion makes them) - if !strings.HasPrefix(contentStr, m.Role+":") { - icon = fmt.Sprintf("(%d) <%s>: ", i, m.Role) - } + // in that case remove it, and then add to icon + // since icon and content are separated by \n + contentStr, _ = strings.CutPrefix(contentStr, m.Role+":") + // if !strings.HasPrefix(contentStr, m.Role+":") { + icon = fmt.Sprintf("(%d) <%s>: ", i, m.Role) + // } textMsg := fmt.Sprintf("[-:-:b]%s[-:-:-]\n%s\n", icon, contentStr) return strings.ReplaceAll(textMsg, "\n\n", "\n") } -- cgit v1.2.3