diff options
Diffstat (limited to 'models')
| -rw-r--r-- | models/card.go | 58 | ||||
| -rw-r--r-- | models/db.go | 17 | ||||
| -rw-r--r-- | models/deepseek.go | 144 | ||||
| -rw-r--r-- | models/extra.go | 8 | ||||
| -rw-r--r-- | models/models.go | 236 | ||||
| -rw-r--r-- | models/openrouter.go | 154 | 
6 files changed, 562 insertions, 55 deletions
diff --git a/models/card.go b/models/card.go new file mode 100644 index 0000000..adfb030 --- /dev/null +++ b/models/card.go @@ -0,0 +1,58 @@ +package models + +import "strings" + +// https://github.com/malfoyslastname/character-card-spec-v2/blob/main/spec_v2.md +// what a bloat; trim to Role->Msg pair and first msg +type CharCardSpec struct { +	Name           string `json:"name"` +	Description    string `json:"description"` +	Personality    string `json:"personality"` +	FirstMes       string `json:"first_mes"` +	Avatar         string `json:"avatar"` +	Chat           string `json:"chat"` +	MesExample     string `json:"mes_example"` +	Scenario       string `json:"scenario"` +	CreateDate     string `json:"create_date"` +	Talkativeness  string `json:"talkativeness"` +	Fav            bool   `json:"fav"` +	Creatorcomment string `json:"creatorcomment"` +	Spec           string `json:"spec"` +	SpecVersion    string `json:"spec_version"` +	Tags           []any  `json:"tags"` +	Extentions     []byte `json:"extentions"` +} + +type Spec2Wrapper struct { +	Data CharCardSpec `json:"data"` +} + +func (c *CharCardSpec) Simplify(userName, fpath string) *CharCard { +	fm := strings.ReplaceAll(strings.ReplaceAll(c.FirstMes, "{{char}}", c.Name), "{{user}}", userName) +	sysPr := strings.ReplaceAll(strings.ReplaceAll(c.Description, "{{char}}", c.Name), "{{user}}", userName) +	return &CharCard{ +		SysPrompt: sysPr, +		FirstMsg:  fm, +		Role:      c.Name, +		FilePath:  fpath, +	} +} + +type CharCard struct { +	SysPrompt string `json:"sys_prompt"` +	FirstMsg  string `json:"first_msg"` +	Role      string `json:"role"` +	FilePath  string `json:"filepath"` +} + +func (cc *CharCard) ToSpec(userName string) *CharCardSpec { +	descr := strings.ReplaceAll(strings.ReplaceAll(cc.SysPrompt, cc.Role, "{{char}}"), userName, "{{user}}") +	return &CharCardSpec{ +		Name:        cc.Role, +		Description: descr, +		FirstMes:    cc.FirstMsg, +		Spec:        "chara_card_v2", +		SpecVersion: "2.0", +		Extentions:  []byte("{}"), +	} +} diff --git a/models/db.go b/models/db.go index 5f49003..090f46d 100644 --- a/models/db.go +++ b/models/db.go @@ -8,13 +8,14 @@ import (  type Chat struct {  	ID        uint32    `db:"id" json:"id"`  	Name      string    `db:"name" json:"name"` -	Msgs      string    `db:"msgs" json:"msgs"` // []MessagesStory to string json +	Msgs      string    `db:"msgs" json:"msgs"` // []RoleMsg to string json +	Agent     string    `db:"agent" json:"agent"`  	CreatedAt time.Time `db:"created_at" json:"created_at"`  	UpdatedAt time.Time `db:"updated_at" json:"updated_at"`  } -func (c Chat) ToHistory() ([]MessagesStory, error) { -	resp := []MessagesStory{} +func (c Chat) ToHistory() ([]RoleMsg, error) { +	resp := []RoleMsg{}  	if err := json.Unmarshal([]byte(c.Msgs), &resp); err != nil {  		return nil, err  	} @@ -34,3 +35,13 @@ type Memory struct {  	CreatedAt time.Time `db:"created_at" json:"created_at"`  	UpdatedAt time.Time `db:"updated_at" json:"updated_at"`  } + +// vector models + +type VectorRow struct { +	Embeddings []float32 `db:"embeddings" json:"embeddings"` +	Slug       string    `db:"slug" json:"slug"` +	RawText    string    `db:"raw_text" json:"raw_text"` +	Distance   float32   `db:"distance" json:"distance"` +	FileName   string    `db:"filename" json:"filename"` +} diff --git a/models/deepseek.go b/models/deepseek.go new file mode 100644 index 0000000..8f9868d --- /dev/null +++ b/models/deepseek.go @@ -0,0 +1,144 @@ +package models + +type DSChatReq struct { +	Messages         []RoleMsg `json:"messages"` +	Model            string    `json:"model"` +	Stream           bool      `json:"stream"` +	FrequencyPenalty int       `json:"frequency_penalty"` +	MaxTokens        int       `json:"max_tokens"` +	PresencePenalty  int       `json:"presence_penalty"` +	Temperature      float32   `json:"temperature"` +	TopP             float32   `json:"top_p"` +	// ResponseFormat   struct { +	// 	Type string `json:"type"` +	// } `json:"response_format"` +	// Stop          any    `json:"stop"` +	// StreamOptions any     `json:"stream_options"` +	// Tools         any     `json:"tools"` +	// ToolChoice    string  `json:"tool_choice"` +	// Logprobs      bool    `json:"logprobs"` +	// TopLogprobs   any     `json:"top_logprobs"` +} + +func NewDSChatReq(cb ChatBody) DSChatReq { +	return DSChatReq{ +		Messages:         cb.Messages, +		Model:            cb.Model, +		Stream:           cb.Stream, +		MaxTokens:        2048, +		PresencePenalty:  0, +		FrequencyPenalty: 0, +		Temperature:      1.0, +		TopP:             1.0, +	} +} + +type DSCompletionReq struct { +	Model            string `json:"model"` +	Prompt           string `json:"prompt"` +	Echo             bool   `json:"echo"` +	FrequencyPenalty int    `json:"frequency_penalty"` +	// Logprobs         int     `json:"logprobs"` +	MaxTokens       int     `json:"max_tokens"` +	PresencePenalty int     `json:"presence_penalty"` +	Stop            any     `json:"stop"` +	Stream          bool    `json:"stream"` +	StreamOptions   any     `json:"stream_options"` +	Suffix          any     `json:"suffix"` +	Temperature     float32 `json:"temperature"` +	TopP            float32 `json:"top_p"` +} + +func NewDSCompletionReq(prompt, model string, temp float32, stopSlice []string) DSCompletionReq { +	return DSCompletionReq{ +		Model:            model, +		Prompt:           prompt, +		Temperature:      temp, +		Stream:           true, +		Echo:             false, +		MaxTokens:        2048, +		PresencePenalty:  0, +		FrequencyPenalty: 0, +		TopP:             1.0, +		Stop:             stopSlice, +	} +} + +type DSCompletionResp struct { +	ID      string `json:"id"` +	Choices []struct { +		FinishReason string `json:"finish_reason"` +		Index        int    `json:"index"` +		Logprobs     struct { +			TextOffset    []int    `json:"text_offset"` +			TokenLogprobs []int    `json:"token_logprobs"` +			Tokens        []string `json:"tokens"` +			TopLogprobs   []struct { +			} `json:"top_logprobs"` +		} `json:"logprobs"` +		Text string `json:"text"` +	} `json:"choices"` +	Created           int    `json:"created"` +	Model             string `json:"model"` +	SystemFingerprint string `json:"system_fingerprint"` +	Object            string `json:"object"` +	Usage             struct { +		CompletionTokens        int `json:"completion_tokens"` +		PromptTokens            int `json:"prompt_tokens"` +		PromptCacheHitTokens    int `json:"prompt_cache_hit_tokens"` +		PromptCacheMissTokens   int `json:"prompt_cache_miss_tokens"` +		TotalTokens             int `json:"total_tokens"` +		CompletionTokensDetails struct { +			ReasoningTokens int `json:"reasoning_tokens"` +		} `json:"completion_tokens_details"` +	} `json:"usage"` +} + +type DSChatResp struct { +	Choices []struct { +		Delta struct { +			Content string `json:"content"` +			Role    any    `json:"role"` +		} `json:"delta"` +		FinishReason string `json:"finish_reason"` +		Index        int    `json:"index"` +		Logprobs     any    `json:"logprobs"` +	} `json:"choices"` +	Created           int    `json:"created"` +	ID                string `json:"id"` +	Model             string `json:"model"` +	Object            string `json:"object"` +	SystemFingerprint string `json:"system_fingerprint"` +	Usage             struct { +		CompletionTokens int `json:"completion_tokens"` +		PromptTokens     int `json:"prompt_tokens"` +		TotalTokens      int `json:"total_tokens"` +	} `json:"usage"` +} + +type DSChatStreamResp struct { +	ID                string `json:"id"` +	Object            string `json:"object"` +	Created           int    `json:"created"` +	Model             string `json:"model"` +	SystemFingerprint string `json:"system_fingerprint"` +	Choices           []struct { +		Index int `json:"index"` +		Delta struct { +			Content          string `json:"content"` +			ReasoningContent string `json:"reasoning_content"` +		} `json:"delta"` +		Logprobs     any    `json:"logprobs"` +		FinishReason string `json:"finish_reason"` +	} `json:"choices"` +} + +type DSBalance struct { +	IsAvailable  bool `json:"is_available"` +	BalanceInfos []struct { +		Currency        string `json:"currency"` +		TotalBalance    string `json:"total_balance"` +		GrantedBalance  string `json:"granted_balance"` +		ToppedUpBalance string `json:"topped_up_balance"` +	} `json:"balance_infos"` +} diff --git a/models/extra.go b/models/extra.go new file mode 100644 index 0000000..e1ca80f --- /dev/null +++ b/models/extra.go @@ -0,0 +1,8 @@ +package models + +type AudioFormat string + +const ( +	AFWav AudioFormat = "wav" +	AFMP3 AudioFormat = "mp3" +) diff --git a/models/models.go b/models/models.go index 880779f..0a10da1 100644 --- a/models/models.go +++ b/models/models.go @@ -5,15 +5,9 @@ import (  	"strings"  ) -// type FuncCall struct { -// 	XMLName xml.Name `xml:"tool_call"` -// 	Name    string   `xml:"name"` -// 	Args    []string `xml:"args"` -// } -  type FuncCall struct { -	Name string `json:"name"` -	Args string `json:"args"` +	Name string            `json:"name"` +	Args map[string]string `json:"args"`  }  type LLMResp struct { @@ -36,13 +30,24 @@ type LLMResp struct {  	ID string `json:"id"`  } +type ToolDeltaFunc struct { +	Name      string `json:"name"` +	Arguments string `json:"arguments"` +} + +type ToolDeltaResp struct { +	Index    int           `json:"index"` +	Function ToolDeltaFunc `json:"function"` +} +  // for streaming  type LLMRespChunk struct {  	Choices []struct {  		FinishReason string `json:"finish_reason"`  		Index        int    `json:"index"`  		Delta        struct { -			Content string `json:"content"` +			Content   string          `json:"content"` +			ToolCalls []ToolDeltaResp `json:"tool_calls"`  		} `json:"delta"`  	} `json:"choices"`  	Created int    `json:"created"` @@ -56,56 +61,183 @@ type LLMRespChunk struct {  	} `json:"usage"`  } -type MessagesStory struct { +type TextChunk struct { +	Chunk     string +	ToolChunk string +	Finished  bool +	ToolResp  bool +	FuncName  string +} + +type RoleMsg struct {  	Role    string `json:"role"`  	Content string `json:"content"`  } -func (m MessagesStory) ToText(i int) string { -	icon := "" -	switch m.Role { -	case "assistant": -		icon = fmt.Sprintf("(%d) <🤖>: ", i) -	case "user": -		icon = fmt.Sprintf("(%d) <user>: ", i) -	case "system": -		icon = fmt.Sprintf("(%d) <system>: ", i) -	case "tool": -		icon = fmt.Sprintf("(%d) <tool>: ", i) +func (m RoleMsg) ToText(i int) string { +	icon := fmt.Sprintf("(%d)", i) +	// check if already has role annotation (/completion makes them) +	if !strings.HasPrefix(m.Content, m.Role+":") { +		icon = fmt.Sprintf("(%d) <%s>: ", i, m.Role)  	} -	textMsg := fmt.Sprintf("%s%s\n", icon, m.Content) +	textMsg := fmt.Sprintf("[-:-:b]%s[-:-:-]\n%s\n", icon, m.Content)  	return strings.ReplaceAll(textMsg, "\n\n", "\n")  } +func (m RoleMsg) ToPrompt() string { +	return strings.ReplaceAll(fmt.Sprintf("%s:\n%s", m.Role, m.Content), "\n\n", "\n") +} +  type ChatBody struct { -	Model    string          `json:"model"` -	Stream   bool            `json:"stream"` -	Messages []MessagesStory `json:"messages"` -} - -type ChatToolsBody struct { -	Model    string          `json:"model"` -	Messages []MessagesStory `json:"messages"` -	Tools    []struct { -		Type     string `json:"type"` -		Function struct { -			Name        string `json:"name"` -			Description string `json:"description"` -			Parameters  struct { -				Type       string `json:"type"` -				Properties struct { -					Location struct { -						Type        string `json:"type"` -						Description string `json:"description"` -					} `json:"location"` -					Unit struct { -						Type string   `json:"type"` -						Enum []string `json:"enum"` -					} `json:"unit"` -				} `json:"properties"` -				Required []string `json:"required"` -			} `json:"parameters"` -		} `json:"function"` -	} `json:"tools"` -	ToolChoice string `json:"tool_choice"` +	Model    string    `json:"model"` +	Stream   bool      `json:"stream"` +	Messages []RoleMsg `json:"messages"` +} + +func (cb *ChatBody) Rename(oldname, newname string) { +	for i, m := range cb.Messages { +		cb.Messages[i].Content = strings.ReplaceAll(m.Content, oldname, newname) +		cb.Messages[i].Role = strings.ReplaceAll(m.Role, oldname, newname) +	} +} + +func (cb *ChatBody) ListRoles() []string { +	namesMap := make(map[string]struct{}) +	for _, m := range cb.Messages { +		namesMap[m.Role] = struct{}{} +	} +	resp := make([]string, len(namesMap)) +	i := 0 +	for k := range namesMap { +		resp[i] = k +		i++ +	} +	return resp +} + +func (cb *ChatBody) MakeStopSlice() []string { +	namesMap := make(map[string]struct{}) +	for _, m := range cb.Messages { +		namesMap[m.Role] = struct{}{} +	} +	ss := []string{"<|im_end|>"} +	for k := range namesMap { +		ss = append(ss, k+":\n") +	} +	return ss +} + +type EmbeddingResp struct { +	Embedding []float32 `json:"embedding"` +	Index     uint32    `json:"index"` +} + +// type EmbeddingsResp struct { +// 	Model  string `json:"model"` +// 	Object string `json:"object"` +// 	Usage  struct { +// 		PromptTokens int `json:"prompt_tokens"` +// 		TotalTokens  int `json:"total_tokens"` +// 	} `json:"usage"` +// 	Data []struct { +// 		Embedding []float32 `json:"embedding"` +// 		Index     int       `json:"index"` +// 		Object    string    `json:"object"` +// 	} `json:"data"` +// } + +// === tools models + +type ToolArgProps struct { +	Type        string `json:"type"` +	Description string `json:"description"` +} + +type ToolFuncParams struct { +	Type       string                  `json:"type"` +	Properties map[string]ToolArgProps `json:"properties"` +	Required   []string                `json:"required"` +} + +type ToolFunc struct { +	Name        string         `json:"name"` +	Description string         `json:"description"` +	Parameters  ToolFuncParams `json:"parameters"` +} + +type Tool struct { +	Type     string   `json:"type"` +	Function ToolFunc `json:"function"` +} + +type OpenAIReq struct { +	*ChatBody +	Tools []Tool `json:"tools"` +} + +// === + +type LLMModels struct { +	Object string `json:"object"` +	Data   []struct { +		ID      string `json:"id"` +		Object  string `json:"object"` +		Created int    `json:"created"` +		OwnedBy string `json:"owned_by"` +		Meta    struct { +			VocabType int   `json:"vocab_type"` +			NVocab    int   `json:"n_vocab"` +			NCtxTrain int   `json:"n_ctx_train"` +			NEmbd     int   `json:"n_embd"` +			NParams   int64 `json:"n_params"` +			Size      int64 `json:"size"` +		} `json:"meta"` +	} `json:"data"` +} + +type LlamaCPPReq struct { +	Stream bool `json:"stream"` +	// Messages      []RoleMsg `json:"messages"` +	Prompt        string   `json:"prompt"` +	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"` +	// DryPenaltyLastN  int     `json:"dry_penalty_last_n"` +	// CachePrompt      bool    `json:"cache_prompt"` +	// DynatempRange    int     `json:"dynatemp_range"` +	// DynatempExponent int     `json:"dynatemp_exponent"` +	// TopK             int     `json:"top_k"` +	// TopP             float32 `json:"top_p"` +	// TypicalP         int     `json:"typical_p"` +	// XtcProbability   int     `json:"xtc_probability"` +	// XtcThreshold     float32 `json:"xtc_threshold"` +	// RepeatLastN      int     `json:"repeat_last_n"` +	// RepeatPenalty    int     `json:"repeat_penalty"` +	// PresencePenalty  int     `json:"presence_penalty"` +	// FrequencyPenalty int     `json:"frequency_penalty"` +	// Samplers         string  `json:"samplers"` +} + +func NewLCPReq(prompt string, props map[string]float32, stopStrings []string) LlamaCPPReq { +	return LlamaCPPReq{ +		Stream: true, +		Prompt: prompt, +		// Temperature:   0.8, +		// DryMultiplier: 0.5, +		Temperature:   props["temperature"], +		DryMultiplier: props["dry_multiplier"], +		MinP:          props["min_p"], +		NPredict:      int32(props["n_predict"]), +		Stop:          stopStrings, +	} +} + +type LlamaCPPResp struct { +	Content string `json:"content"` +	Stop    bool   `json:"stop"`  } diff --git a/models/openrouter.go b/models/openrouter.go new file mode 100644 index 0000000..933598e --- /dev/null +++ b/models/openrouter.go @@ -0,0 +1,154 @@ +package models + +// openrouter +// https://openrouter.ai/docs/api-reference/completion +type OpenRouterCompletionReq struct { +	Model       string   `json:"model"` +	Prompt      string   `json:"prompt"` +	Stream      bool     `json:"stream"` +	Temperature float32  `json:"temperature"` +	Stop        []string `json:"stop"` // not present in docs +	MinP        float32  `json:"min_p"` +	NPredict    int32    `json:"max_tokens"` +} + +func NewOpenRouterCompletionReq(model, prompt string, props map[string]float32, stopStrings []string) OpenRouterCompletionReq { +	return OpenRouterCompletionReq{ +		Stream:      true, +		Prompt:      prompt, +		Temperature: props["temperature"], +		MinP:        props["min_p"], +		NPredict:    int32(props["n_predict"]), +		Stop:        stopStrings, +		Model:       model, +	} +} + +type OpenRouterChatReq struct { +	Messages    []RoleMsg `json:"messages"` +	Model       string    `json:"model"` +	Stream      bool      `json:"stream"` +	Temperature float32   `json:"temperature"` +	MinP        float32   `json:"min_p"` +	NPredict    int32     `json:"max_tokens"` +} + +func NewOpenRouterChatReq(cb ChatBody, props map[string]float32) OpenRouterChatReq { +	return OpenRouterChatReq{ +		Messages:    cb.Messages, +		Model:       cb.Model, +		Stream:      cb.Stream, +		Temperature: props["temperature"], +		MinP:        props["min_p"], +		NPredict:    int32(props["n_predict"]), +	} +} + +type OpenRouterChatRespNonStream struct { +	ID       string `json:"id"` +	Provider string `json:"provider"` +	Model    string `json:"model"` +	Object   string `json:"object"` +	Created  int    `json:"created"` +	Choices  []struct { +		Logprobs           any    `json:"logprobs"` +		FinishReason       string `json:"finish_reason"` +		NativeFinishReason string `json:"native_finish_reason"` +		Index              int    `json:"index"` +		Message            struct { +			Role      string `json:"role"` +			Content   string `json:"content"` +			Refusal   any    `json:"refusal"` +			Reasoning any    `json:"reasoning"` +		} `json:"message"` +	} `json:"choices"` +	Usage struct { +		PromptTokens     int `json:"prompt_tokens"` +		CompletionTokens int `json:"completion_tokens"` +		TotalTokens      int `json:"total_tokens"` +	} `json:"usage"` +} + +type OpenRouterChatResp struct { +	ID       string `json:"id"` +	Provider string `json:"provider"` +	Model    string `json:"model"` +	Object   string `json:"object"` +	Created  int    `json:"created"` +	Choices  []struct { +		Index int `json:"index"` +		Delta struct { +			Role    string `json:"role"` +			Content string `json:"content"` +		} `json:"delta"` +		FinishReason       string `json:"finish_reason"` +		NativeFinishReason string `json:"native_finish_reason"` +		Logprobs           any    `json:"logprobs"` +	} `json:"choices"` +} + +type OpenRouterCompletionResp struct { +	ID       string `json:"id"` +	Provider string `json:"provider"` +	Model    string `json:"model"` +	Object   string `json:"object"` +	Created  int    `json:"created"` +	Choices  []struct { +		Text               string `json:"text"` +		FinishReason       string `json:"finish_reason"` +		NativeFinishReason string `json:"native_finish_reason"` +		Logprobs           any    `json:"logprobs"` +	} `json:"choices"` +} + +type ORModel struct { +	ID            string `json:"id"` +	CanonicalSlug string `json:"canonical_slug"` +	HuggingFaceID string `json:"hugging_face_id"` +	Name          string `json:"name"` +	Created       int    `json:"created"` +	Description   string `json:"description"` +	ContextLength int    `json:"context_length"` +	Architecture  struct { +		Modality         string   `json:"modality"` +		InputModalities  []string `json:"input_modalities"` +		OutputModalities []string `json:"output_modalities"` +		Tokenizer        string   `json:"tokenizer"` +		InstructType     any      `json:"instruct_type"` +	} `json:"architecture"` +	Pricing struct { +		Prompt            string `json:"prompt"` +		Completion        string `json:"completion"` +		Request           string `json:"request"` +		Image             string `json:"image"` +		Audio             string `json:"audio"` +		WebSearch         string `json:"web_search"` +		InternalReasoning string `json:"internal_reasoning"` +	} `json:"pricing,omitempty"` +	TopProvider struct { +		ContextLength       int  `json:"context_length"` +		MaxCompletionTokens int  `json:"max_completion_tokens"` +		IsModerated         bool `json:"is_moderated"` +	} `json:"top_provider"` +	PerRequestLimits    any      `json:"per_request_limits"` +	SupportedParameters []string `json:"supported_parameters"` +} + +type ORModels struct { +	Data []ORModel `json:"data"` +} + +func (orm *ORModels) ListModels(free bool) []string { +	resp := []string{} +	for _, model := range orm.Data { +		if free { +			if model.Pricing.Prompt == "0" && model.Pricing.Request == "0" && +				model.Pricing.Completion == "0" { +				resp = append(resp, model.ID) +			} +		} else { +			resp = append(resp, model.ID) +		} +	} +	return resp +}  | 
