О проекте
Команда: Hahathon Devs
Кейс HackAlem.ai: Торговля — Электрокомплект · ekt.kz
Стадия: Доведённый продукт
Задача кейса: AI-консультант по каталогу ekt.kz: товары, аналоги, цены, остатки, корзина
Суть
Витрина и чат-консультант по каталогу ekt.kz: поиск по артикулу, характеристики, цена, остаток, подбор аналогов при нулевом остатке, условия оплаты и доставки, корзина с явным подтверждением. Поддерживаются вложения (XLSX/DOCX/PDF/изображения).
Как устроено
Next.js-proxy передаёт запросы в FastAPI (сессии, CSRF). Отдельный worker забирает задачи из SQLite-очереди. Оркестратор ассистента получает от LLM структурированный JSON-план с намерением и позициями, а факты о цене и остатке берутся и проверяются на сервере из каталога (PostgreSQL с pg_trgm или демо-SQLite). Аналоги подбираются серверным сравнением характеристик с исключением критических противоречий. Корзина меняется только после отдельного подтверждения с проверкой версии, цены и остатка и защитой ключом идемпотентности. Файлы проходят карантин, сканер ClamAV и изолированный парсер.
Ключевые решения
- structured JSON plan (Responses API)
- bounded assistant loop с верификацией ответа
- PostgreSQL pg_trgm поиск
- сравнение атрибутов для аналогов
- idempotent cart confirmation
- durable worker queue с lease
- ClamAV scanning
- OCR
Особенности
Зрелая архитектура на портах и адаптерах: PostgreSQL или SQLite, OpenAI или Claude, адаптер EKT API. Модель только планирует, а все факты и изменения корзины проверяет сервер. Безопасная загрузка документов через карантин, антивирус и изолированный парсер. Smoke-скрипт проверяет, что корзина не меняется до подтверждения и повторный запрос не дублирует добавление.
Стек
Python · FastAPI · Pydantic · Uvicorn · HTTPX · Next.js 15 · React 19 · TypeScript · PostgreSQL · SQLite · ClamAV · pypdf · python-docx · openpyxl
Модели
OpenAI (Responses API) · Anthropic Claude
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-4a724051-hahathon-devs.
README
README.mdЭКТ — каталог и ИИ-ассистент
Команда Hahathon Devs, HACKALEM AI 2026. Кейс: ИИ-ассистент для чата на сайте ekt.kz.
Прототип помогает покупателю найти электротехнические товары, проверить характеристики, цену и остаток, подобрать доступные аналоги и подготовить корзину. Витрина, чат и проверка корзины используют один источник каталога. Модель определяет намерение и выбирает позиции; фактические сведения и разрешение на изменение корзины проверяет сервер.
Основной каталог команды — подготовленная PostgreSQL-база Нурсултана. Для её использования выполните установку зависимостей, затем раздел «Подключение уже заполненного PostgreSQL» и запустите API, worker и frontend. Не запускайте импорт, генерацию товаров или сброс рабочей БД. Описанный ниже автономный режим нужен только экспертам без доступа к PostgreSQL; он не заменяет основной каталог. Демонстрационная корзина не оформляет заказ на EKT.
Возможности и границы
| Требование | Реализация |
|---|---|
| Информация по артикулу | Поиск, цена, характеристики, остаток; сертификат показывается только при наличии ссылки в источнике |
| Аналоги при нулевом остатке | Серверное сравнение характеристик, исключение критических противоречий, объяснение; при недостатке данных — уточнение |
| Условия покупки | Темы оплаты/доставки из файла знаний с URL источников |
| Подтверждение покупки | Предложение не меняет корзину. Отдельное подтверждение проверяет версию, срок, цену, остаток и количество с учётом корзины |
| Ссылка на корзину | Возвращается после подтверждения. Повтор запроса не дублирует добавление |
| Вложения | TXT, CSV, XLSX, DOCX, PDF, JPG/PNG: проверка типа/размера/структуры, scanner, изолированный parser; фото/сканы требуют vision |
| Интерфейс | Категории, фильтры, поиск, сортировка, карточки, избранное, сравнение, подборка, чат и история в пределах сессии |
Без OpenAI работают серверные сценарии по артикулам и известным намерениям. Свободный анализ запроса и изображений требует включённой модели. Реальные платные вызовы OpenAI и работа настоящего антивируса пока не подтверждены внешней приёмкой.
Реальное оформление заказа EKT, оплата, аккаунт/B2B, передача заявки менеджеру и гарантированно полный каталог не реализованы. API корзины партнёр не предоставил. Проект предназначен для локальной проверки MVP; APP_ENV=production блокируется до завершения эксплуатационной подготовки.
Архитектура
flowchart LR
Browser[Витрина и чат] --> Next[Next.js: серверный proxy]
Next --> API[FastAPI: сессии, CSRF, контракты]
API --> State[(SQLite: сессии, очередь и демо-корзина)]
State --> Worker[Отдельный worker]
Worker --> Assistant[Оркестратор ассистента]
Assistant --> Planner[OpenAI: структурированный план]
API --> Catalog[Интерфейс каталога]
Assistant --> Catalog
Catalog --> PG[(PostgreSQL: EKT)]
Catalog --> Demo[(SQLite: синтетические товары)]
Assistant --> Actions[Предложение и подтверждение]
Actions --> Catalog
Actions --> State
API --> Files[Приватные файлы: scanner и parser]
Files --> Assistant
PostgreSQL и SQLite-каталог — альтернативные режимы. В catalog_db товары читаются из PostgreSQL; SQLite продолжает хранить сессии, очередь, предложения и демо-корзину. Удаление SQLite не обновляет PostgreSQL и не заменяет выбор правильного режима.
| Путь | Ответственность |
|---|---|
frontend/src/components/storefront/ | Витрина, карточки, фильтры и корзина |
frontend/src/components/assistant-chat.tsx | Чат, вложения, ответы и подтверждения |
frontend/src/lib/api.ts | Типы, HTTP-транспорт и сессия |
frontend/src/app/api/v1/[...path]/route.ts | Серверный proxy; внешние ключи не передаются браузеру |
backend/app/api/, core/ | HTTP-контракты, конфигурация, безопасность и ошибки |
backend/app/modules/catalog/, search/ | Нормализация, качество данных, поиск и аналоги |
backend/app/modules/assistant/ | Оркестрация и ответ из проверенных данных |
backend/app/modules/actions/ | Предложение, подтверждение и идемпотентность |
backend/app/modules/chat/, worker.py | Диалоги, сохранённая очередь и обработка задач |
backend/app/modules/documents/ | Карантин, изолированный разбор, доступ по сессии |
backend/app/adapters/, bootstrap.py | Адаптеры PostgreSQL/SQLite/EKT/OpenAI и сборка зависимостей |
data/, docs/, test-files/ | Данные демо, документация и тестовые вложения |
Контракты отделяют доменную логику от хранилищ и провайдеров. API и worker запускаются отдельно; задачи сохраняются, используют lease и таймауты. Подтверждение защищено ключом операции и транзакцией. Ошибка источника не превращается в выдуманный остаток или успешный заказ.
SQLite-очередь рассчитана на один хост и общий файл. Для горизонтального масштабирования нужно совместно перенести сессии, очередь, предложения и корзину в общее транзакционное хранилище, а файлы — в приватное объектное хранилище. Независимые SQLite-копии на разных серверах не обеспечивают общую сессию. Подробнее: архитектура, интеграция, контракты.
Технологии и зависимости
| Компонент | Требование |
|---|---|
| Python | 3.11+; проверено на 3.11, нужен venv |
| Backend | FastAPI, Pydantic, Uvicorn, HTTPX; версии в backend/requirements.lock |
| Frontend | Node.js 22.18+ (проверено на 24), npm; Next.js 15.5.26, React 19, TypeScript 5.9 |
| Каталог | SQLite для демо; PostgreSQL 14+ с pg_trgm для реальной БД |
| Документы | pypdf, pypdfium2, python-docx, openpyxl, Pillow; устанавливаются с backend |
| ИИ | OpenAI Responses API, структурированный JSON-план |
| Антивирус | ClamAV с актуальными сигнатурами, если включены загрузки |
Нужны Git и доступ к пакетным реестрам при первой установке. Для автономного демо после установки внешние API не нужны. Docker, Redis и LibreOffice для запуска приложения не требуются. На Linux при отсутствии venv установите python3-venv средствами ОС.
Установка и запуск
1. Получить проект и подготовить окружение
git clone https://github.com/BAITC-Hacks/hack-4a724051-hahathon-devs.git
cd hack-4a724051-hahathon-devs
Используйте ветку/коммит, представленные командой на проверку. В нём должны быть этот README и scripts/smoke_test.py. Команды ниже выполняются из корня клона, если явно не указан другой каталог.
PowerShell:
if (!(Test-Path .env)) { Copy-Item .env.example .env }
if (!(Test-Path frontend/.env.local)) { Copy-Item frontend/.env.example frontend/.env.local }
Linux/macOS:
test -f .env || cp .env.example .env
test -f frontend/.env.local || cp frontend/.env.example frontend/.env.local
В корневом .env для автономной проверки:
APP_ENV=development
INTEGRATION_MODE=synthetic
LLM_ENABLED=false
LLM_DATA_POLICY_ACCEPTED=false
UPLOADS_ENABLED=false
EKT_LIVE_REFRESH=false
Остальные настройки оставьте из .env.example; ключи и DATABASE_URL не нужны. Если LOCAL_DB_PATH уже задан, используйте отдельный абсолютный путь для демо. По умолчанию файл backend/var/participant2.sqlite3 создаётся автоматически. API и worker должны использовать один файл.
В frontend/.env.local:
BACKEND_INTERNAL_URL=http://127.0.0.1:8000
Переменные терминала имеют приоритет над .env. Для чистой проверки используйте новые терминалы без старых значений INTEGRATION_MODE, LOCAL_DB_PATH, DATABASE_URL.
2. Установить зависимости
PowerShell, из корня:
python -m venv backend/.venv
backend/.venv/Scripts/python.exe -m pip install -r backend/requirements.lock -e './backend[dev]'
cd frontend
npm ci
cd ..
Linux/macOS, из корня:
python3 -m venv backend/.venv
backend/.venv/bin/python -m pip install -r backend/requirements.lock -e './backend[dev]'
cd frontend
npm ci
cd ..
Активация venv не нужна. На Windows при блокировке npm.ps1 используйте npm.cmd.
3. Запустить три процесса
Откройте три терминала в корне репозитория. На Linux/macOS замените .venv/Scripts/python.exe на .venv/bin/python.
Терминал 1 — API:
cd backend
.venv/Scripts/python.exe -m uvicorn app.main:create_app --factory --host 127.0.0.1 --port 8000 --no-proxy-headers
Терминал 2 — worker:
cd backend
.venv/Scripts/python.exe -m app.worker
Терминал 3 — frontend:
cd frontend
npm run build
npm run start -- --hostname 127.0.0.1 --port 3000
Для разработки вместо build и start используйте npm run dev. Не запускайте оба режима на одном порту. Сборка Next.js не означает включение APP_ENV=production в backend.
- Витрина и чат: http://127.0.0.1:3000.
- Swagger: http://127.0.0.1:8000/docs; актуальный OpenAPI: http://127.0.0.1:8000/openapi.json.
- Процесс: http://127.0.0.1:8000/health/live; хранилище: http://127.0.0.1:8000/health/ready.
Ожидается 281 демонстрационный товар с пометкой демо. /health/ready не подтверждает работу worker/OpenAI — это проверяется сценарием ниже. Остановка: Ctrl+C в каждом терминале. После изменения .env перезапустите API и worker; после frontend-настроек — Next.js.
Проверка основного сценария
Автоматически через HTTP
При работающих трёх процессах из корня:
backend/.venv/Scripts/python.exe scripts/smoke_test.py
На Linux/macOS используйте backend/.venv/bin/python. Скрипт через frontend-прокси создаёт отдельную сессию, проверяет каталог, ответ worker, неизменность корзины до подтверждения, подтверждение, безопасный повтор и ссылку корзины. В конце удаляет свою тестовую сессию. Успех — строки PASS, ошибка — ненулевой код. Скрипт отказывается работать с адаптером реального оформления заказа.
В браузере
- Найдите
990100003_. В неизменённом демо: цена 1 350 ₸, продающий остаток 2 шт, характеристики и тестовый сертификат. - В чате отправьте
990100003_, затемхарактеристики. Ответ и карточка должны использовать одинаковые факты. - Отправьте
990100015_, затем отдельным сообщениеместь аналог?без артикула. Ожидаются аналоги предыдущего товара с объяснением. Повтора есть ещё аналоги?сохраняет исходный товар, а не переключается на предложенную замену. В новом пустом диалоге тот же вопрос должен запросить артикул. Неоднозначная выдача также требует уточнения. - Отправьте
условия доставки: ответ из файла знаний со ссылкой на источник. - В новом диалоге отправьте
Купить 990100003_ 2 шт. Предложение: 2 × 1 350 = 2 700 ₸. До подтверждения корзина пуста. - Подтвердите предложение. Ссылка открывает корзину: 2 шт, 2 700 ₸. Повтор не увеличивает количество; запрос сверх остатка отклоняется.
- Перезагрузите страницу: история текущей сессии восстанавливается. Избранное и сравнение сохраняются в браузере.
- Для вложений сначала включите scanner по инструкции ниже. Десять тестовых файлов содержат ожидаемые результаты; отклонение файла больше лимита — успешная проверка защиты.
Подключение уже заполненного PostgreSQL
Для базы, подготовленной участником команды, не запускайте повторный импорт и не очищайте таблицы. В корневом .env:
INTEGRATION_MODE=catalog_db
DATABASE_URL=postgresql://USER:[скрыто]@HOST:5432/ekt
EKT_LIVE_REFRESH=false
Замените placeholders собственными значениями. Спецсимволы логина/пароля URI должны быть percent-encoded. Нужна доступная схема проекта с таблицами products, catalog_sync_runs и миграциями. Строка подключения — секрет; она не публикуется в Git или frontend. EKT Basic Auth не является доступом PostgreSQL.
Перезапустите API и worker. Каталог читается из products PostgreSQL; SQLite остаётся для чата/очереди/демо-корзины. Удаление этого файла уничтожит локальные сессии и историю. Новые записи PostgreSQL видны при следующих запросах без пересоздания SQLite. Перезапуск нужен при смене режима/подключения.
Проверка состояния без импорта, из backend:
.venv/Scripts/python.exe -m app.catalog_cli status
Сквозная проверка из корня с артикулом доступного товара вашей БД:
backend/.venv/Scripts/python.exe scripts/smoke_test.py --article 200300285_
Этот пример проверен в API партнёра; его наличие в вашей БД и остаток могут измениться. Для новой пустой БД подготовка описана в docs/data-and-db.md. Миграции и импорт изменяют целевую БД; не выполняйте их при проверке уже подготовленной базы без необходимости.
Почему встречались счётчики 0 и 54
В старом демо-файле было 65 товаров, из них 54 — низковольтная аппаратура. После объединения актуального main набор содержит 281 товар во всех 12 разделах. Старую локальную копию требуется обновить; перезапуск сам по себе не перезаписывает заполненный SQLite-каталог. Счётчики соответствуют выбранному источнику. Ноль в частичном наборе не означает отсутствие товара на EKT. Категория определяется по URL, поэтому промо-разделы/архив могут образовать дополнительные ветки. Проект не выдумывает товары для заполнения пустых категорий.
Используемые данные
data/synthetic/ekt_products.json: 281 демонстрационная карточка: 68 полностью синтетических и 213 на основе названий, изображений и характеристик из API EKT. У всех ID, артикулы, цены и остатки демонстрационные. Сертификаты вdata/synthetic/certificates/также тестовые.data/curated/purchase_terms.json: редакционная подборка условий с публичных страниц EKT и URL источников; условия требуют периодической актуализации.- Реальный API:
GET https://ekt.kz/api/products?page=NиGET https://ekt.kz/api/products/detail?id=ID, HTTP Basic Auth. Доступы передаются отдельно, реальные карточки/БД в Git не включены. - Список содержит до 20 позиций;
count— число на странице, а не общее число товаров. При проверке далёкая страница вернула первую страницу: номер страницы не подтверждает полноту обхода. - Детальная карточка содержит
description,properties, общую величину остатка и склады. Нормализатор сопоставляет характеристики, помечает противоречия, исключает непродающие склады. Неподтверждённые данные не подменяются точными. - В API нет подтверждённого интерфейса корзины/заказов. Корзина прототипа локальная даже при реальном каталоге.
Контракт EKT: docs/api.md. HTTP-контракт приложения: docs/openapi.json; актуальную схему запущенной версии проверяйте по /openapi.json.
Параметры окружения
API и worker читают корневой .env; Next.js — frontend/.env.local. Шаблоны: .env.example, frontend/.env.example. Секреты исключены .gitignore, не имеют префикса NEXT_PUBLIC_.
| Параметр | Назначение / демо-значение |
|---|---|
APP_ENV | development; production в текущем MVP запрещён |
INTEGRATION_MODE | synthetic, catalog_db или unavailable |
LOCAL_DB_PATH | Один абсолютный SQLite-путь API/worker; по умолчанию backend/var/participant2.sqlite3 |
DATABASE_URL | PostgreSQL URI, обязателен для catalog_db |
CATALOG_DB_SEED_DEMO | true; в catalog_db досевает демо-товары в пустую базу. Для подготовленной базы партнёра ставьте false |
EKT_API_USER, EKT_API_PASSWORD | Basic Auth EKT; пустые в шаблоне |
EKT_LIVE_REFRESH | false; true проверяет цену/остаток перед корзиной и обновляет карточку PostgreSQL |
LLM_ENABLED, LLM_API_KEY | false, пустой серверный ключ |
LLM_PROVIDER | openai |
LLM_MODEL, LLM_ALLOWED_MODELS | gpt-6-sol, ["gpt-6-sol"]; нужен доступ проекта OpenAI к модели |
LLM_REASONING_EFFORT | medium |
LLM_DATA_POLICY_ACCEPTED | false; true после принятия оператором политики передачи данных |
LLM_TIMEOUT_SECONDS, LLM_MAX_OUTPUT_TOKENS | 45, 8192 |
LLM_SESSION_DAILY_TOKENS, LLM_SITE_DAILY_TOKENS | 100000, 1000000; токенные, не денежные лимиты |
AI_SERVICES_PROVIDER | openai или nvidia; провайдер OCR и эмбеддингов |
NVIDIA_API_KEY, NVIDIA_DATA_POLICY_ACCEPTED | Нужны только при AI_SERVICES_PROVIDER=nvidia |
OCR_ENABLED, OCR_MODEL | false, пусто; распознавание сканов и фото. Пустая модель — значение провайдера по умолчанию |
SEMANTIC_SEARCH_ENABLED, EMBED_MODEL | false, пусто; поиск по смыслу. Модель обязана совпадать с моделью индекса |
SEMANTIC_INDEX_PATH | data/synthetic/embeddings.json; файл индекса эмбеддингов |
AI_SITE_DAILY_CALLS, AI_SESSION_DAILY_CALLS | 3000, 200; лимиты вызовов AI-сервиса в сутки |
WORKER_TIMEOUT_SECONDS, WORKER_LEASE_SECONDS | 75, 120; timeout worker > timeout модели с запасом, lease > timeout worker |
UPLOADS_ENABLED, SCANNER_COMMAND | false, []; включение требует настоящего антивируса |
ASSETS_ROOT | Приватные загрузки: по умолчанию backend/var/assets |
ALLOWED_ORIGINS, ALLOWED_HOSTS | JSON-массивы разрешённых origin/host; локальные значения по умолчанию в Settings |
COOKIE_SECURE | false для локального HTTP; HTTPS требует отдельной настройки |
BACKEND_INTERNAL_URL | Только frontend server: http://127.0.0.1:8000 |
FRONTEND_ORIGIN | Только frontend server: точный публичный origin за доверенным reverse proxy |
CLAMD_SOCKET и DOCUMENTS_ALLOW_UNSCANNED относятся к неподключённому PostgreSQL-адаптеру документов. Они не включают HTTP-загрузки и не обходят scanner текущего DocumentsService.
Включение OpenAI
Заполните LLM_API_KEY, установите LLM_ENABLED=true, LLM_DATA_POLICY_ACCEPTED=true в корневом .env. Остальные настройки Sol есть в шаблоне. Перезапустите API и worker. Вызовы оплачиваются вашим проектом OpenAI; настройте его бюджет. Локальные mock-тесты не подтверждают качество/время ответа реальной модели.
Включение файлов
Установите ClamAV, обновите сигнатуры и проверьте команду из окружения backend. Затем:
UPLOADS_ENABLED=true
SCANNER_COMMAND=["clamscan","--no-summary"]
Если scanner не в PATH, укажите полный путь: например ["C:/Program Files/ClamAV/clamscan.exe","--no-summary"]. Перезапустите API/worker. Для отправки вложения внешней модели нужно также согласие пользователя в чате. Сбой scanner оставляет файл недоступным для анализа.
На Windows вместо ClamAV можно использовать работающий Microsoft Defender с актуальными сигнатурами:
UPLOADS_ENABLED=true
SCANNER_COMMAND=["windows-defender"]
LLM_ENABLED=true
LLM_DATA_POLICY_ACCEPTED=true
Ключ LLM_API_KEY заполните только в локальном .env. Backend сам находит установленную версию Defender и проверяет каждый файл; ошибка проверки блокирует анализ. После изменения настроек перезапустите API и worker, обновите страницу. В /api/v1/capabilities ожидаются upload: ready, documents: ready, llm: openai. Прикрепите файл скрепкой, отметьте разрешение на передачу выбранных файлов в OpenAI и отправьте «Найди товары по артикулам из вложенного файла». Sol получает изображения страниц скана и фотографии, а цены и наличие ответа берутся из каталога. Без разрешения доступен только локальный текстовый разбор, изображение не распознаётся.
Проверка всех десяти файлов через запущенный сайт (реальный антивирус и parser):
backend/.venv/Scripts/python.exe scripts/smoke_attachments.py
# Дополнительно три платных запроса OpenAI на синтетических DOCX, PDF-скане и фото:
backend/.venv/Scripts/python.exe scripts/smoke_attachments.py --analyze
Тест проверяет найденные артикулы и удаляет свои сессии. Суммарный текст вложений для одного вызова ограничен 24 000 UTF-8 байт; при превышении чат показывает предупреждение и предлагает разделить документ. Это предотвращает отказ модели из-за слишком большого контекста; полный разбор всех страниц больших файлов не гарантируется.
Лимиты: 3 вложения на сообщение, 10 МиБ на файл, 200 строк таблиц, 20 листов XLSX, 20 страниц PDF, 40 000 символов; до 4 страниц PDF без текста передаются как изображения. При обрезке показывается предупреждение. .doc/.xls не поддерживаются — используйте .docx/.xlsx. Полный анализ больших документов требует обработки по частям. Подробная инструкция.
Включение OCR и семантического поиска
Обе возможности обращаются к внешнему AI-сервису и по умолчанию выключены. Провайдер задаёт AI_SERVICES_PROVIDER: значение openai использует уже настроенные LLM_API_KEY и LLM_DATA_POLICY_ACCEPTED, значение nvidia — отдельные NVIDIA_API_KEY и NVIDIA_DATA_POLICY_ACCEPTED. Без ключа и согласия на передачу данных запуск останавливается с сообщением, в котором названы недостающие переменные.
OCR читает сканы и фотографии без текстового слоя — те файлы, для которых локального разбора не хватает:
AI_SERVICES_PROVIDER=openai
OCR_ENABLED=true
Семантический поиск отвечает на запрос по смыслу, а не только по артикулу и подстроке. Ему нужен заранее посчитанный индекс эмбеддингов. Из backend:
.venv/Scripts/python.exe -m app.catalog_cli embed --source synthetic
Для каталога PostgreSQL укажите --source db, путь файла меняется через --out. После этого в корневом .env:
SEMANTIC_SEARCH_ENABLED=true
Индекс читается из SEMANTIC_INDEX_PATH. EMBED_MODEL при запуске обязана совпадать с моделью, которой считался индекс: при расхождении выдача становится бессмысленной, а не пустой. Пустые OCR_MODEL и EMBED_MODEL означают модель провайдера по умолчанию.
Расход ограничен числом вызовов в сутки — AI_SITE_DAILY_CALLS и AI_SESSION_DAILY_CALLS. Это лимиты вызовов, а не денег: бюджет настраивается в кабинете провайдера. Перезапустите API и worker; в /api/v1/capabilities поля ocr и semantic_search перейдут из disabled в рабочее состояние.
Проверки качества
Из backend (Linux/macOS: заменить Scripts/python.exe на bin/python):
.venv/Scripts/python.exe -m pip check
.venv/Scripts/python.exe -m pytest
Из frontend:
npm run typecheck
npm run test:proxy
npm run build
Из корня — локальная проверка вложений без LLM/scanner:
backend/.venv/Scripts/python.exe scripts/verify_assistant_fixtures.py
PostgreSQL-тесты требуют отдельной одноразовой TEST_DATABASE_URL: они пересоздают схему public. Никогда не указывайте рабочую БД. Без неё 20 PostgreSQL-тестов пропускаются.
Проверка 23.09.2026: 254 backend-теста прошли, 20 PostgreSQL-тестов пропущены; 5 тестов proxy и TypeScript прошли. Подробности и ограничения: отчёт. Тесты не гарантируют отсутствия всех ошибок; внешние зависимости проверяются отдельно.
Диагностика запуска
| Симптом | Действие |
|---|---|
| Каталог недоступен | Для демо: INTEGRATION_MODE=synthetic, перезапуск API/worker |
| Остались 65 товаров после обновления PostgreSQL | Проверить catalog_db, DATABASE_URL, переменные терминала и BACKEND_INTERNAL_URL; перезапустить и обновить страницу |
catalog_db requires DATABASE_URL | Указать PostgreSQL URI; EKT Basic Auth не заменяет его |
| PostgreSQL timeout / нет таблицы | Проверить хост/порт, пользователя и схему; использовать подготовленную базу |
| Сообщение в очереди | Запустить worker с тем же .env и SQLite-путём, что API |
| 403 / CSRF | Использовать один адрес 127.0.0.1:3000, не смешивать localhost и IP; обновить сессию |
| 503 при загрузке | Проверить включение, путь scanner, сигнатуры |
| Модель недоступна | Проверить ключ, доступ к модели, квоту, политику; каталог может работать в резервном режиме |
| OCR или семантический поиск не включаются | Проверить AI_SERVICES_PROVIDER, ключ и согласие на передачу данных; для поиска — что индекс посчитан catalog_cli embed и EMBED_MODEL совпадает с моделью индекса |
| Порт занят | Остановить старый экземпляр; при смене порта согласованно изменить proxy и allowed origins |
| SQLite занят / API и worker видят разное | Проверить общий абсолютный путь, права записи, лишние старые процессы |
Перед сдачей выполните установку и основной сценарий на чистом клоне именно сдаваемого коммита. Незакоммиченные локальные файлы эксперту недоступны. Не публикуйте .env, приватные файлы и БД; доступы передавайте организатору отдельно.
Авторы коммитов
История коммитов
- 2026-09-23 17:55README: OCR, семантический поиск и недостающие переменныеTomiris
- 2026-09-23 17:48Resolve visible image articles before requesting clarificationTomiris
- 2026-09-23 17:48Merge remote-tracking branch 'origin/main' into codex/participant-2-backendTomiris
- 2026-09-23 17:47OCR and semantic search on OpenAI or NVIDIAGoslinuly
- 2026-09-23 17:47Search: understand how customers write and table rows from filesGoslinuly
- 2026-09-23 17:47Semantic index for the demo catalog and docsGoslinuly
- 2026-09-23 17:44Fix attachment analysis, ground document lookup and bound model contextTomiris
- 2026-09-23 17:40Поддержка Microsoft Defender как сканера вложенийTomiris
Все коммиты (57)
- 2026-09-23 17:40Презентация проекта для защитыTomiris
- 2026-09-23 17:32NVIDIA API client with a shared daily call budgetGoslinuly
- 2026-09-23 17:32Read photos and scans with NVIDIA when the customer allows itGoslinuly
- 2026-09-23 17:32Semantic catalog search on NVIDIA embeddingsGoslinuly
- 2026-09-23 17:18Clearer DATABASE_URL errors: say when it is needed and the no-DB optionGoslinuly
- 2026-09-23 17:11Add PostgreSQL catalog dump and make synthetic the default modeGoslinuly
- 2026-09-23 17:10Merge remote-tracking branch 'origin/main' into codex/participant-2-backendTomiris
- 2026-09-23 17:09catalog_db works on an empty PostgreSQL out of the boxGoslinuly
- 2026-09-23 17:06fix: retain product context for assistant alternatives and document verificationTomiris
- 2026-09-23 17:00Session logout, per-session rate limiting and upload test fixturesTomiris
- 2026-09-23 16:54Merge remote-tracking branch 'origin/main' into codex/participant-2-backendTomiris
- 2026-09-23 16:42README: stack and versions, one run guide for macOS, Linux, WindowsGoslinuly
- 2026-09-23 16:32Fill every catalog section with real ekt.kz itemsGoslinuly
- 2026-09-23 16:32Storefront: show Russian titles for all subsectionsGoslinuly
- 2026-09-23 16:23Readable product names and illustrations for the demo catalogGoslinuly
- 2026-09-23 16:23Serve product illustrations at /api/v1/product-imagesGoslinuly
- 2026-09-23 16:13Demo data and profiles so every out-of-stock item has an analogGoslinuly
- 2026-09-23 16:12fix: restore assistant chat after completed turns and verify storefront flowsTomiris
- 2026-09-23 16:11Do not flag 400V AC vs 400V as a conflictGoslinuly
- 2026-09-23 16:10Parser subprocess: do not crash on macOS memory limitGoslinuly
- 2026-09-23 16:04Merge origin/main: PostgreSQL catalog, EKT API client and storefront UITomiris
- 2026-09-23 15:43frontend: assistant chat component and catalog browse testTomiris
- 2026-09-23 15:40frontend apiTomiris
- 2026-09-23 15:34Backend and assistentTomiris
- 2026-09-23 15:26docs: catalog DB, import CLI, documents and what is leftGoslinuly
- 2026-09-23 15:26Fewer false attribute conflicts on real EKT cardsGoslinuly
- 2026-09-23 15:24Parse client files: xlsx, docx, pdfGoslinuly
- 2026-09-23 15:24Store client files in PostgreSQL and implement DocumentsPortGoslinuly
- 2026-09-23 15:20EKT catalog API clientGoslinuly
- 2026-09-23 15:20Catalog in PostgreSQL with import from file or EKT APIGoslinuly
- 2026-09-23 15:20Wire PostgreSQL catalog into the API and workerGoslinuly
- 2026-09-23 15:15Align ownership comments with the current splitGoslinuly
- 2026-09-23 15:14Fix alternatives matching, add barcode lookupGoslinuly
- 2026-09-23 15:05feat: integrate marketplace backend, assistant services and Next.js frontendTomiris
- 2026-09-23 14:46feat: add session and chat API with durable worker and integration contractsTomiris
- 2026-09-23 14:27README: data, assistant modules and how to run testsGoslinuly
- 2026-09-23 14:27LLM port and Claude providerGoslinuly
- 2026-09-23 14:27Assistant tools, prompt and reply contractsGoslinuly
- 2026-09-23 14:27Bounded assistant loop with answer verificationGoslinuly
- 2026-09-23 14:27Chat service: confirmation, model turn and no-LLM fallbackGoslinuly
- 2026-09-23 14:27docs: service interfaces and chat reply format for the API sideGoslinuly
- 2026-09-23 14:21Add synthetic catalog in ekt.kz API formatGoslinuly
- 2026-09-23 14:21Catalog model and data quality rulesGoslinuly
- 2026-09-23 14:21Product search and explainable alternativesGoslinuly
- 2026-09-23 14:21Purchase terms knowledge base from ekt.kz pagesGoslinuly
- 2026-09-23 14:21Cart proposals with explicit confirmationGoslinuly
- 2026-09-23 14:00docs: add EKT API reference and environment templatesTomiris
- 2026-09-23 13:53basesTomiris
- 2026-09-23 13:44Add README with case description and data notesGoslinuly
Структура репозитория (470 файлов)
.env.example .gitattributes .gitignore README.md backend/app/__init__.py backend/app/adapters/__init__.py backend/app/adapters/ai_services/__init__.py backend/app/adapters/ai_services/client.py backend/app/adapters/ekt/__init__.py backend/app/adapters/ekt/client.py backend/app/adapters/llm/__init__.py backend/app/adapters/llm/anthropic_provider.py backend/app/adapters/llm/openai_provider.py backend/app/adapters/memory.py backend/app/adapters/postgres/__init__.py backend/app/adapters/postgres/catalog.py backend/app/adapters/postgres/documents.py backend/app/adapters/storage/__init__.py backend/app/adapters/storage/catalog.py backend/app/adapters/storage/sqlite.py backend/app/api/__init__.py backend/app/api/v1/__init__.py backend/app/api/v1/dependencies.py backend/app/api/v1/responses.py backend/app/api/v1/router.py backend/app/bootstrap.py backend/app/catalog_cli.py backend/app/contracts.py backend/app/core/__init__.py backend/app/core/config.py backend/app/core/errors.py backend/app/core/http.py backend/app/core/privacy.py backend/app/core/security.py backend/app/infrastructure/__init__.py backend/app/infrastructure/db.py backend/app/integrations/__init__.py backend/app/integrations/domain.py backend/app/integrations/ports.py backend/app/integrations/unavailable.py backend/app/main.py backend/app/modules/__init__.py backend/app/modules/actions/__init__.py backend/app/modules/actions/models.py backend/app/modules/actions/ports.py backend/app/modules/actions/service.py backend/app/modules/assistant/__init__.py backend/app/modules/assistant/budget.py backend/app/modules/assistant/catalog_only.py backend/app/modules/assistant/config.py backend/app/modules/assistant/contracts.py backend/app/modules/assistant/policy.py backend/app/modules/assistant/ports.py backend/app/modules/assistant/processing.py backend/app/modules/assistant/prompts/consultant.md backend/app/modules/assistant/prompts/planner.md backend/app/modules/assistant/references.py backend/app/modules/assistant/rendering.py backend/app/modules/assistant/runner.py backend/app/modules/assistant/runtime.py backend/app/modules/assistant/state.py backend/app/modules/assistant/tools.py backend/app/modules/catalog/__init__.py backend/app/modules/catalog/browse.py backend/app/modules/catalog/dto.py backend/app/modules/catalog/models.py backend/app/modules/catalog/ports.py backend/app/modules/catalog/quality.py backend/app/modules/catalog/routes.py backend/app/modules/catalog/sync.py backend/app/modules/chat/__init__.py backend/app/modules/chat/application.py backend/app/modules/chat/factory.py backend/app/modules/chat/models.py backend/app/modules/chat/ports.py backend/app/modules/chat/routes.py backend/app/modules/chat/service.py backend/app/modules/documents/__init__.py backend/app/modules/documents/extraction.py backend/app/modules/documents/models.py backend/app/modules/documents/ocr.py backend/app/modules/documents/parser.py backend/app/modules/documents/scanner.py backend/app/modules/documents/service.py backend/app/modules/documents/validation.py backend/app/modules/knowledge/__init__.py backend/app/modules/knowledge/service.py backend/app/modules/knowledge/translations.py backend/app/modules/search/__init__.py backend/app/modules/search/alternatives.py backend/app/modules/search/semantic.py backend/app/modules/search/service.py backend/app/worker.py backend/migrations/001_catalog.sql backend/migrations/002_documents.sql backend/pyproject.toml backend/requirements.lock backend/scripts/export_openapi.py backend/tests/conftest.py backend/tests/test_actions.py backend/tests/test_ai_services.py backend/tests/test_analog_safety.py backend/tests/test_anthropic_provider.py backend/tests/test_api.py backend/tests/test_assets_http.py backend/tests/test_catalog_browse.py backend/tests/test_catalog_quality.py backend/tests/test_catalog_reader_contract.py backend/tests/test_chat.py backend/tests/test_documents.py backend/tests/test_documents_extraction.py backend/tests/test_ekt_client.py backend/tests/test_grounded_runtime.py backend/tests/test_integration_boundaries.py backend/tests/test_openai_provider.py backend/tests/test_postgres_catalog.py backend/tests/test_postgres_documents.py backend/tests/test_product_images.py backend/tests/test_resource_lifecycle.py backend/tests/test_search.py backend/tests/test_security_boundaries.py backend/tests/test_session_lifecycle.py backend/tests/test_store_worker.py backend/tests/test_synthetic_integration.py backend/tests/test_terms_translations.py data/curated/category_labels.json data/curated/purchase_terms.json data/db/README.md data/db/ekt_catalog.sql data/synthetic/certificates/SYN-900003.pdf data/synthetic/certificates/SYN-900005.pdf data/synthetic/certificates/SYN-900006.pdf data/synthetic/certificates/SYN-900008.pdf data/synthetic/certificates/SYN-900010.pdf data/synthetic/certificates/SYN-900013.pdf data/synthetic/certificates/SYN-900014.pdf data/synthetic/certificates/SYN-900016.pdf data/synthetic/certificates/SYN-900017.pdf data/synthetic/certificates/SYN-900022.pdf data/synthetic/certificates/SYN-900023.pdf data/synthetic/certificates/SYN-900026.pdf data/synthetic/certificates/SYN-900027.pdf data/synthetic/certificates/SYN-900029.pdf data/synthetic/certificates/SYN-900030.pdf data/synthetic/certificates/SYN-900034.pdf data/synthetic/certificates/SYN-900035.pdf data/synthetic/certificates/SYN-900036.pdf data/synthetic/certificates/SYN-900037.pdf data/synthetic/certificates/SYN-900039.pdf data/synthetic/certificates/SYN-900040.pdf data/synthetic/certificates/SYN-900042.pdf data/synthetic/certificates/SYN-900043.pdf data/synthetic/certificates/SYN-900044.pdf data/synthetic/certificates/SYN-900045.pdf data/synthetic/certificates/SYN-900046.pdf data/synthetic/certificates/SYN-900047.pdf data/synthetic/certificates/SYN-900048.pdf data/synthetic/certificates/SYN-900049.pdf data/synthetic/certificates/SYN-900050.pdf data/synthetic/certificates/SYN-900051.pdf data/synthetic/certificates/SYN-900052.pdf data/synthetic/certificates/SYN-900053.pdf data/synthetic/certificates/SYN-900054.pdf data/synthetic/certificates/SYN-900055.pdf data/synthetic/certificates/SYN-900056.pdf data/synthetic/certificates/SYN-900057.pdf data/synthetic/certificates/SYN-900058.pdf data/synthetic/certificates/SYN-900059.pdf data/synthetic/certificates/SYN-900060.pdf data/synthetic/certificates/SYN-900061.pdf data/synthetic/certificates/SYN-900062.pdf data/synthetic/certificates/SYN-900063.pdf data/synthetic/certificates/SYN-900064.pdf data/synthetic/certificates/SYN-900065.pdf data/synthetic/certificates/SYN-900066.pdf data/synthetic/certificates/SYN-900067.pdf data/synthetic/certificates/SYN-900068.pdf data/synthetic/certificates/SYN-900072.pdf data/synthetic/certificates/SYN-900074.pdf data/synthetic/certificates/SYN-900075.pdf data/synthetic/certificates/SYN-900076.pdf data/synthetic/certificates/SYN-900077.pdf data/synthetic/certificates/SYN-900078.pdf data/synthetic/certificates/SYN-900079.pdf data/synthetic/certificates/SYN-900080.pdf data/synthetic/certificates/SYN-900081.pdf data/synthetic/certificates/SYN-900082.pdf data/synthetic/certificates/SYN-900084.pdf data/synthetic/certificates/SYN-900085.pdf data/synthetic/certificates/SYN-900086.pdf data/synthetic/certificates/SYN-900087.pdf data/synthetic/certificates/SYN-900088.pdf data/synthetic/certificates/SYN-900089.pdf data/synthetic/certificates/SYN-900090.pdf data/synthetic/certificates/SYN-900091.pdf data/synthetic/certificates/SYN-900092.pdf data/synthetic/certificates/SYN-900093.pdf data/synthetic/certificates/SYN-900095.pdf data/synthetic/certificates/SYN-900096.pdf data/synthetic/certificates/SYN-900097.pdf data/synthetic/certificates/SYN-900099.pdf data/synthetic/certificates/SYN-900100.pdf data/synthetic/certificates/SYN-900101.pdf data/synthetic/certificates/SYN-900102.pdf data/synthetic/certificates/SYN-900103.pdf data/synthetic/certificates/SYN-900104.pdf data/synthetic/certificates/SYN-900105.pdf data/synthetic/certificates/SYN-900106.pdf data/synthetic/certificates/SYN-900107.pdf data/synthetic/certificates/SYN-900108.pdf data/synthetic/certificates/SYN-900109.pdf data/synthetic/certificates/SYN-900111.pdf data/synthetic/certificates/SYN-900114.pdf data/synthetic/certificates/SYN-900115.pdf data/synthetic/certificates/SYN-900116.pdf data/synthetic/certificates/SYN-900117.pdf data/synthetic/certificates/SYN-900119.pdf data/synthetic/certificates/SYN-900120.pdf data/synthetic/certificates/SYN-900121.pdf data/synthetic/certificates/SYN-900122.pdf data/synthetic/certificates/SYN-900126.pdf data/synthetic/certificates/SYN-900127.pdf data/synthetic/certificates/SYN-900128.pdf data/synthetic/certificates/SYN-900129.pdf data/synthetic/certificates/SYN-900131.pdf data/synthetic/certificates/SYN-900132.pdf data/synthetic/certificates/SYN-900133.pdf data/synthetic/certificates/SYN-900134.pdf data/synthetic/certificates/SYN-900135.pdf data/synthetic/certificates/SYN-900136.pdf data/synthetic/certificates/SYN-900138.pdf data/synthetic/certificates/SYN-900139.pdf data/synthetic/certificates/SYN-900140.pdf data/synthetic/certificates/SYN-900141.pdf data/synthetic/certificates/SYN-900142.pdf data/synthetic/certificates/SYN-900143.pdf data/synthetic/certificates/SYN-900146.pdf data/synthetic/certificates/SYN-900148.pdf data/synthetic/certificates/SYN-900149.pdf data/synthetic/certificates/SYN-900151.pdf data/synthetic/certificates/SYN-900152.pdf data/synthetic/certificates/SYN-900153.pdf data/synthetic/certificates/SYN-900154.pdf data/synthetic/certificates/SYN-900155.pdf data/synthetic/certificates/SYN-900157.pdf data/synthetic/certificates/SYN-900158.pdf data/synthetic/certificates/SYN-900159.pdf data/synthetic/certificates/SYN-900160.pdf data/synthetic/certificates/SYN-900161.pdf data/synthetic/certificates/SYN-900162.pdf data/synthetic/certificates/SYN-900164.pdf data/synthetic/certificates/SYN-900165.pdf data/synthetic/certificates/SYN-900166.pdf data/synthetic/certificates/SYN-900167.pdf data/synthetic/certificates/SYN-900168.pdf data/synthetic/certificates/SYN-900169.pdf data/synthetic/certificates/SYN-900171.pdf data/synthetic/certificates/SYN-900172.pdf data/synthetic/certificates/SYN-900173.pdf data/synthetic/certificates/SYN-900174.pdf data/synthetic/certificates/SYN-900175.pdf data/synthetic/certificates/SYN-900176.pdf data/synthetic/certificates/SYN-900177.pdf data/synthetic/certificates/SYN-900178.pdf data/synthetic/certificates/SYN-900179.pdf data/synthetic/certificates/SYN-900180.pdf data/synthetic/certificates/SYN-900181.pdf data/synthetic/certificates/SYN-900182.pdf data/synthetic/certificates/SYN-900183.pdf data/synthetic/certificates/SYN-900184.pdf data/synthetic/certificates/SYN-900185.pdf data/synthetic/certificates/SYN-900186.pdf data/synthetic/certificates/SYN-900187.pdf data/synthetic/certificates/SYN-900188.pdf data/synthetic/certificates/SYN-900189.pdf data/synthetic/certificates/SYN-900190.pdf data/synthetic/certificates/SYN-900192.pdf data/synthetic/certificates/SYN-900194.pdf data/synthetic/certificates/SYN-900195.pdf data/synthetic/certificates/SYN-900196.pdf data/synthetic/certificates/SYN-900197.pdf data/synthetic/certificates/SYN-900198.pdf data/synthetic/certificates/SYN-900199.pdf data/synthetic/certificates/SYN-900200.pdf data/synthetic/certificates/SYN-900201.pdf data/synthetic/certificates/SYN-900202.pdf data/synthetic/certificates/SYN-900203.pdf data/synthetic/certificates/SYN-900204.pdf data/synthetic/certificates/SYN-900205.pdf data/synthetic/certificates/SYN-900206.pdf data/synthetic/certificates/SYN-900207.pdf data/synthetic/certificates/SYN-900208.pdf data/synthetic/certificates/SYN-900209.pdf data/synthetic/certificates/SYN-900210.pdf data/synthetic/certificates/SYN-900212.pdf data/synthetic/certificates/SYN-900213.pdf data/synthetic/certificates/SYN-900214.pdf data/synthetic/certificates/SYN-900215.pdf data/synthetic/certificates/SYN-900219.pdf data/synthetic/certificates/SYN-900221.pdf data/synthetic/certificates/SYN-900223.pdf data/synthetic/certificates/SYN-900224.pdf data/synthetic/certificates/SYN-900226.pdf data/synthetic/certificates/SYN-900228.pdf data/synthetic/certificates/SYN-900229.pdf data/synthetic/certificates/SYN-900230.pdf data/synthetic/certificates/SYN-900231.pdf data/synthetic/certificates/SYN-900232.pdf data/synthetic/certificates/SYN-900234.pdf data/synthetic/certificates/SYN-900235.pdf data/synthetic/certificates/SYN-900236.pdf data/synthetic/certificates/SYN-900238.pdf data/synthetic/certificates/SYN-900239.pdf data/synthetic/certificates/SYN-900240.pdf data/synthetic/certificates/SYN-900241.pdf data/synthetic/certificates/SYN-900242.pdf data/synthetic/certificates/SYN-900243.pdf data/synthetic/certificates/SYN-900244.pdf data/synthetic/certificates/SYN-900245.pdf data/synthetic/certificates/SYN-900246.pdf data/synthetic/certificates/SYN-900247.pdf data/synthetic/certificates/SYN-900252.pdf data/synthetic/certificates/SYN-900253.pdf data/synthetic/certificates/SYN-900254.pdf data/synthetic/certificates/SYN-900255.pdf data/synthetic/certificates/SYN-900256.pdf data/synthetic/certificates/SYN-900257.pdf data/synthetic/certificates/SYN-900259.pdf data/synthetic/certificates/SYN-900261.pdf data/synthetic/certificates/SYN-900262.pdf data/synthetic/certificates/SYN-900263.pdf data/synthetic/certificates/SYN-900264.pdf data/synthetic/certificates/SYN-900265.pdf data/synthetic/certificates/SYN-900266.pdf data/synthetic/certificates/SYN-900267.pdf data/synthetic/certificates/SYN-900269.pdf data/synthetic/certificates/SYN-900270.pdf data/synthetic/certificates/SYN-900271.pdf data/synthetic/certificates/SYN-900272.pdf data/synthetic/certificates/SYN-900273.pdf data/synthetic/certificates/SYN-900274.pdf data/synthetic/certificates/SYN-900275.pdf data/synthetic/certificates/SYN-900276.pdf data/synthetic/certificates/SYN-900277.pdf data/synthetic/certificates/SYN-900278.pdf data/synthetic/certificates/SYN-900279.pdf data/synthetic/ekt_products.json data/synthetic/ekt_selection.json data/synthetic/embeddings.json data/synthetic/fetch_ekt_selection.py data/synthetic/generate.py data/synthetic/images/SYN-900001.svg data/synthetic/images/SYN-900002.svg data/synthetic/images/SYN-900003.svg data/synthetic/images/SYN-900004.svg data/synthetic/images/SYN-900005.svg data/synthetic/images/SYN-900006.svg data/synthetic/images/SYN-900007.svg data/synthetic/images/SYN-900008.svg data/synthetic/images/SYN-900009.svg data/synthetic/images/SYN-900010.svg data/synthetic/images/SYN-900011.svg data/synthetic/images/SYN-900012.svg data/synthetic/images/SYN-900013.svg data/synthetic/images/SYN-900014.svg data/synthetic/images/SYN-900015.svg data/synthetic/images/SYN-900016.svg data/synthetic/images/SYN-900017.svg data/synthetic/images/SYN-900018.svg data/synthetic/images/SYN-900019.svg data/synthetic/images/SYN-900020.svg data/synthetic/images/SYN-900021.svg data/synthetic/images/SYN-900022.svg data/synthetic/images/SYN-900023.svg data/synthetic/images/SYN-900024.svg data/synthetic/images/SYN-900025.svg data/synthetic/images/SYN-900026.svg data/synthetic/images/SYN-900027.svg data/synthetic/images/SYN-900028.svg data/synthetic/images/SYN-900029.svg data/synthetic/images/SYN-900030.svg data/synthetic/images/SYN-900031.svg data/synthetic/images/SYN-900032.svg data/synthetic/images/SYN-900033.svg data/synthetic/images/SYN-900034.svg data/synthetic/images/SYN-900035.svg data/synthetic/images/SYN-900036.svg data/synthetic/images/SYN-900037.svg data/synthetic/images/SYN-900038.svg data/synthetic/images/SYN-900039.svg data/synthetic/images/SYN-900040.svg data/synthetic/images/SYN-900041.svg data/synthetic/images/SYN-900042.svg data/synthetic/images/SYN-900043.svg data/synthetic/images/SYN-900044.svg data/synthetic/images/SYN-900045.svg data/synthetic/images/SYN-900046.svg data/synthetic/images/SYN-900047.svg data/synthetic/images/SYN-900048.svg data/synthetic/images/SYN-900049.svg data/synthetic/images/SYN-900050.svg data/synthetic/images/SYN-900051.svg data/synthetic/images/SYN-900052.svg data/synthetic/images/SYN-900053.svg data/synthetic/images/SYN-900054.svg data/synthetic/images/SYN-900055.svg data/synthetic/images/SYN-900056.svg data/synthetic/images/SYN-900057.svg data/synthetic/images/SYN-900058.svg data/synthetic/images/SYN-900059.svg data/synthetic/images/SYN-900060.svg data/synthetic/images/SYN-900061.svg data/synthetic/images/SYN-900062.svg data/synthetic/images/SYN-900063.svg data/synthetic/images/SYN-900064.svg data/synthetic/images/SYN-900065.svg data/synthetic/images/SYN-900066.svg data/synthetic/images/SYN-900067.svg data/synthetic/images/SYN-900068.svg data/synthetic/pictures.py docs/api.md docs/architecture.md docs/assistant-testing.md docs/backend-integration.md docs/contracts.md docs/data-and-db.md docs/mvp-plan.md docs/openapi.json docs/research-ekt-you-aero.md docs/verification-2026-09-23.md frontend/.env.example frontend/.gitignore frontend/README.md frontend/next-env.d.ts frontend/next.config.ts frontend/package-lock.json frontend/package.json frontend/src/app/api/v1/[...path]/route.ts frontend/src/app/globals.css frontend/src/app/layout.tsx frontend/src/app/page.tsx frontend/src/components/assistant-chat.css frontend/src/components/assistant-chat.tsx frontend/src/components/storefront/cart-drawer.tsx frontend/src/components/storefront/compare-modal.tsx frontend/src/components/storefront/header.tsx frontend/src/components/storefront/icons.tsx frontend/src/components/storefront/product-card.tsx frontend/src/components/storefront/types.ts frontend/src/components/storefront/use-dialog.ts frontend/src/lib/api.ts frontend/tests/proxy.test.mjs frontend/tsconfig.json scripts/build_assistant_fixtures.py scripts/build_assistant_sheets.mjs scripts/smoke_attachments.py scripts/smoke_test.py scripts/verify_assistant_fixtures.py test-files/README.md test-files/assistant/01_small_spec.txt test-files/assistant/02_large_text.txt test-files/assistant/03_spec_500_rows.csv test-files/assistant/04_spec_1200_rows.xlsx test-files/assistant/05_spec_22_sheets.xlsx test-files/assistant/06_specification.docx test-files/assistant/07_catalog_30_pages.pdf test-files/assistant/08_scanned_spec.pdf test-files/assistant/09_photo_spec.jpg test-files/verification.json презентации/ekt-assistant.pptx
Ветки (2)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.