# Structured Output – Union событий API ## 📖 Описание `solution.py` – практическое задание, в котором из **сырого лога** (несколько строк разного формата) извлекаются типизированные события через **Pydantic v2** и **LangChain ≥1.0**. Задача закрепляет работу с **Union + Enums + StructuredOutput**: одна входная лента, разные схемы, явный `type`/`kind` для маршрутизации. > **Цель** – получить список типизированных событий `ApiEvent` из произвольного текста, а затем вывести их в табличном виде. --- ## 📦 Установка зависимостей ```bash # Создайте и активируйте виртуальное окружение (необязательно) python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate # Установите зависимости pip install langchain-core langchain-openai pydantic python-dotenv ``` > **Важно**: > * `langchain-openai` – подключает OpenAI API. > * Для работы с другими LLM (например, Ollama) замените `langchain-openai` на `langchain-ollama` и поправьте импорт в `solution.py`. > * В файле `.env` укажите переменную `OPENAI_API_KEY` (или `OLLAMA_BASE_URL` для Ollama). --- ## 🏃‍♂️ Запуск ### 1. Запуск по умолчанию (пример лога) ```bash python solution.py ``` > Вы увидите вывод, где каждая строка лога преобразована в объект `ApiEvent` и напечатана в виде таблицы: ``` Вход: 3 строки лога разного формата Выход: ┌───────┬───────────────────────┬────────┬───────────────┐ │ kind │ path │ status │ duration_ms │ ├───────┼───────────────────────┼────────┼───────────────┤ │ ok │ /api/v1/users │ 200 │ 123 │ │ error │ /api/v1/orders │ 404 │ 0 │ │ error │ /api/v1/payments │ 500 │ 0 │ └───────┴───────────────────────┴────────┴───────────────┘ ``` ### 2. Передача собственного текста ```bash python solution.py --text "GET /api/v1/products 200 45ms\nPOST /api/v1/cart 500 Internal Server Error" ``` > Скрипт разбивает входной текст на строки, отправляет каждую в LLM, получает типизированный объект и выводит таблицу. ### 3. Использование собственного файла лога ```bash python solution.py --file path/to/your.log ``` > Внутри `solution.py` реализована простая логика: если указан `--file`, то читается содержимое файла и используется в качестве входного текста. --- ## 📦 Структура проекта ``` . ├── solution.py # Основной скрипт ├── .env # Переменные окружения (OPENAI_API_KEY) └── README.md # Это файл ``` --- ## 🔧 Как это работает 1. **Модели** ```python class HttpOkEvent(BaseModel): kind: Literal["ok"] = Field(..., description="Тип события: OK") status: Literal[200] = Field(..., description="HTTP статус 200") path: str = Field(..., description="Запрошенный путь") duration_ms: int = Field(..., description="Время выполнения в миллисекундах") class HttpErrorEvent(BaseModel): kind: Literal["error"] = Field(..., description="Тип события: Ошибка") status: int = Field(..., description="HTTP статус 4xx/5xx") path: str = Field(..., description="Запрошенный путь") error_message: str = Field(..., description="Текст ошибки") ``` 2. **Union** ```python ApiEvent = Annotated[ Union[HttpOkEvent, HttpErrorEvent], Field(discriminator="kind") ] ``` 3. **Парсинг** * Для каждой строки лога создаётся промпт, который задаёт задачу LLM: «Преобразуй строку в JSON, соответствующий `ApiEvent`». * Используется `PydanticOutputParser` (или `with_structured_output`) для строгой валидации. 4. **Вывод** * Список `ApiEvent` сериализуется через `model_dump()` и печатается в табличном формате (можно заменить на `tabulate` или `pandas`). --- ## 📌 Пример использования в коде ```python from solution import parse_log, print_events log_text = """ GET /api/v1/users 200 123ms POST /api/v1/orders 404 Not Found GET /api/v1/payments 500 Internal Server Error """ events = parse_log(log_text) print_events(events) ``` --- ## 🤝 Вклад Если хотите улучшить скрипт (добавить поддержку новых форматов, улучшить промпт, добавить кэширование), создайте Pull Request. Убедитесь, что все тесты (если есть) проходят. --- ## 📄 Лицензия MIT © 2026 ---