150 lines
3.7 KiB
Go
150 lines
3.7 KiB
Go
package gigachat
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"gitea.pena/SQuiz/common/model"
|
|
"github.com/go-redis/redis/v8"
|
|
"github.com/go-resty/resty/v2"
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
"time"
|
|
)
|
|
|
|
type Deps struct {
|
|
Logger *zap.Logger
|
|
Client *resty.Client
|
|
BaseURL string
|
|
AuthKey string
|
|
RedisClient *redis.Client
|
|
}
|
|
|
|
type GigaChatClient struct {
|
|
logger *zap.Logger
|
|
client *resty.Client
|
|
baseURL string
|
|
authKey string
|
|
redisClient *redis.Client
|
|
}
|
|
|
|
func NewGigaChatClient(ctx context.Context, deps Deps) (*GigaChatClient, error) {
|
|
client := &GigaChatClient{
|
|
logger: deps.Logger,
|
|
client: deps.Client,
|
|
baseURL: deps.BaseURL,
|
|
authKey: deps.AuthKey,
|
|
redisClient: deps.RedisClient,
|
|
}
|
|
|
|
if err := client.updateToken(ctx); err != nil {
|
|
return nil, fmt.Errorf("failed to get access token: %w", err)
|
|
}
|
|
|
|
return client, nil
|
|
}
|
|
|
|
func (r *GigaChatClient) SendMsg(ctx context.Context, audience model.GigaChatAudience, question model.Question) (string, error) {
|
|
gender := "женский"
|
|
if audience.Sex {
|
|
gender = "мужской"
|
|
}
|
|
userInput := fmt.Sprintf(model.ReworkQuestionPrompt, audience.Age, gender, question.Title, question.Description)
|
|
|
|
token, err := r.redisClient.Get(ctx, "gigachat_token").Result()
|
|
if err != nil {
|
|
r.logger.Error("failed to get token from redis", zap.Error(err))
|
|
return "", err
|
|
}
|
|
|
|
reqBody := model.GigaChatRequest{
|
|
Model: "GigaChat-2-Max",
|
|
Stream: false,
|
|
UpdateInterval: 0,
|
|
Messages: []model.GigaChatMessage{
|
|
{Role: "system", Content: model.CreatePrompt},
|
|
{Role: "user", Content: userInput},
|
|
},
|
|
}
|
|
|
|
var response model.GigaChatResponse
|
|
|
|
resp, err := r.client.R().
|
|
SetHeader("Content-Type", "application/json").
|
|
SetHeader("Authorization", "Bearer "+token).
|
|
SetBody(reqBody).
|
|
SetResult(&response).
|
|
Post(r.baseURL + "/chat/completions")
|
|
|
|
if err != nil {
|
|
r.logger.Error("failed send request to GigaChat", zap.Error(err))
|
|
return "", err
|
|
}
|
|
|
|
if resp.IsError() {
|
|
errMsg := fmt.Sprintf("error GigaChat API: %s", resp.Status())
|
|
r.logger.Error(errMsg)
|
|
return "", errors.New(errMsg)
|
|
}
|
|
|
|
if len(response.Choices) == 0 || response.Choices[0].Message.Content == "" {
|
|
// когда возникает такая ошибка то значит еще траим отправить запрос
|
|
return "", model.EmptyResponseErrorGigaChat
|
|
}
|
|
|
|
return response.Choices[0].Message.Content, nil
|
|
}
|
|
|
|
func (r *GigaChatClient) TokenResearch(ctx context.Context) {
|
|
ticker := time.NewTicker(5 * time.Minute)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
ttl, err := r.redisClient.TTL(ctx, "gigachat_token").Result()
|
|
if err != nil || ttl < 2*time.Minute {
|
|
if err := r.updateToken(ctx); err != nil {
|
|
r.logger.Error("failed to update GigaChat token", zap.Error(err))
|
|
} else {
|
|
r.logger.Info("successfully updated GigaChat token")
|
|
}
|
|
}
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *GigaChatClient) updateToken(ctx context.Context) error {
|
|
formData := "scope=GIGACHAT_API_PERS"
|
|
|
|
var respData struct {
|
|
AccessToken string `json:"access_token"`
|
|
ExpiresAt int64 `json:"expires_at"`
|
|
}
|
|
|
|
resp, err := r.client.R().
|
|
SetHeader("Authorization", "Basic "+r.authKey).
|
|
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
|
SetBody(formData).
|
|
SetHeader("RqUID", uuid.New().String()).
|
|
SetResult(&respData).
|
|
Post("https://ngw.devices.sberbank.ru:9443/api/v2/oauth")
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp.IsError() {
|
|
return fmt.Errorf("token request failed: %s", resp.Status())
|
|
}
|
|
|
|
ttl := time.Until(time.Unix(respData.ExpiresAt, 0))
|
|
err = r.redisClient.Set(ctx, "gigachat_token", respData.AccessToken, ttl).Err()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save token to redis: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|