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_B2B" 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 }