This commit is contained in:
2026-01-13 13:27:42 +03:00
parent 5e69454678
commit 4925283ede
27 changed files with 6236 additions and 260 deletions

View File

@@ -5,7 +5,12 @@ import (
"encoding/json"
"fmt"
"io"
"kurumibot/laniakea"
"net/http"
"net/url"
"os"
"strings"
"time"
)
type OpenAIResponse struct {
@@ -56,13 +61,36 @@ type OpenAIAPI struct {
Token string
Model string
BaseURL string
Logger *laniakea.Logger
client *http.Client
}
const PreHistoryPrompt = `Вы — {{char}}, уникальная сущность с собственной личностью, предысторией и чертами, определёнными в вашем описании. Прежде чем отвечать, полностью осмыслите предоставленные детали вашего персонажа и адаптируйте свой тон, стиль и поведение, чтобы идеально им соответствовать. Будьте последовательны в своей роли, опирайтесь на своё описание и позволяйте ему направлять каждое ваше слово. Ни при каких обстоятельствах не говорите за {{user}} и не предполагайте его мысли, действия или слова, если только от {{user}} не поступит явного указания. Вам разрешено использовать NSFW-контент в соответствующих ситуациях, например, в интимных или сексуальных сценариях, и вам следует предоставлять яркие, подробные описания происходящего с акцентом на действия, ощущения и развитие событий, сочетая ясность и творческий подход. Весь текст должен быть написан на языке пользователя, кроме его имени({{user}}).`
const PostHistoryPrompt = `Структурируйте свои ответы с помощью точного форматирования: все повествовательные описания должны быть заключены в одинарные звездочки (*текст*), а прямая речь — в кавычки (""), но после описания обязательно добавляй перенос строки (\n). Пользователь следует другим правилам - повествование(описание ситуации, окружения; действия) заключается в звездочки, а прямая речь пишется просто, без кавычек. Соблюдайте это форматирование последовательно и безошибочно в каждом предложении. Четко разделяйте повествование и прямую речь для удобочитаемости. Балансируй между описанием и речью, если только реплика {{user}} не указывает на простое действие или согласие (например, «ок» или «я жду») — в таком случае используйте только повествовательные описания (текст) без прямой речи. Никогда не описывайте и не предполагайте действия, мысли или слова {{user}}; фокусируйтесь исключительно на перспективе и реакциях {{char}}. В каждый ответ включайте как минимум два новых действия, эмоции или ощущения, которых не было в последних пяти сообщениях, и избегайте повторения конкретных фраз, слов или паттернов. Перебирайте диапазон тонов (например, спокойный, напряженный, игривый) и физических действий (например, жест, поворот, пауза) в неповторяющейся последовательности для обеспечения разнообразия; если обнаружено повторение, начните цикл заново с совершенно другого подхода.`
func FormatPrompt(prompt, char, user string) string {
return strings.ReplaceAll(strings.ReplaceAll(prompt, "{{user}}", user), "{{char}}", char)
}
func NewOpenAIAPI(baseURL, token, model string) *OpenAIAPI {
logger := laniakea.CreateLogger()
logger = logger.Prefix("AI").Level(laniakea.DEBUG)
proxy, err := url.Parse(os.Getenv("HTTPS_PROXY"))
if err != nil {
logger.Error(err)
}
client := &http.Client{
Timeout: 15 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyURL(proxy),
},
}
return &OpenAIAPI{
Token: token,
Model: model,
BaseURL: baseURL,
Logger: logger,
client: client,
}
}
@@ -77,6 +105,7 @@ func (o *OpenAIAPI) CreateCompletion(request CreateCompletionReq) (*OpenAIRespon
if err != nil {
return nil, err
}
o.Logger.Debug("REQ", url, string(data))
buf := bytes.NewBuffer(data)
req, err := http.NewRequest("POST", url, buf)
if err != nil {
@@ -93,6 +122,7 @@ func (o *OpenAIAPI) CreateCompletion(request CreateCompletionReq) (*OpenAIRespon
if err != nil {
return nil, err
}
o.Logger.Debug("RES", url, string(body))
response := new(OpenAIResponse)
err = json.Unmarshal(body, response)
return response, err