# Kraken LLM Framework
Универсальный Python фреймворк для работы с большими языковыми моделями (LLM) с полной поддержкой OpenAI API.
## Обзор
Kraken LLM Framework предоставляет единый интерфейс для различных типов взаимодействия с LLM, включая стандартные запросы, потоковую передачу, структурированный вывод, мультимодальность и работу с речью.
### Ключевые особенности
- **Универсальный клиент**: UniversalLLMClient объединяет все возможности в едином интерфейсе
- **Полная поддержка OpenAI API**: chat completions, streaming, function calling, tool calling
- **Структурированный вывод**: Валидация Pydantic моделей с интеграцией Outlines и нативной поддержкой OpenAI
- **Асинхронность**: Построен на AsyncOpenAI для высокой производительности
- **Типобезопасность**: Полная поддержка type hints и IDE
- **Простая конфигурация**: Pydantic Settings с поддержкой переменных окружения
- **Расширяемость**: Архитектура плагинов для пользовательских функций и инструментов
- **Мультимодальность**: Поддержка текста, изображений, аудио и видео
- **Речевые технологии**: ASR (распознавание речи), TTS (синтез речи), диаризация спикеров
- **Рассуждающие модели**: Поддержка Chain of Thought и нативных thinking токенов
- **Адаптивность**: Автоматический выбор оптимального режима работы
- **Анализ возможностей**: Автоматическое определение возможностей моделей
## Установка
### Базовая установка
```bash
# Пакетом из PyPI
pip install kraken-llm
# Из исходников
git clone https://github.com/antonshalin76/kraken_llm
cd kraken-llm
pip install -e .
# С дополнительными зависимостями
pip install -e .[dev] # Для разработки
pip install -e .[all] # Все зависимости
```
### Системные требования
- Python 3.10+
- AsyncOpenAI 1.0.0+
- Pydantic 2.0.0+
- Outlines 0.0.30+
- Pillow 10.0.0+ (для работы с изображениями)
## Быстрый старт
### Простейший пример
```python
from kraken_llm import create_universal_client
async with create_universal_client() as client:
response = await client.chat_completion([
{"role": "user", "content": "Привет, мир!"}
])
print(response)
```
### Анализ возможностей моделей
Перед началом работы рекомендуется проанализировать возможности ваших моделей:
```bash
# Быстрый анализ
python3 model_capabilities_analyzer.py --quick
# Полный анализ с Markdown отчетом
python3 model_capabilities_analyzer.py --output markdown
# Через Makefile
make capabilities-analyze-quick
```
## Конфигурация
### Переменные окружения
Все параметры конфигурации могут быть заданы через переменные окружения с префиксом `LLM_`:
```bash
export LLM_ENDPOINT="http://localhost:8080"
export LLM_API_KEY="your-api-key"
export LLM_MODEL="chat"
export LLM_TEMPERATURE=0.7
export LLM_MAX_TOKENS=2000
```
### Файл .env
```env
LLM_ENDPOINT=http://localhost:8080
LLM_API_KEY=your-api-key
LLM_MODEL=chat
LLM_TEMPERATURE=0.7
LLM_MAX_TOKENS=2000
LLM_STREAM=false
LLM_OUTLINES_SO_MODE=true
```
### Класс LLMConfig
```python
from kraken_llm import LLMConfig
config = LLMConfig(
endpoint="http://localhost:8080",
api_key="your-api-key",
model="chat",
temperature=0.7,
max_tokens=2000
)
```
## Универсальный клиент
### Основные возможности
UniversalLLMClient - это универсальный клиент, который объединяет все возможности Kraken LLM в едином интерфейсе:
```python
from kraken_llm import (
create_universal_client,
create_basic_client,
create_advanced_client,
create_full_client,
UniversalCapability
)
# Базовый клиент (chat + streaming)
async with create_basic_client() as client:
response = await client.chat_completion([
{"role": "user", "content": "Привет!"}
])
# Продвинутый клиент (+ structured output, function calling, reasoning)
async with create_advanced_client() as client:
# Автоматический fallback для structured output
from pydantic import BaseModel
class Task(BaseModel):
title: str
priority: int
task = await client.chat_completion_structured([
{"role": "user", "content": "Создай задачу изучить Python"}
], response_model=Task)
# Полнофункциональный клиент (все возможности)
async with create_full_client() as client:
capabilities = client.get_available_capabilities()
print(f"Доступные возможности: {capabilities}")
```
### Создание на основе анализа возможностей
```python
from kraken_llm import create_universal_client_from_report
# Анализируем возможности модели
from model_capabilities_analyzer import ModelCapabilitiesAnalyzer
analyzer = ModelCapabilitiesAnalyzer()
report = await analyzer.analyze_all_models()
# Создаем оптимальный клиент
async with create_universal_client_from_report(report) as client:
# Клиент автоматически настроен под возможности модели
response = await client.chat_completion([
{"role": "user", "content": "Тест"}
])
```
### Кастомная конфигурация
```python
from kraken_llm import create_universal_client, UniversalCapability
# Выбираем только нужные возможности
capabilities = {
UniversalCapability.CHAT_COMPLETION,
UniversalCapability.STREAMING,
UniversalCapability.STRUCTURED_OUTPUT,
UniversalCapability.FUNCTION_CALLING
}
async with create_universal_client(capabilities=capabilities) as client:
# Используем только выбранные возможности
pass
```
## Типы клиентов
### Специализированные клиенты
Kraken предоставляет специализированные клиенты для различных задач:
```python
from kraken_llm import (
create_standard_client, # Базовые операции
create_streaming_client, # Потоковая передача
create_structured_client, # Структурированный вывод
create_reasoning_client, # Рассуждающие модели
create_multimodal_client, # Мультимодальность
create_adaptive_client, # Адаптивный режим
create_asr_client, # Речевые технологии
create_embeddings_client, # Векторные представления
)
# Стандартный клиент
async with create_standard_client() as client:
response = await client.chat_completion([
{"role": "user", "content": "Привет"}
])
# Потоковый клиент
async with create_streaming_client() as client:
async for chunk in client.chat_completion_stream([
{"role": "user", "content": "Расскажи историю"}
]):
print(chunk, end="", flush=True)
```
### Фабрика клиентов
```python
from kraken_llm import ClientFactory, create_client
# Автоматический выбор типа клиента
client = create_client(
stream=True # Автоматически выберет StreamingLLMClient
)
# Явное указание типа
client = ClientFactory.create_client(
client_type="structured",
endpoint="http://localhost:8080"
)
```
## Структурированный вывод
### Автоматический fallback
UniversalLLMClient автоматически выбирает оптимальный режим для structured output:
```python
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
skills: list[str]
async with create_universal_client() as client:
# Автоматически пробует:
# 1. Нативный OpenAI structured output
# 2. Outlines режим
# 3. Fallback через JSON parsing
person = await client.chat_completion_structured([
{"role": "user", "content": "Создай профиль разработчика"}
], response_model=Person)
```
### Режимы работы
```python
async with create_structured_client() as client:
# Принудительное использование Outlines
person = await client.chat_completion_structured(
messages=[{"role": "user", "content": "Создай профиль"}],
response_model=Person,
mode="outlines"
)
# Использование нативного режима OpenAI
person = await client.chat_completion_structured(
messages=[{"role": "user", "content": "Создай профиль"}],
response_model=Person,
mode="native"
)
```
## Рассуждающие модели
### Chain of Thought
```python
from kraken_llm import create_reasoning_client, ReasoningConfig
config = ReasoningConfig(
model_type="prompt_based",
enable_cot=True,
max_reasoning_steps=10
)
async with create_reasoning_client(reasoning_config=config) as client:
response = await client.reasoning_completion([
{"role": "user", "content": "Реши: 15 * 23 + 45"}
], problem_type="math")
# Доступ к шагам рассуждения
for step in response.steps:
print(f"Шаг {step.step_number}: {step.thought}")
```
### Native Thinking
```python
config = ReasoningConfig(
model_type="native_thinking",
enable_thinking=True,
thinking_max_tokens=5000
)
async with create_reasoning_client(reasoning_config=config) as client:
response = await client.reasoning_completion([
{"role": "user", "content": "Объясни квантовую физику"}
])
# Доступ к thinking блокам
if response.thinking_blocks:
for block in response.thinking_blocks:
print(f"Thinking: {block.content}")
```
## Мультимодальность
### Анализ изображений
```python
from kraken_llm import create_multimodal_client
from pathlib import Path
async with create_multimodal_client() as client:
# Анализ изображения
response = await client.vision_completion(
text_prompt="Опиши что видишь на изображении",
images="photo.jpg"
)
# Анализ нескольких изображений
response = await client.vision_completion(
text_prompt="Сравни эти изображения",
images=["photo1.jpg", "photo2.jpg"]
)
```
### Работа с аудио и видео
```python
# Обработка аудио
response = await client.audio_completion(
text_prompt="Проанализируй содержание аудио",
audio_files="recording.wav",
task_type="analysis"
)
# Анализ видео
response = await client.video_completion(
text_prompt="Опиши что происходит в видео",
video_files="video.mp4"
)
```
## Речевые технологии
### ASR Client
```python
from kraken_llm import create_asr_client
async with create_asr_client() as client:
# Распознавание речи
result = await client.speech_to_text(
audio_file="recording.wav",
language="ru"
)
# Синтез речи
audio_data = await client.text_to_speech(
text="Привет, как дела?",
voice="alloy"
)
# Диаризация спикеров
diarization = await client.speaker_diarization(
audio_file="meeting.wav",
num_speakers=3
)
```
## Function и Tool Calling
### Регистрация функций
```python
def get_weather(city: str) -> str:
"""Получить погоду в указанном городе."""
return f"В городе {city} сейчас солнечно, +20°C"
async with create_universal_client() as client:
# Регистрация функции
client.register_function(
name="get_weather",
function=get_weather,
description="Получить текущую погоду"
)
# Использование
response = await client.chat_completion([
{"role": "user", "content": "Какая погода в Москве?"}
])
```
### Декораторы для функций
```python
from kraken_llm.tools import register_function
@register_function("calculate", "Выполнить математические вычисления")
async def calculate(expression: str) -> float:
"""Безопасное вычисление математических выражений."""
return eval(expression) # В реальности используйте безопасный парсер
```
## Векторные представления
### Embeddings Client
```python
from kraken_llm import create_embeddings_client
async with create_embeddings_client() as client:
# Получение embeddings
embeddings = await client.create_embeddings([
"Первый текст для векторизации",
"Второй текст для векторизации"
])
# Поиск похожих текстов
similar = await client.similarity_search(
query_text="поисковый запрос",
candidate_texts=["текст 1", "текст 2", "текст 3"],
top_k=2
)
```
## Потоковые операции
### Streaming Handler
```python
from kraken_llm.streaming import StreamHandler, StreamAggregator
# Обработка потока
handler = StreamHandler()
aggregator = StreamAggregator()
async for chunk_data in handler.process_stream(response_stream):
if chunk_data["type"] == "content":
aggregator.add_content(chunk_data["data"])
elif chunk_data["type"] == "function_call_complete":
print(f"Function call: {chunk_data['data']}")
# Получение полного контента
full_content = aggregator.get_aggregated_content()
```
## Анализ возможностей моделей
### ModelCapabilitiesAnalyzer
```python
from model_capabilities_analyzer import ModelCapabilitiesAnalyzer
# Создание анализатора
analyzer = ModelCapabilitiesAnalyzer()
# Быстрый анализ
report = await analyzer.analyze_all_models(quick_mode=True)
# Полный анализ
report = await analyzer.analyze_all_models(quick_mode=False)
# Сохранение отчета
analyzer.save_report(report, output_format="markdown", filename="capabilities.md")
analyzer.save_report(report, output_format="json", filename="capabilities.json")
```
### Использование результатов анализа
```python
# Создание клиента на основе анализа
async with create_universal_client_from_report(report, model_name="my_model") as client:
# Клиент настроен под возможности конкретной модели
capabilities = client.get_available_capabilities()
print(f"Подтвержденные возможности: {capabilities}")
```
## Обработка ошибок
### Иерархия исключений
```python
from kraken_llm.exceptions import (
KrakenError, # Базовое исключение
APIError, # Ошибки API
ValidationError, # Ошибки валидации
NetworkError, # Сетевые ошибки
AuthenticationError, # Ошибки аутентификации
RateLimitError, # Превышение лимитов
)
try:
response = await client.chat_completion([
{"role": "user", "content": "Тест"}
])
except RateLimitError as e:
print(f"Превышен лимит запросов: {e}")
print(f"Повторить через: {e.retry_after} секунд")
except AuthenticationError as e:
print(f"Ошибка аутентификации: {e}")
except ValidationError as e:
print(f"Ошибка валидации: {e}")
for detail in e.context.get("error_details", []):
print(f"Поле {detail['field']}: {detail['message']}")
except KrakenError as e:
print(f"Общая ошибка Kraken: {e}")
```
## Утилиты
### Работа с медиа файлами
```python
from kraken_llm.utils.media import MediaUtils
# Валидация медиа файла
validation = MediaUtils.validate_media_file(
"image.jpg",
media_type="image",
max_size=10 * 1024 * 1024
)
# Изменение размера изображения
result = MediaUtils.resize_image(
"large_image.jpg",
"resized_image.jpg",
max_width=1024,
max_height=1024
)
# Создание data URL
data_url = MediaUtils.create_data_url("image.jpg")
```
## Тестирование
### Запуск тестов
```bash
# Все тесты
make test
# Только unit тесты
make test-unit
# Только integration тесты
make test-integration
# С покрытием
make test-coverage
```
### Тестирование возможностей
```python
async with create_universal_client() as client:
# Автоматическое тестирование всех возможностей
test_results = await client.test_capabilities()
for capability, result in test_results.items():
status = "✅" if result else "❌"
print(f"{status} {capability}")
```
## Примеры использования
В папке `examples/` находятся подробные примеры:
- `quick_universal_example.py` - Быстрый старт с универсальным клиентом
- `universal_client_example.py` - Подробные примеры использования
- `complete_workflow_example.py` - Полный рабочий процесс
- `adaptive_capabilities_example.py` - Адаптивные возможности
- `structured_output_fallback_example.py` - Структурированный вывод с fallback
- `reasoning_example.py` - Рассуждающие модели
- `multimodal_example.py` - Мультимодальные операции
- `streaming_example.py` - Потоковые операции
- `function_tool_example.py` - Функции и инструменты
## Архитектура
### Структура проекта
```
kraken_llm/
├── client/ # LLM клиенты
│ ├── base.py # Базовый клиент
│ ├── standard.py # Стандартный клиент
│ ├── streaming.py # Потоковый клиент
│ ├── structured.py # Структурированный вывод
│ ├── reasoning.py # Рассуждающие модели
│ ├── multimodal.py # Мультимодальный клиент
│ ├── adaptive.py # Адаптивный клиент
│ ├── asr.py # Речевые технологии
│ ├── embeddings.py # Векторные представления
│ ├── universal.py # Универсальный клиент
│ └── factory.py # Фабрика клиентов
├── tools/ # Система функций и инструментов
├── streaming/ # Потоковые операции
├── structured/ # Структурированный вывод
├── utils/ # Утилиты (медиа, логирование)
├── exceptions/ # Обработка ошибок
└── models/ # Модели данных
```
### Принципы архитектуры
1. **Модульность**: Каждый компонент имеет четко определенную ответственность
2. **Расширяемость**: Легко добавлять новые типы клиентов и функциональность
3. **Типобезопасность**: Полная поддержка type hints во всех компонентах
4. **Асинхронность**: Все операции построены на async/await
5. **Конфигурируемость**: Гибкая система настроек через Pydantic Settings
6. **Обработка ошибок**: Иерархическая система исключений с контекстом
7. **Автоопределение**: Автоматический выбор подходящего клиента через фабрику
## Лицензия
MIT License - делайте что хотите ;-). См. файл [LICENSE](LICENSE) для подробностей.
## Поддержка
- Примеры: [examples/](examples/)
- Тесты: [tests/](tests/)
Raw data
{
"_id": null,
"home_page": null,
"name": "kraken-llm",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "ai, async, llm, machine-learning, openai, streaming",
"author": null,
"author_email": "Anton Shalin <anton.shalin@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/d0/e7/298deff580ccebeee6f4772122058e3b6529947571de805fd88d84b00ec7/kraken_llm-0.1.1.post1.tar.gz",
"platform": null,
"description": "# Kraken LLM Framework\n\n\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 Python \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u044f\u0437\u044b\u043a\u043e\u0432\u044b\u043c\u0438 \u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 (LLM) \u0441 \u043f\u043e\u043b\u043d\u043e\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 OpenAI API.\n\n## \u041e\u0431\u0437\u043e\u0440\n\nKraken LLM Framework \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0434\u0438\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 LLM, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u0443\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434, \u043c\u0443\u043b\u044c\u0442\u0438\u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0440\u0435\u0447\u044c\u044e.\n\n### \u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438\n\n- **\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442**: UniversalLLMClient \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432 \u0435\u0434\u0438\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435\n- **\u041f\u043e\u043b\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 OpenAI API**: chat completions, streaming, function calling, tool calling\n- **\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434**: \u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f Pydantic \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439 Outlines \u0438 \u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 OpenAI\n- **\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c**: \u041f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 AsyncOpenAI \u0434\u043b\u044f \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438\n- **\u0422\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c**: \u041f\u043e\u043b\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 type hints \u0438 IDE\n- **\u041f\u0440\u043e\u0441\u0442\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f**: Pydantic Settings \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\n- **\u0420\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u043e\u0441\u0442\u044c**: \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432\n- **\u041c\u0443\u043b\u044c\u0442\u0438\u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c**: \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0442\u0435\u043a\u0441\u0442\u0430, \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u0430\u0443\u0434\u0438\u043e \u0438 \u0432\u0438\u0434\u0435\u043e\n- **\u0420\u0435\u0447\u0435\u0432\u044b\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438**: ASR (\u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u0447\u0438), TTS (\u0441\u0438\u043d\u0442\u0435\u0437 \u0440\u0435\u0447\u0438), \u0434\u0438\u0430\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043f\u0438\u043a\u0435\u0440\u043e\u0432\n- **\u0420\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438**: \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 Chain of Thought \u0438 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0445 thinking \u0442\u043e\u043a\u0435\u043d\u043e\u0432\n- **\u0410\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c**: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u044b\u0431\u043e\u0440 \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0435\u0436\u0438\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b\n- **\u0410\u043d\u0430\u043b\u0438\u0437 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439**: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u043c\u043e\u0434\u0435\u043b\u0435\u0439\n\n## \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430\n\n### \u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430\n\n```bash\n# \u041f\u0430\u043a\u0435\u0442\u043e\u043c \u0438\u0437 PyPI\npip install kraken-llm\n\n# \u0418\u0437 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u043e\u0432\ngit clone https://github.com/antonshalin76/kraken_llm\ncd kraken-llm\npip install -e .\n\n# \u0421 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438\npip install -e .[dev] # \u0414\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438\npip install -e .[all] # \u0412\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438\n```\n\n### \u0421\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\n\n- Python 3.10+\n- AsyncOpenAI 1.0.0+\n- Pydantic 2.0.0+\n- Outlines 0.0.30+\n- Pillow 10.0.0+ (\u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438)\n\n## \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\n\n### \u041f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440\n\n```python\nfrom kraken_llm import create_universal_client\n\nasync with create_universal_client() as client:\n response = await client.chat_completion([\n {\"role\": \"user\", \"content\": \"\u041f\u0440\u0438\u0432\u0435\u0442, \u043c\u0438\u0440!\"}\n ])\n print(response)\n```\n\n### \u0410\u043d\u0430\u043b\u0438\u0437 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u043c\u043e\u0434\u0435\u043b\u0435\u0439\n\n\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439:\n\n```bash\n# \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437\npython3 model_capabilities_analyzer.py --quick\n\n# \u041f\u043e\u043b\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437 \u0441 Markdown \u043e\u0442\u0447\u0435\u0442\u043e\u043c\npython3 model_capabilities_analyzer.py --output markdown\n\n# \u0427\u0435\u0440\u0435\u0437 Makefile\nmake capabilities-analyze-quick\n```\n\n## \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\n\n### \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\n\n\u0412\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u0434\u0430\u043d\u044b \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c `LLM_`:\n\n```bash\nexport LLM_ENDPOINT=\"http://localhost:8080\"\nexport LLM_API_KEY=\"your-api-key\"\nexport LLM_MODEL=\"chat\"\nexport LLM_TEMPERATURE=0.7\nexport LLM_MAX_TOKENS=2000\n```\n\n### \u0424\u0430\u0439\u043b .env\n\n```env\nLLM_ENDPOINT=http://localhost:8080\nLLM_API_KEY=your-api-key\nLLM_MODEL=chat\nLLM_TEMPERATURE=0.7\nLLM_MAX_TOKENS=2000\nLLM_STREAM=false\nLLM_OUTLINES_SO_MODE=true\n```\n\n### \u041a\u043b\u0430\u0441\u0441 LLMConfig\n\n```python\nfrom kraken_llm import LLMConfig\n\nconfig = LLMConfig(\n endpoint=\"http://localhost:8080\",\n api_key=\"your-api-key\",\n model=\"chat\",\n temperature=0.7,\n max_tokens=2000\n)\n```\n\n## \u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\n\n### \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n\nUniversalLLMClient - \u044d\u0442\u043e \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 Kraken LLM \u0432 \u0435\u0434\u0438\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435:\n\n```python\nfrom kraken_llm import (\n create_universal_client,\n create_basic_client,\n create_advanced_client,\n create_full_client,\n UniversalCapability\n)\n\n# \u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 (chat + streaming)\nasync with create_basic_client() as client:\n response = await client.chat_completion([\n {\"role\": \"user\", \"content\": \"\u041f\u0440\u0438\u0432\u0435\u0442!\"}\n ])\n\n# \u041f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 (+ structured output, function calling, reasoning)\nasync with create_advanced_client() as client:\n # \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 fallback \u0434\u043b\u044f structured output\n from pydantic import BaseModel\n \n class Task(BaseModel):\n title: str\n priority: int\n \n task = await client.chat_completion_structured([\n {\"role\": \"user\", \"content\": \"\u0421\u043e\u0437\u0434\u0430\u0439 \u0437\u0430\u0434\u0430\u0447\u0443 \u0438\u0437\u0443\u0447\u0438\u0442\u044c Python\"}\n ], response_model=Task)\n\n# \u041f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 (\u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438)\nasync with create_full_client() as client:\n capabilities = client.get_available_capabilities()\n print(f\"\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438: {capabilities}\")\n```\n\n### \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439\n\n```python\nfrom kraken_llm import create_universal_client_from_report\n\n# \u0410\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043c\u043e\u0434\u0435\u043b\u0438\nfrom model_capabilities_analyzer import ModelCapabilitiesAnalyzer\n\nanalyzer = ModelCapabilitiesAnalyzer()\nreport = await analyzer.analyze_all_models()\n\n# \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\nasync with create_universal_client_from_report(report) as client:\n # \u041a\u043b\u0438\u0435\u043d\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043f\u043e\u0434 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043c\u043e\u0434\u0435\u043b\u0438\n response = await client.chat_completion([\n {\"role\": \"user\", \"content\": \"\u0422\u0435\u0441\u0442\"}\n ])\n```\n\n### \u041a\u0430\u0441\u0442\u043e\u043c\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\n\n```python\nfrom kraken_llm import create_universal_client, UniversalCapability\n\n# \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0443\u0436\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\ncapabilities = {\n UniversalCapability.CHAT_COMPLETION,\n UniversalCapability.STREAMING,\n UniversalCapability.STRUCTURED_OUTPUT,\n UniversalCapability.FUNCTION_CALLING\n}\n\nasync with create_universal_client(capabilities=capabilities) as client:\n # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n pass\n```\n\n## \u0422\u0438\u043f\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432\n\n### \u0421\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b\n\nKraken \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447:\n\n```python\nfrom kraken_llm import (\n create_standard_client, # \u0411\u0430\u0437\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\n create_streaming_client, # \u041f\u043e\u0442\u043e\u043a\u043e\u0432\u0430\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430\n create_structured_client, # \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434\n create_reasoning_client, # \u0420\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438\n create_multimodal_client, # \u041c\u0443\u043b\u044c\u0442\u0438\u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c\n create_adaptive_client, # \u0410\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c\n create_asr_client, # \u0420\u0435\u0447\u0435\u0432\u044b\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438\n create_embeddings_client, # \u0412\u0435\u043a\u0442\u043e\u0440\u043d\u044b\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n)\n\n# \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\nasync with create_standard_client() as client:\n response = await client.chat_completion([\n {\"role\": \"user\", \"content\": \"\u041f\u0440\u0438\u0432\u0435\u0442\"}\n ])\n\n# \u041f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\nasync with create_streaming_client() as client:\n async for chunk in client.chat_completion_stream([\n {\"role\": \"user\", \"content\": \"\u0420\u0430\u0441\u0441\u043a\u0430\u0436\u0438 \u0438\u0441\u0442\u043e\u0440\u0438\u044e\"}\n ]):\n print(chunk, end=\"\", flush=True)\n```\n\n### \u0424\u0430\u0431\u0440\u0438\u043a\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432\n\n```python\nfrom kraken_llm import ClientFactory, create_client\n\n# \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u044b\u0431\u043e\u0440 \u0442\u0438\u043f\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\nclient = create_client(\n stream=True # \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u0431\u0435\u0440\u0435\u0442 StreamingLLMClient\n)\n\n# \u042f\u0432\u043d\u043e\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u0435 \u0442\u0438\u043f\u0430\nclient = ClientFactory.create_client(\n client_type=\"structured\",\n endpoint=\"http://localhost:8080\"\n)\n```\n\n## \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434\n\n### \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 fallback\n\nUniversalLLMClient \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0434\u043b\u044f structured output:\n\n```python\nfrom pydantic import BaseModel\n\nclass Person(BaseModel):\n name: str\n age: int\n skills: list[str]\n\nasync with create_universal_client() as client:\n # \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u043e\u0431\u0443\u0435\u0442:\n # 1. \u041d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 OpenAI structured output\n # 2. Outlines \u0440\u0435\u0436\u0438\u043c\n # 3. Fallback \u0447\u0435\u0440\u0435\u0437 JSON parsing\n person = await client.chat_completion_structured([\n {\"role\": \"user\", \"content\": \"\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\"}\n ], response_model=Person)\n```\n\n### \u0420\u0435\u0436\u0438\u043c\u044b \u0440\u0430\u0431\u043e\u0442\u044b\n\n```python\nasync with create_structured_client() as client:\n # \u041f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Outlines\n person = await client.chat_completion_structured(\n messages=[{\"role\": \"user\", \"content\": \"\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c\"}],\n response_model=Person,\n mode=\"outlines\"\n )\n \n # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0440\u0435\u0436\u0438\u043c\u0430 OpenAI\n person = await client.chat_completion_structured(\n messages=[{\"role\": \"user\", \"content\": \"\u0421\u043e\u0437\u0434\u0430\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c\"}],\n response_model=Person,\n mode=\"native\"\n )\n```\n\n## \u0420\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438\n\n### Chain of Thought\n\n```python\nfrom kraken_llm import create_reasoning_client, ReasoningConfig\n\nconfig = ReasoningConfig(\n model_type=\"prompt_based\",\n enable_cot=True,\n max_reasoning_steps=10\n)\n\nasync with create_reasoning_client(reasoning_config=config) as client:\n response = await client.reasoning_completion([\n {\"role\": \"user\", \"content\": \"\u0420\u0435\u0448\u0438: 15 * 23 + 45\"}\n ], problem_type=\"math\")\n \n # \u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0448\u0430\u0433\u0430\u043c \u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0435\u043d\u0438\u044f\n for step in response.steps:\n print(f\"\u0428\u0430\u0433 {step.step_number}: {step.thought}\")\n```\n\n### Native Thinking\n\n```python\nconfig = ReasoningConfig(\n model_type=\"native_thinking\",\n enable_thinking=True,\n thinking_max_tokens=5000\n)\n\nasync with create_reasoning_client(reasoning_config=config) as client:\n response = await client.reasoning_completion([\n {\"role\": \"user\", \"content\": \"\u041e\u0431\u044a\u044f\u0441\u043d\u0438 \u043a\u0432\u0430\u043d\u0442\u043e\u0432\u0443\u044e \u0444\u0438\u0437\u0438\u043a\u0443\"}\n ])\n \n # \u0414\u043e\u0441\u0442\u0443\u043f \u043a thinking \u0431\u043b\u043e\u043a\u0430\u043c\n if response.thinking_blocks:\n for block in response.thinking_blocks:\n print(f\"Thinking: {block.content}\")\n```\n\n## \u041c\u0443\u043b\u044c\u0442\u0438\u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c\n\n### \u0410\u043d\u0430\u043b\u0438\u0437 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439\n\n```python\nfrom kraken_llm import create_multimodal_client\nfrom pathlib import Path\n\nasync with create_multimodal_client() as client:\n # \u0410\u043d\u0430\u043b\u0438\u0437 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\n response = await client.vision_completion(\n text_prompt=\"\u041e\u043f\u0438\u0448\u0438 \u0447\u0442\u043e \u0432\u0438\u0434\u0438\u0448\u044c \u043d\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0438\",\n images=\"photo.jpg\"\n )\n \n # \u0410\u043d\u0430\u043b\u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439\n response = await client.vision_completion(\n text_prompt=\"\u0421\u0440\u0430\u0432\u043d\u0438 \u044d\u0442\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\",\n images=[\"photo1.jpg\", \"photo2.jpg\"]\n )\n```\n\n### \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0430\u0443\u0434\u0438\u043e \u0438 \u0432\u0438\u0434\u0435\u043e\n\n```python\n# \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0430\u0443\u0434\u0438\u043e\nresponse = await client.audio_completion(\n text_prompt=\"\u041f\u0440\u043e\u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0430\u0443\u0434\u0438\u043e\",\n audio_files=\"recording.wav\",\n task_type=\"analysis\"\n)\n\n# \u0410\u043d\u0430\u043b\u0438\u0437 \u0432\u0438\u0434\u0435\u043e\nresponse = await client.video_completion(\n text_prompt=\"\u041e\u043f\u0438\u0448\u0438 \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 \u0432\u0438\u0434\u0435\u043e\",\n video_files=\"video.mp4\"\n)\n```\n\n## \u0420\u0435\u0447\u0435\u0432\u044b\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438\n\n### ASR Client\n\n```python\nfrom kraken_llm import create_asr_client\n\nasync with create_asr_client() as client:\n # \u0420\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u0447\u0438\n result = await client.speech_to_text(\n audio_file=\"recording.wav\",\n language=\"ru\"\n )\n \n # \u0421\u0438\u043d\u0442\u0435\u0437 \u0440\u0435\u0447\u0438\n audio_data = await client.text_to_speech(\n text=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u043a\u0430\u043a \u0434\u0435\u043b\u0430?\",\n voice=\"alloy\"\n )\n \n # \u0414\u0438\u0430\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043f\u0438\u043a\u0435\u0440\u043e\u0432\n diarization = await client.speaker_diarization(\n audio_file=\"meeting.wav\",\n num_speakers=3\n )\n```\n\n## Function \u0438 Tool Calling\n\n### \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0439\n\n```python\ndef get_weather(city: str) -> str:\n \"\"\"\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u0433\u043e\u0434\u0443 \u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c \u0433\u043e\u0440\u043e\u0434\u0435.\"\"\"\n return f\"\u0412 \u0433\u043e\u0440\u043e\u0434\u0435 {city} \u0441\u0435\u0439\u0447\u0430\u0441 \u0441\u043e\u043b\u043d\u0435\u0447\u043d\u043e, +20\u00b0C\"\n\nasync with create_universal_client() as client:\n # \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438\n client.register_function(\n name=\"get_weather\",\n function=get_weather,\n description=\"\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u043f\u043e\u0433\u043e\u0434\u0443\"\n )\n \n # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\n response = await client.chat_completion([\n {\"role\": \"user\", \"content\": \"\u041a\u0430\u043a\u0430\u044f \u043f\u043e\u0433\u043e\u0434\u0430 \u0432 \u041c\u043e\u0441\u043a\u0432\u0435?\"}\n ])\n```\n\n### \u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b \u0434\u043b\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0439\n\n```python\nfrom kraken_llm.tools import register_function\n\n@register_function(\"calculate\", \"\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f\")\nasync def calculate(expression: str) -> float:\n \"\"\"\u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439.\"\"\"\n return eval(expression) # \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440\n```\n\n## \u0412\u0435\u043a\u0442\u043e\u0440\u043d\u044b\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n\n### Embeddings Client\n\n```python\nfrom kraken_llm import create_embeddings_client\n\nasync with create_embeddings_client() as client:\n # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 embeddings\n embeddings = await client.create_embeddings([\n \"\u041f\u0435\u0440\u0432\u044b\u0439 \u0442\u0435\u043a\u0441\u0442 \u0434\u043b\u044f \u0432\u0435\u043a\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438\",\n \"\u0412\u0442\u043e\u0440\u043e\u0439 \u0442\u0435\u043a\u0441\u0442 \u0434\u043b\u044f \u0432\u0435\u043a\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438\"\n ])\n \n # \u041f\u043e\u0438\u0441\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\n similar = await client.similarity_search(\n query_text=\"\u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\",\n candidate_texts=[\"\u0442\u0435\u043a\u0441\u0442 1\", \"\u0442\u0435\u043a\u0441\u0442 2\", \"\u0442\u0435\u043a\u0441\u0442 3\"],\n top_k=2\n )\n```\n\n## \u041f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\n\n### Streaming Handler\n\n```python\nfrom kraken_llm.streaming import StreamHandler, StreamAggregator\n\n# \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u043e\u0442\u043e\u043a\u0430\nhandler = StreamHandler()\naggregator = StreamAggregator()\n\nasync for chunk_data in handler.process_stream(response_stream):\n if chunk_data[\"type\"] == \"content\":\n aggregator.add_content(chunk_data[\"data\"])\n elif chunk_data[\"type\"] == \"function_call_complete\":\n print(f\"Function call: {chunk_data['data']}\")\n\n# \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430\nfull_content = aggregator.get_aggregated_content()\n```\n\n## \u0410\u043d\u0430\u043b\u0438\u0437 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u043c\u043e\u0434\u0435\u043b\u0435\u0439\n\n### ModelCapabilitiesAnalyzer\n\n```python\nfrom model_capabilities_analyzer import ModelCapabilitiesAnalyzer\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430\nanalyzer = ModelCapabilitiesAnalyzer()\n\n# \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437\nreport = await analyzer.analyze_all_models(quick_mode=True)\n\n# \u041f\u043e\u043b\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437\nreport = await analyzer.analyze_all_models(quick_mode=False)\n\n# \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043e\u0442\u0447\u0435\u0442\u0430\nanalyzer.save_report(report, output_format=\"markdown\", filename=\"capabilities.md\")\nanalyzer.save_report(report, output_format=\"json\", filename=\"capabilities.json\")\n```\n\n### \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0430\u043d\u0430\u043b\u0438\u0437\u0430\n\n```python\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0430\u043d\u0430\u043b\u0438\u0437\u0430\nasync with create_universal_client_from_report(report, model_name=\"my_model\") as client:\n # \u041a\u043b\u0438\u0435\u043d\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043f\u043e\u0434 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438\n capabilities = client.get_available_capabilities()\n print(f\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438: {capabilities}\")\n```\n\n## \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a\n\n### \u0418\u0435\u0440\u0430\u0440\u0445\u0438\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439\n\n```python\nfrom kraken_llm.exceptions import (\n KrakenError, # \u0411\u0430\u0437\u043e\u0432\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\n APIError, # \u041e\u0448\u0438\u0431\u043a\u0438 API\n ValidationError, # \u041e\u0448\u0438\u0431\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438\n NetworkError, # \u0421\u0435\u0442\u0435\u0432\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438\n AuthenticationError, # \u041e\u0448\u0438\u0431\u043a\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438\n RateLimitError, # \u041f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u0438\u0435 \u043b\u0438\u043c\u0438\u0442\u043e\u0432\n)\n\ntry:\n response = await client.chat_completion([\n {\"role\": \"user\", \"content\": \"\u0422\u0435\u0441\u0442\"}\n ])\nexcept RateLimitError as e:\n print(f\"\u041f\u0440\u0435\u0432\u044b\u0448\u0435\u043d \u043b\u0438\u043c\u0438\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432: {e}\")\n print(f\"\u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437: {e.retry_after} \u0441\u0435\u043a\u0443\u043d\u0434\")\nexcept AuthenticationError as e:\n print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438: {e}\")\nexcept ValidationError as e:\n print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438: {e}\")\n for detail in e.context.get(\"error_details\", []):\n print(f\"\u041f\u043e\u043b\u0435 {detail['field']}: {detail['message']}\")\nexcept KrakenError as e:\n print(f\"\u041e\u0431\u0449\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 Kraken: {e}\")\n```\n\n## \u0423\u0442\u0438\u043b\u0438\u0442\u044b\n\n### \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043c\u0435\u0434\u0438\u0430 \u0444\u0430\u0439\u043b\u0430\u043c\u0438\n\n```python\nfrom kraken_llm.utils.media import MediaUtils\n\n# \u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043c\u0435\u0434\u0438\u0430 \u0444\u0430\u0439\u043b\u0430\nvalidation = MediaUtils.validate_media_file(\n \"image.jpg\",\n media_type=\"image\",\n max_size=10 * 1024 * 1024\n)\n\n# \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\nresult = MediaUtils.resize_image(\n \"large_image.jpg\",\n \"resized_image.jpg\",\n max_width=1024,\n max_height=1024\n)\n\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 data URL\ndata_url = MediaUtils.create_data_url(\"image.jpg\")\n```\n\n## \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\n\n### \u0417\u0430\u043f\u0443\u0441\u043a \u0442\u0435\u0441\u0442\u043e\u0432\n\n```bash\n# \u0412\u0441\u0435 \u0442\u0435\u0441\u0442\u044b\nmake test\n\n# \u0422\u043e\u043b\u044c\u043a\u043e unit \u0442\u0435\u0441\u0442\u044b\nmake test-unit\n\n# \u0422\u043e\u043b\u044c\u043a\u043e integration \u0442\u0435\u0441\u0442\u044b\nmake test-integration\n\n# \u0421 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435\u043c\nmake test-coverage\n```\n\n### \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439\n\n```python\nasync with create_universal_client() as client:\n # \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439\n test_results = await client.test_capabilities()\n \n for capability, result in test_results.items():\n status = \"\u2705\" if result else \"\u274c\"\n print(f\"{status} {capability}\")\n```\n\n## \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\n\n\u0412 \u043f\u0430\u043f\u043a\u0435 `examples/` \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b:\n\n- `quick_universal_example.py` - \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442 \u0441 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c\n- `universal_client_example.py` - \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\n- `complete_workflow_example.py` - \u041f\u043e\u043b\u043d\u044b\u0439 \u0440\u0430\u0431\u043e\u0447\u0438\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\n- `adaptive_capabilities_example.py` - \u0410\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n- `structured_output_fallback_example.py` - \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u0441 fallback\n- `reasoning_example.py` - \u0420\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438\n- `multimodal_example.py` - \u041c\u0443\u043b\u044c\u0442\u0438\u043c\u043e\u0434\u0430\u043b\u044c\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\n- `streaming_example.py` - \u041f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\n- `function_tool_example.py` - \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b\n\n## \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430\n\n### \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\n\n```\nkraken_llm/\n\u251c\u2500\u2500 client/ # LLM \u043a\u043b\u0438\u0435\u043d\u0442\u044b\n\u2502 \u251c\u2500\u2500 base.py # \u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\n\u2502 \u251c\u2500\u2500 standard.py # \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\n\u2502 \u251c\u2500\u2500 streaming.py # \u041f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\n\u2502 \u251c\u2500\u2500 structured.py # \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434\n\u2502 \u251c\u2500\u2500 reasoning.py # \u0420\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438\n\u2502 \u251c\u2500\u2500 multimodal.py # \u041c\u0443\u043b\u044c\u0442\u0438\u043c\u043e\u0434\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\n\u2502 \u251c\u2500\u2500 adaptive.py # \u0410\u0434\u0430\u043f\u0442\u0438\u0432\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\n\u2502 \u251c\u2500\u2500 asr.py # \u0420\u0435\u0447\u0435\u0432\u044b\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438\n\u2502 \u251c\u2500\u2500 embeddings.py # \u0412\u0435\u043a\u0442\u043e\u0440\u043d\u044b\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n\u2502 \u251c\u2500\u2500 universal.py # \u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\n\u2502 \u2514\u2500\u2500 factory.py # \u0424\u0430\u0431\u0440\u0438\u043a\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432\n\u251c\u2500\u2500 tools/ # \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432\n\u251c\u2500\u2500 streaming/ # \u041f\u043e\u0442\u043e\u043a\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\n\u251c\u2500\u2500 structured/ # \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434\n\u251c\u2500\u2500 utils/ # \u0423\u0442\u0438\u043b\u0438\u0442\u044b (\u043c\u0435\u0434\u0438\u0430, \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435)\n\u251c\u2500\u2500 exceptions/ # \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a\n\u2514\u2500\u2500 models/ # \u041c\u043e\u0434\u0435\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\n```\n\n### \u041f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b\n\n1. **\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u044c**: \u041a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0438\u043c\u0435\u0435\u0442 \u0447\u0435\u0442\u043a\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u0443\u044e \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u044c\n2. **\u0420\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u043e\u0441\u0442\u044c**: \u041b\u0435\u0433\u043a\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0442\u0438\u043f\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c\n3. **\u0422\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c**: \u041f\u043e\u043b\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 type hints \u0432\u043e \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445\n4. **\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c**: \u0412\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u043d\u0430 async/await\n5. **\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c**: \u0413\u0438\u0431\u043a\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0447\u0435\u0440\u0435\u0437 Pydantic Settings\n6. **\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a**: \u0418\u0435\u0440\u0430\u0440\u0445\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u0441 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043e\u043c\n7. **\u0410\u0432\u0442\u043e\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435**: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u044b\u0431\u043e\u0440 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0447\u0435\u0440\u0435\u0437 \u0444\u0430\u0431\u0440\u0438\u043a\u0443\n\n## \u041b\u0438\u0446\u0435\u043d\u0437\u0438\u044f\n\nMIT License - \u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 ;-). \u0421\u043c. \u0444\u0430\u0439\u043b [LICENSE](LICENSE) \u0434\u043b\u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0435\u0439.\n\n## \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430\n\n- \u041f\u0440\u0438\u043c\u0435\u0440\u044b: [examples/](examples/)\n- \u0422\u0435\u0441\u0442\u044b: [tests/](tests/)",
"bugtrack_url": null,
"license": null,
"summary": "Universal LLM framework with full OpenAI API support",
"version": "0.1.1.post1",
"project_urls": {
"Homepage": "https://github.com/antonshalin76/kraken_llm",
"Issues": "https://github.com/antonshalin76/kraken_llm/issues",
"Repository": "https://github.com/antonshalin76/kraken_llm"
},
"split_keywords": [
"ai",
" async",
" llm",
" machine-learning",
" openai",
" streaming"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "bfd72433ae55b3deec3d8c48319118ef3d11a298dc18beeb8bba4b9050bd9959",
"md5": "113de7be3322c78a20aa44c8985545ab",
"sha256": "e216a1a09e43f4fccb250cec831b165278b28131d552e78fcb2bac542e75aacf"
},
"downloads": -1,
"filename": "kraken_llm-0.1.1.post1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "113de7be3322c78a20aa44c8985545ab",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 160201,
"upload_time": "2025-09-14T19:32:50",
"upload_time_iso_8601": "2025-09-14T19:32:50.241650Z",
"url": "https://files.pythonhosted.org/packages/bf/d7/2433ae55b3deec3d8c48319118ef3d11a298dc18beeb8bba4b9050bd9959/kraken_llm-0.1.1.post1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "d0e7298deff580ccebeee6f4772122058e3b6529947571de805fd88d84b00ec7",
"md5": "907d05cb8727df45ba4394fd88ed4ed1",
"sha256": "83240b1749aac43a750b19e376217ad16c54c8473d5cf53b13ad33cffe07d878"
},
"downloads": -1,
"filename": "kraken_llm-0.1.1.post1.tar.gz",
"has_sig": false,
"md5_digest": "907d05cb8727df45ba4394fd88ed4ed1",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 337356,
"upload_time": "2025-09-14T19:32:51",
"upload_time_iso_8601": "2025-09-14T19:32:51.568847Z",
"url": "https://files.pythonhosted.org/packages/d0/e7/298deff580ccebeee6f4772122058e3b6529947571de805fd88d84b00ec7/kraken_llm-0.1.1.post1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-14 19:32:51",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "antonshalin76",
"github_project": "kraken_llm",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "openai",
"specs": []
},
{
"name": "outlines",
"specs": []
},
{
"name": "pydantic",
"specs": []
},
{
"name": "pydantic-settings",
"specs": []
},
{
"name": "httpx",
"specs": []
},
{
"name": "Pillow",
"specs": []
},
{
"name": "numpy",
"specs": []
},
{
"name": "python-dotenv",
"specs": []
},
{
"name": "typing-extensions",
"specs": []
}
],
"lcname": "kraken-llm"
}