- Добавлен ConsoleLogger для подробного логирования этапов обработки аудиокниг в консоли. - Введен ConsolePresenter для форматированного вывода результатов сканирования в консоль. - Создан ProcessAudioBooksUseCase для обработки полного конвейера обработки аудиокниг, включая сканирование папок, извлечение метаданных, поиск торрентов и запись результатов. - Реализована проверка LLM для улучшения метаданных. - Добавлена обработка ошибок и логирование на всех этапах обработки.
336 lines
11 KiB
Markdown
336 lines
11 KiB
Markdown
# OpenRouter Integration
|
||
|
||
Модуль интеграции с [OpenRouter API](https://openrouter.ai/) для использования различных LLM моделей в проекте GenAudioBookInfo.
|
||
|
||
## Описание
|
||
|
||
OpenRouter предоставляет унифицированный API для доступа к различным языковым моделям (GPT-4, Claude, LLaMA и др.) через единый интерфейс, совместимый с OpenAI API.
|
||
|
||
## Архитектура
|
||
|
||
Модуль реализован в соответствии с Clean Architecture:
|
||
|
||
- **Domain Layer** (`internal/domain/`):
|
||
- `ports.go` - интерфейс `LLMClient`
|
||
- `llm.go` - доменные структуры `LLMRequest`, `LLMResponse`, `LLMMessage`
|
||
|
||
- **Infrastructure Layer** (`internal/infrastructure/`):
|
||
- `openrouter_client.go` - реализация HTTP клиента для OpenRouter API
|
||
|
||
## Конфигурация
|
||
|
||
Добавьте настройки в `config.yaml`:
|
||
|
||
```yaml
|
||
openrouter:
|
||
api_key: "sk-or-v1-..." # Ваш API ключ с https://openrouter.ai/keys
|
||
base_url: "https://openrouter.ai/api/v1" # Базовый URL (по умолчанию)
|
||
timeout: 60s # Таймаут запросов
|
||
model: "openai/gpt-3.5-turbo" # Модель по умолчанию
|
||
prompt: | # System prompt для валидации метаданных (опционально)
|
||
Ты — эксперт по метаданным аудиокниг.
|
||
Проверяй формат автора (Фамилия Имя) и чистоту названий.
|
||
```
|
||
|
||
### Получение API ключа
|
||
|
||
1. Зарегистрируйтесь на [openrouter.ai](https://openrouter.ai/)
|
||
2. Перейдите в раздел [Keys](https://openrouter.ai/keys)
|
||
3. Создайте новый API ключ
|
||
4. Добавьте ключ в `config.yaml` или переменную окружения `OPENROUTER_API_KEY`
|
||
|
||
## Использование
|
||
|
||
### Базовый пример
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/fofanov/genaudiobookinfo/internal/domain"
|
||
"github.com/fofanov/genaudiobookinfo/internal/infrastructure"
|
||
)
|
||
|
||
func main() {
|
||
// Создание клиента
|
||
client := infrastructure.NewOpenRouterClient(infrastructure.OpenRouterConfig{
|
||
APIKey: "sk-or-v1-...",
|
||
BaseURL: "https://openrouter.ai/api/v1",
|
||
Timeout: 60 * time.Second,
|
||
Model: "openai/gpt-3.5-turbo",
|
||
})
|
||
|
||
// Создание запроса
|
||
req := &domain.LLMRequest{
|
||
Model: "openai/gpt-3.5-turbo",
|
||
Messages: []domain.LLMMessage{
|
||
{
|
||
Role: "system",
|
||
Content: "Ты — полезный ассистент.",
|
||
},
|
||
{
|
||
Role: "user",
|
||
Content: "Привет! Как дела?",
|
||
},
|
||
},
|
||
Temperature: 0.7,
|
||
MaxTokens: 100,
|
||
}
|
||
|
||
// Выполнение запроса
|
||
ctx := context.Background()
|
||
resp, err := client.GenerateCompletion(ctx, req)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
// Получение ответа
|
||
if len(resp.Choices) > 0 {
|
||
fmt.Println(resp.Choices[0].Message.Content)
|
||
fmt.Printf("Использовано токенов: %d\n", resp.Usage.TotalTokens)
|
||
}
|
||
}
|
||
```
|
||
|
||
### Параметры запроса
|
||
|
||
#### LLMRequest
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| `Model` | string | Идентификатор модели (например, `openai/gpt-4`) |
|
||
| `Messages` | []LLMMessage | История диалога |
|
||
| `Temperature` | float64 | Случайность генерации (0.0 - 2.0) |
|
||
| `MaxTokens` | int | Максимум токенов в ответе |
|
||
| `TopP` | float64 | Nucleus sampling параметр |
|
||
| `FrequencyPenalty` | float64 | Штраф за повторяющиеся токены |
|
||
| `PresencePenalty` | float64 | Штраф за уже встречавшиеся токены |
|
||
|
||
#### LLMMessage
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| `Role` | string | Роль: `system`, `user`, или `assistant` |
|
||
| `Content` | string | Текст сообщения |
|
||
|
||
### Доступные модели
|
||
|
||
OpenRouter поддерживает множество моделей. Популярные варианты:
|
||
|
||
- **OpenAI**: `openai/gpt-4`, `openai/gpt-3.5-turbo`
|
||
- **Anthropic**: `anthropic/claude-3-opus`, `anthropic/claude-3-haiku`
|
||
- **Google**: `google/gemini-pro`
|
||
- **Meta**: `meta-llama/llama-3-70b-instruct`
|
||
- **Mistral**: `mistralai/mistral-7b-instruct`
|
||
|
||
Полный список: [openrouter.ai/docs#models](https://openrouter.ai/docs#models)
|
||
|
||
### Примеры использования
|
||
|
||
#### Анализ метаданных аудиокниги
|
||
|
||
```go
|
||
req := &domain.LLMRequest{
|
||
Model: "openai/gpt-4",
|
||
Messages: []domain.LLMMessage{
|
||
{
|
||
Role: "system",
|
||
Content: "Ты — эксперт по метаданным аудиокниг.",
|
||
},
|
||
{
|
||
Role: "user",
|
||
Content: "Нормализуй автора: Иванов Петр Сергеевич → формат 'Фамилия Имя'",
|
||
},
|
||
},
|
||
Temperature: 0.3,
|
||
MaxTokens: 50,
|
||
}
|
||
```
|
||
|
||
#### Генерация описания
|
||
|
||
```go
|
||
req := &domain.LLMRequest{
|
||
Model: "anthropic/claude-3-haiku",
|
||
Messages: []domain.LLMMessage{
|
||
{
|
||
Role: "system",
|
||
Content: "Создавай краткие описания книг на русском языке.",
|
||
},
|
||
{
|
||
Role: "user",
|
||
Content: fmt.Sprintf("Создай описание для: %s - %s", author, title),
|
||
},
|
||
},
|
||
Temperature: 0.7,
|
||
MaxTokens: 200,
|
||
}
|
||
```
|
||
|
||
#### Валидация метаданных
|
||
|
||
Модуль поддерживает автоматическую валидацию метаданных через метод `ValidateMetadata`:
|
||
|
||
```go
|
||
// Создание клиента с промптом валидации
|
||
client := infrastructure.NewOpenRouterClient(infrastructure.OpenRouterConfig{
|
||
APIKey: "sk-or-v1-...",
|
||
Model: "openai/gpt-3.5-turbo",
|
||
Prompt: validationPrompt, // см. config.yaml
|
||
})
|
||
|
||
// Валидация метаданных
|
||
result, err := client.ValidateMetadata(ctx, "Иванов Петр Сергеевич", "Путешествие. Книга 1")
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
if result != nil {
|
||
if result.AuthorFixed {
|
||
fmt.Printf("Исправлен автор: %s\n", result.Author) // "Иванов Петр"
|
||
}
|
||
if result.TitleFixed {
|
||
fmt.Printf("Исправлено название: %s\n", result.Title) // "Путешествие"
|
||
}
|
||
fmt.Printf("Изменения: %s\n", result.Changes)
|
||
}
|
||
```
|
||
|
||
**Важно**: Валидация работает только если:
|
||
- Указан `api_key` в конфигурации
|
||
- Настроен `prompt` в конфигурации
|
||
|
||
Если `api_key` пустой, метод вернет `nil` и валидация не выполнится.
|
||
|
||
## Продвинутые возможности
|
||
|
||
### Контроль таймаутов
|
||
|
||
```go
|
||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||
defer cancel()
|
||
|
||
resp, err := client.GenerateCompletion(ctx, req)
|
||
```
|
||
|
||
### Обработка ошибок
|
||
|
||
```go
|
||
resp, err := client.GenerateCompletion(ctx, req)
|
||
if err != nil {
|
||
if ctx.Err() == context.DeadlineExceeded {
|
||
fmt.Println("Таймаут запроса")
|
||
} else {
|
||
fmt.Printf("Ошибка API: %v\n", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
if len(resp.Choices) == 0 {
|
||
fmt.Println("Пустой ответ от API")
|
||
return
|
||
}
|
||
```
|
||
|
||
### Настройка параметров генерации
|
||
|
||
```go
|
||
req := &domain.LLMRequest{
|
||
Model: "openai/gpt-3.5-turbo",
|
||
Messages: messages,
|
||
Temperature: 0.8, // Выше = более креативно
|
||
MaxTokens: 500, // Ограничение длины ответа
|
||
TopP: 0.9, // Nucleus sampling
|
||
FrequencyPenalty: 0.5, // Избегать повторов
|
||
PresencePenalty: 0.3, // Разнообразие тем
|
||
}
|
||
```
|
||
|
||
## Запуск примеров
|
||
|
||
В папке `examples/` находятся примеры использования:
|
||
|
||
### Базовое использование API
|
||
|
||
```bash
|
||
# Установите API ключ
|
||
export OPENROUTER_API_KEY="sk-or-v1-..."
|
||
|
||
# Запустите пример общего использования
|
||
go run examples/openrouter_example.go
|
||
```
|
||
|
||
### Валидация метаданных аудиокниг
|
||
|
||
```bash
|
||
# Запустите пример валидации метаданных
|
||
go run examples/validate_metadata_example.go
|
||
```
|
||
|
||
Этот пример демонстрирует:
|
||
- Исправление порядка "Имя Фамилия" → "Фамилия Имя"
|
||
- Удаление отчества из имени автора
|
||
- Очистку названий от номеров серий, томов, технических терминов
|
||
- Обработку различных вариантов некорректных метаданных
|
||
|
||
## Стоимость использования
|
||
|
||
OpenRouter работает по модели pay-as-you-go. Стоимость зависит от выбранной модели:
|
||
|
||
- GPT-3.5 Turbo: ~$0.002 / 1K токенов
|
||
- GPT-4: ~$0.03-0.06 / 1K токенов
|
||
- Claude 3 Haiku: ~$0.25 / 1M токенов
|
||
|
||
Следите за расходами на [openrouter.ai/activity](https://openrouter.ai/activity)
|
||
|
||
## Безопасность
|
||
|
||
⚠️ **Важно**: Никогда не коммитьте API ключи в репозиторий!
|
||
|
||
Используйте:
|
||
- Переменные окружения: `OPENROUTER_API_KEY`
|
||
- Файл `.env` (добавьте в `.gitignore`)
|
||
- Секреты CI/CD систем
|
||
|
||
## Лимиты и ограничения
|
||
|
||
- **Rate limiting**: OpenRouter имеет лимиты на количество запросов
|
||
- **Timeout**: Рекомендуется устанавливать таймаут 30-60 секунд
|
||
- **Max tokens**: Учитывайте лимиты контекста моделей (обычно 4K-128K токенов)
|
||
|
||
## Устранение неполадок
|
||
|
||
### Ошибка: "API key не указан"
|
||
|
||
Проверьте:
|
||
1. Ключ указан в `config.yaml`
|
||
2. Формат: `api_key: "sk-or-v1-..."`
|
||
3. Нет лишних пробелов или кавычек
|
||
|
||
### Ошибка: HTTP 401 Unauthorized
|
||
|
||
- Проверьте валидность API ключа на [openrouter.ai/keys](https://openrouter.ai/keys)
|
||
- Убедитесь, что ключ активен и не истёк
|
||
|
||
### Ошибка: HTTP 429 Too Many Requests
|
||
|
||
- Слишком много запросов, снизьте частоту
|
||
- Проверьте лимиты аккаунта
|
||
|
||
### Таймауты
|
||
|
||
- Увеличьте `timeout` в конфигурации
|
||
- Используйте более быстрые модели (например, `claude-3-haiku`)
|
||
- Уменьшите `max_tokens`
|
||
|
||
## Дополнительные ресурсы
|
||
|
||
- [OpenRouter Документация](https://openrouter.ai/docs)
|
||
- [Список моделей](https://openrouter.ai/docs#models)
|
||
- [Цены](https://openrouter.ai/docs#models)
|
||
- [API Reference](https://openrouter.ai/docs#api)
|