mirror of
https://github.com/Direct-Dev-Ru/go-lcg.git
synced 2025-11-16 01:29:55 +00:00
added new proxy llm provider
This commit is contained in:
118
gpt/gpt.go
118
gpt/gpt.go
@@ -1,24 +1,40 @@
|
||||
package gpt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ProxySimpleChatRequest структура для простого запроса
|
||||
type ProxySimpleChatRequest struct {
|
||||
Message string `json:"message"`
|
||||
Model string `json:"model,omitempty"`
|
||||
}
|
||||
|
||||
// ProxySimpleChatResponse структура ответа для простого запроса
|
||||
type ProxySimpleChatResponse struct {
|
||||
Response string `json:"response"`
|
||||
Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
} `json:"usage,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Timeout int `json:"timeout_seconds,omitempty"`
|
||||
}
|
||||
|
||||
// Gpt3 обновленная структура с поддержкой разных провайдеров
|
||||
type Gpt3 struct {
|
||||
CompletionUrl string
|
||||
Prompt string
|
||||
Model string
|
||||
HomeDir string
|
||||
ApiKeyFile string
|
||||
ApiKey string
|
||||
Temperature float64
|
||||
Provider Provider
|
||||
Prompt string
|
||||
Model string
|
||||
HomeDir string
|
||||
ApiKeyFile string
|
||||
ApiKey string
|
||||
Temperature float64
|
||||
ProviderType string // "ollama", "proxy"
|
||||
}
|
||||
|
||||
type Chat struct {
|
||||
@@ -135,6 +151,11 @@ func (gpt3 *Gpt3) DeleteKey() {
|
||||
}
|
||||
|
||||
func (gpt3 *Gpt3) InitKey() {
|
||||
// Для proxy провайдера не нужен API ключ, используется JWT токен
|
||||
if gpt3.ProviderType == "proxy" {
|
||||
return
|
||||
}
|
||||
|
||||
load := gpt3.loadApiKey()
|
||||
if load {
|
||||
return
|
||||
@@ -145,55 +166,46 @@ func (gpt3 *Gpt3) InitKey() {
|
||||
gpt3.storeApiKey(apiKey)
|
||||
}
|
||||
|
||||
func (gpt3 *Gpt3) Completions(ask string) string {
|
||||
req, err := http.NewRequest("POST", gpt3.CompletionUrl, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
// req.Header.Set("Authorization", "Bearer "+strings.TrimSpace(gpt3.ApiKey))
|
||||
// NewGpt3 создает новый экземпляр GPT с выбранным провайдером
|
||||
func NewGpt3(providerType, host, apiKey, model, prompt string, temperature float64) *Gpt3 {
|
||||
var provider Provider
|
||||
|
||||
switch providerType {
|
||||
case "proxy":
|
||||
provider = NewProxyAPIProvider(host, apiKey, model) // apiKey используется как JWT токен
|
||||
case "ollama":
|
||||
provider = NewOllamaProvider(host, model, temperature)
|
||||
default:
|
||||
provider = NewOllamaProvider(host, model, temperature)
|
||||
}
|
||||
|
||||
return &Gpt3{
|
||||
Provider: provider,
|
||||
Prompt: prompt,
|
||||
Model: model,
|
||||
ApiKey: apiKey,
|
||||
Temperature: temperature,
|
||||
ProviderType: providerType,
|
||||
}
|
||||
}
|
||||
|
||||
// Completions обновленный метод с поддержкой разных провайдеров
|
||||
func (gpt3 *Gpt3) Completions(ask string) string {
|
||||
messages := []Chat{
|
||||
{"system", gpt3.Prompt},
|
||||
{"user", ask + "." + gpt3.Prompt},
|
||||
}
|
||||
payload := Gpt3Request{
|
||||
Model: gpt3.Model,
|
||||
Messages: messages,
|
||||
Stream: false,
|
||||
Options: Gpt3Options{gpt3.Temperature},
|
||||
{"user", ask + ". " + gpt3.Prompt},
|
||||
}
|
||||
|
||||
payloadJson, err := json.Marshal(payload)
|
||||
response, err := gpt3.Provider.Chat(messages)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
req.Body = io.NopCloser(bytes.NewBuffer(payloadJson))
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Println(string(body))
|
||||
fmt.Printf("Ошибка при выполнении запроса: %v\n", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
// var res Gpt3Response
|
||||
var res OllamaResponse
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// return strings.TrimSpace(res.Choices[0].Message.Content)
|
||||
return strings.TrimSpace(res.Message.Content)
|
||||
return response
|
||||
}
|
||||
|
||||
// Health проверяет состояние провайдера
|
||||
func (gpt3 *Gpt3) Health() error {
|
||||
return gpt3.Provider.Health()
|
||||
}
|
||||
|
||||
246
gpt/providers.go
Normal file
246
gpt/providers.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package gpt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Provider интерфейс для работы с разными LLM провайдерами
|
||||
type Provider interface {
|
||||
Chat(messages []Chat) (string, error)
|
||||
Health() error
|
||||
}
|
||||
|
||||
// ProxyAPIProvider реализация для прокси API (gin-restapi)
|
||||
type ProxyAPIProvider struct {
|
||||
BaseURL string
|
||||
JWTToken string
|
||||
Model string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// ProxyChatRequest структура запроса к прокси API
|
||||
type ProxyChatRequest struct {
|
||||
Messages []Chat `json:"messages"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Temperature float64 `json:"temperature,omitempty"`
|
||||
TopP float64 `json:"top_p,omitempty"`
|
||||
Stream bool `json:"stream,omitempty"`
|
||||
SystemContent string `json:"system_content,omitempty"`
|
||||
UserContent string `json:"user_content,omitempty"`
|
||||
RandomWords []string `json:"random_words,omitempty"`
|
||||
FallbackString string `json:"fallback_string,omitempty"`
|
||||
}
|
||||
|
||||
// ProxyChatResponse структура ответа от прокси API
|
||||
type ProxyChatResponse struct {
|
||||
Response string `json:"response"`
|
||||
Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
} `json:"usage,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Timeout int `json:"timeout_seconds,omitempty"`
|
||||
}
|
||||
|
||||
// ProxyHealthResponse структура ответа health check
|
||||
type ProxyHealthResponse struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Model string `json:"default_model,omitempty"`
|
||||
Timeout int `json:"default_timeout_seconds,omitempty"`
|
||||
}
|
||||
|
||||
// OllamaProvider реализация для Ollama API
|
||||
type OllamaProvider struct {
|
||||
BaseURL string
|
||||
Model string
|
||||
Temperature float64
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
func NewProxyAPIProvider(baseURL, jwtToken, model string) *ProxyAPIProvider {
|
||||
return &ProxyAPIProvider{
|
||||
BaseURL: strings.TrimSuffix(baseURL, "/"),
|
||||
JWTToken: jwtToken,
|
||||
Model: model,
|
||||
HTTPClient: &http.Client{Timeout: 120 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
func NewOllamaProvider(baseURL, model string, temperature float64) *OllamaProvider {
|
||||
return &OllamaProvider{
|
||||
BaseURL: strings.TrimSuffix(baseURL, "/"),
|
||||
Model: model,
|
||||
Temperature: temperature,
|
||||
HTTPClient: &http.Client{Timeout: 120 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// Chat для ProxyAPIProvider
|
||||
func (p *ProxyAPIProvider) Chat(messages []Chat) (string, error) {
|
||||
// Используем основной endpoint /api/v1/protected/sberchat/chat
|
||||
payload := ProxyChatRequest{
|
||||
Messages: messages,
|
||||
Model: p.Model,
|
||||
Temperature: 0.5,
|
||||
TopP: 0.5,
|
||||
Stream: false,
|
||||
RandomWords: []string{"linux", "command", "gpt"},
|
||||
FallbackString: "I'm sorry, I can't help with that. Please try again.",
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка маршалинга запроса: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", p.BaseURL+"/api/v1/protected/sberchat/chat", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка создания запроса: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if p.JWTToken != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+p.JWTToken)
|
||||
}
|
||||
|
||||
resp, err := p.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка выполнения запроса: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка чтения ответа: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("ошибка API: %d - %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var response ProxyChatResponse
|
||||
if err := json.Unmarshal(body, &response); err != nil {
|
||||
return "", fmt.Errorf("ошибка парсинга ответа: %w", err)
|
||||
}
|
||||
|
||||
if response.Error != "" {
|
||||
return "", fmt.Errorf("ошибка прокси API: %s", response.Error)
|
||||
}
|
||||
|
||||
if response.Response == "" {
|
||||
return "", fmt.Errorf("пустой ответ от API")
|
||||
}
|
||||
|
||||
return strings.TrimSpace(response.Response), nil
|
||||
}
|
||||
|
||||
// Health для ProxyAPIProvider
|
||||
func (p *ProxyAPIProvider) Health() error {
|
||||
req, err := http.NewRequest("GET", p.BaseURL+"/api/v1/protected/sberchat/health", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка создания health check запроса: %w", err)
|
||||
}
|
||||
|
||||
if p.JWTToken != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+p.JWTToken)
|
||||
}
|
||||
|
||||
resp, err := p.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка health check: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("health check failed: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var healthResponse ProxyHealthResponse
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка чтения health check ответа: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &healthResponse); err != nil {
|
||||
return fmt.Errorf("ошибка парсинга health check ответа: %w", err)
|
||||
}
|
||||
|
||||
if healthResponse.Status != "ok" {
|
||||
return fmt.Errorf("health check status: %s - %s", healthResponse.Status, healthResponse.Message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chat для OllamaProvider
|
||||
func (o *OllamaProvider) Chat(messages []Chat) (string, error) {
|
||||
payload := Gpt3Request{
|
||||
Model: o.Model,
|
||||
Messages: messages,
|
||||
Stream: false,
|
||||
Options: Gpt3Options{o.Temperature},
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка маршалинга запроса: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", o.BaseURL+"/api/chat", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка создания запроса: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := o.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка выполнения запроса: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ошибка чтения ответа: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("ошибка API: %d - %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var response OllamaResponse
|
||||
if err := json.Unmarshal(body, &response); err != nil {
|
||||
return "", fmt.Errorf("ошибка парсинга ответа: %w", err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(response.Message.Content), nil
|
||||
}
|
||||
|
||||
// Health для OllamaProvider
|
||||
func (o *OllamaProvider) Health() error {
|
||||
req, err := http.NewRequest("GET", o.BaseURL+"/api/tags", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка создания health check запроса: %w", err)
|
||||
}
|
||||
|
||||
resp, err := o.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка health check: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("health check failed: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user