AAYMA
RU
Войти

Qamqor Match

Next.js-сервис подбирает до трёх event-подрядчиков из официального CSV (66 профилей) по городу, дате, формату, категории и бюджету, объясняет выбор…

Доведённый продукт
Команда «Qamqor» 25💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: Qamqor
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Доведённый продукт

Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор

Суть

Next.js-сервис подбирает до трёх event-подрядчиков из официального CSV (66 профилей) по городу, дате, формату, категории и бюджету, объясняет выбор и помогает собрать смету события.

Как устроено

POST /api/match читает CSV на сервере. Детерминированный matcher фильтрует по занятости, бюджету, формату, языку и длительности (считая причины исключения), затем ранжирует по формуле score v1: остаток бюджета, язык, запас часов, совпавший признак из описания, полнота данных. Сортировка стабильна по id. LLM (gpt-4o-mini через Responses API) не выбирает и не переставляет подрядчиков: по фактам кандидатов она возвращает строгий JSON-план формулировки, код проверяет ID и факты, а при ошибке или таймауте 6000 мс включается fallback. При пустой выдаче предлагаются альтернативы: ближайшая дата, достаточный бюджет.

Ключевые решения

  • deterministic filtering and scoring
  • stable sort
  • strict JSON explanation plan
  • fact validation
  • fallback explanations
  • alternative suggestions search

Особенности

LLM полностью отделена от ранжирования и получает только структурированные факты без имён и полных профилей, поэтому выдача воспроизводима. Если подходящих подрядчиков нет, интерфейс предлагает проверенные альтернативы, где каждая кнопка меняет одно поле запроса. Есть сборка сметы из подрядчиков разных категорий.

Стек

Next.js · React · TypeScript · node:test · Vercel

Модели

gpt-4o-mini


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-181c6071-qamqor.

README

README.md

Qamqor Match

Решение кейса #79-lite: подбор event-подрядчиков по параметрам мероприятия с конкретными объяснениями выбора. Сервис принимает город, дату, формат, категорию и бюджет в тенге; дополнительно можно указать язык и длительность. Возвращает до трёх доступных вариантов в стабильном порядке.

Исходный код для проверки: репозиторий организаторов, ветка main.

Демонстрационный сайт: https://qamqor-hackathon.vercel.app. Для запуска и проверки решения сайт команды не требуется: исходный код и датасет находятся в репозитории. Инструкция самостоятельного развёртывания приведена ниже.

Требования кейса: docs/CASE.md. Этот README описывает версию для технической проверки.

Что реализовано

  • Серверная загрузка официального CSV из 66 профилей: hackathon-dataset-anonymized.csv.
  • Стартовый каталог показывает примеры из того же CSV: имя, категорию, город и стартовую цену. Выбор профиля подставляет город и категорию в форму; ниже доступны все 17 направлений с числом анкет в каждом. Доступность проверяется только после ввода даты. Счётчики вычисляются из CSV при сборке страницы.
  • Два готовых demo-примера заполняют обязательные поля формы для быстрой проверки; после ответа страница переходит к результатам. Интерфейс адаптирован для мобильного экрана.
  • Фильтрация по городу, категории, дате, формату мероприятия и бюджету. Подрядчики, занятые на выбранную дату, исключаются.
  • API проверяет типы обязательных полей, существование даты и положительность числовых значений; некорректный ввод возвращает HTTP 400.
  • Дополнительная фильтрация по запрошенному языку и длительности. Ограничение по часам применяется, если в профиле указан max_hours.
  • До трёх карточек с именем, категорией, городом, ценой «от» и объяснением. В объяснении используются короткие признаки из анкет, если они явно найдены в официальном описании. Отображаются признаки синтетических и восстановленных данных.
  • Для каждого варианта показан сметный ориентир: стартовая цена, заданный лимит на подрядчика и остаток до него. Это сравнение с бюджетом, а не окончательная стоимость услуги.
  • Из результатов можно собрать смету одного события: по одному выбранному подрядчику на категорию, сумма стартовых цен и необязательный общий бюджет с ориентировочным остатком. При новом городе, дате или формате смета начинается заново. Она живёт только в открытой вкладке, не создаёт заявку или бронирование.
  • Детерминированное ранжирование: одинаковый запрос на неизменном CSV даёт одинаковые ID и порядок, в том числе при недоступном OpenAI.
  • Три состояния результата и сообщение при количестве вариантов меньше трёх.
  • При пустом результате показаны до трёх проверенных альтернатив: ближайшая подходящая дата, достаточный бюджет или другое явно указанное условие. Каждая кнопка меняет только одно поле и повторяет поиск. Даты перебираются вперёд не более чем на 30 дней и не дальше последней даты календаря CSV; доступность реального рынка не обещается.
  • После изменения формы прежняя выдача скрывается; ответ уже отправленного запроса не подменит результаты для новых условий.
  • Серверные AI-объяснения и детерминированный fallback. В успешной выдаче интерфейс показывает режим объяснения.
outcomeЗначение
MATCHEDНайдены от одного до трёх подходящих подрядчиков
NO_CATEGORY_IN_CITYВ выбранном городе нет профилей нужной категории
NO_ELIGIBLEПрофили города и категории есть, но все исключены фильтрами

Архитектура

Стек: Next.js App Router, React, TypeScript; серверный источник данных — CSV.

Browser UI → POST /api/match → CSV loader → deterministic matcher
                                              ↓
                                    OpenAI explanation layer
                                      ↙                  ↘
                             проверенный AI-план      fallback
                                      ↘                  ↙
                                             response
КомпонентФайл / назначение
Browser UIsrc/app/page.tsx, components/SearchForm.tsx, ContractorCard.tsx: каталог из CSV, форма, состояния и карточки
APIsrc/app/api/match/route.ts: обработка POST /api/match, время ответа
CSV loadersrc/lib/csv.ts: чтение и разбор официального CSV на сервере
Matchersrc/lib/matcher.ts: фильтры, score, стабильная сортировка, facts и базовые объяснения
AI-слойsrc/lib/ai-explanations.server.ts: запрос к OpenAI, проверка плана, формулировки и fallback
Контрактsrc/lib/types.ts: типы запроса и ответа

Ответ содержит outcome, matches, rejectionSummary, message, algorithmVersion, elapsedMs и explanationMode. В каждой записи matches находятся contractor, score, facts, explanation.

Фильтрация и порядок

После выбора города и категории matcher проверяет занятость, бюджет, формат, язык и длительность. rejectionSummary содержит количества исключений по причинам busy, budget, format, language, duration. Для каждого исключённого кандидата учитывается первая сработавшая причина именно в этом порядке.

Score версии v1 — сумма факторов:

ФакторБаллы
Остаток бюджетаmin(30, round(max(0, (budgetKzt − priceFromKzt) / budgetKzt) × 30))
Запрошенный язык, подтверждённый фильтром20; если язык не указан — 0
Запас длительностиmin(10, maxHours − durationHours + 5), если оба значения заданы; иначе 0
Совпавшее слово описания (facts.evidence)20 при наличии; иначе 0
Полнота данных5, если город или цена восстановлены; иначе 10

Сортировка: score по убыванию, при равенстве — стабильный id по возрастанию. Затем берутся первые три результата. LLM в этом расчёте не участвует.

AI и fallback

LLM не выбирает подрядчиков и не меняет их порядок. Модель получает только явно перечисленные параметры запроса и структурированные facts уже найденных кандидатов: ID, доступность на дату, цену «от», остаток бюджета, формат, подтверждённый язык, часы и короткий признак, извлечённый из анкеты по фиксированным правилам. Полные профили, имена, описания и score ей не передаются.

Текущая реализация делает один запрос к OpenAI Responses API с моделью gpt-4o-mini, без повторных попыток, с store: false. Модель выбирает акцент и вариант формулировки через строгий JSON-план. Код проверяет ID, допустимые значения и наличие соответствующих facts, затем собирает два предложения из этих фактов. Произвольные утверждения модели не выводятся.

  • explanationMode: "ai" — план модели принят; UI: «Объяснение подготовлено AI-агентом».
  • explanationMode: "fallback" — использованы исходные объяснения matcher; UI: «Использовано базовое объяснение». Отсутствующее поле UI также трактует как fallback.

Без ключа или при ошибке OpenAI приложение продолжает работать. Отсутствие ключа, HTTP/сетевая ошибка, отказ модели, неверный или неполный ответ, неподтверждённые факты и таймаут автоматически возвращают детерминированные объяснения. Общий таймаут AI — 6000 мс, включая чтение ответа; размер ответа ограничен 32 КиБ. При пустой выдаче вызова модели нет. AI может менять формулировки между запросами, но состав и порядок карточек сохраняются.

Локальный запуск

Проверки выполнялись на Node.js 25.2.1 и npm. Тесты используют встроенный node:test и нативную загрузку TypeScript; для воспроизведения используйте указанную версию Node.js.

Для первого запуска клонируйте репозиторий (нужен доступ к приватному репозиторию организаторов):

git clone https://github.com/BAITC-Hacks/hack-181c6071-qamqor.git
cd hack-181c6071-qamqor
git switch main
npm ci
npm run dev

Если репозиторий уже скачан, выполните только npm ci и npm run dev из папки с package.json. npm ci устанавливает версии из сохранённого package-lock.json.

Откройте http://localhost:3000. Без настройки окружения подбор работает через fallback.

На стартовом экране можно выбрать пример профиля и затем указать дату, формат и бюджет. Для полного проверочного запроса нажмите «Ведущий · Астана» или «Флорист · Алматы» над формой, затем «Найти подрядчиков». Первые карточки каталога — только примеры записей, их доступность заранее не обещается. Сметный ориентир в выдаче относится к одному подрядчику и сравнивает стартовую цену с введённым лимитом; он не заменяет финальное коммерческое предложение.

Чтобы проверить общую смету, выполните поиск, добавьте карточку кнопкой «Добавить в смету», затем ищите другую категорию с теми же городом, датой и форматом. В разделе «Смета события» видны выбранные подрядчики и сумма. Укажите общий бюджет, чтобы увидеть ориентировочный остаток или превышение. Другой город, дата или формат начинают новую смету; кнопка заранее показывает «Начать новую смету».

Переменные окружения

Для AI-объяснений задайте OPENAI_API_KEY на сервере: локально в .env.local, на Vercel — в Environment Variables проекта. После изменения локальной переменной перезапустите dev-сервер; для production примените конфигурацию новым deployment.

OPENAI_API_KEY=

Это пустой пример. Настоящий ключ нельзя добавлять в Git, README, клиентский код или логи. .env.local исключён из Git. AI-слой читает ключ только из process.env.OPENAI_API_KEY.

В новом клоне можно создать файл командой PowerShell Copy-Item .env.example .env.local, затем вписать свой ключ после OPENAI_API_KEY= в редакторе. Если .env.local уже существует, отредактируйте его без копирования поверх. Для проверки без AI этот шаг можно пропустить. Подключать базу данных, Supabase или выполнять миграции не нужно: CSV уже включён в репозиторий.

Самостоятельное развёртывание (деплой)

Локальная production-сборка без облака

Из корня клонированного репозитория, после установки зависимостей:

npm run check
node --test tests/matching.test.mjs tests/ai-explanations.test.mjs
npm run build
npm run start

Откройте http://localhost:3000. Перед start остановите dev-сервер на том же порту через Ctrl+C. Оставьте терминал работающим. API доступен по адресу http://localhost:3000/api/match; четыре проверочных запроса приведены ниже. На собственном Node.js-сервере используется та же сборка и запуск; сохраните папку data/ рядом с package.json, поскольку сервер читает CSV из неё. Для постоянной публикации нужны менеджер процесса и HTTPS на стороне хостинга.

Vercel через CLI

Нужны аккаунт Vercel и локальный клон. Выполняйте команды по очереди из корня проекта:

npm install --global vercel
vercel login
vercel link

В vercel link выберите свой аккаунт/команду и создайте новый проект либо выберите свой существующий. Каталог проекта — ./, framework — Next.js. Для воспроизведения не требуется доступ к Vercel-аккаунту команды Qamqor. Созданная служебная папка .vercel/ исключена из Git.

Если нужны AI-объяснения, добавьте собственный ключ в окружение Production:

vercel env add OPENAI_API_KEY production

Вставьте ключ в интерактивном запросе CLI и выберите Yes для Sensitive. Если переменная уже существует, обновите её в настройках проекта; повторно добавлять её не нужно. Ключ не должен иметь префикс NEXT_PUBLIC_. Без него деплой тоже работает, используя fallback. Для Preview ключ при необходимости добавляется отдельно в окружение Preview.

После настройки выполните:

vercel --prod

Дождитесь успешной сборки и откройте URL, который вернула команда. После добавления или изменения переменной окружения выполните новый vercel --prod, чтобы применить её. Инструкции CLI: деплой, переменные окружения.

Vercel через веб-интерфейс

Вместо CLI можно выбрать Add New → Project → Import Git Repository и импортировать этот репозиторий, если Vercel разрешён доступ к нему. Настройки: Framework Preset — Next.js, Root Directory — корень репозитория, Install Command — npm ci, Build Command — npm run build, Output Directory — значение по умолчанию для Next.js. При необходимости добавьте OPENAI_API_KEY для Production и нажмите Deploy. Если политика GitHub-организации не позволяет подключить интеграцию, используйте CLI из доступного вам локального клона. Документация Vercel по Git.

Проверка после развёртывания

  1. Откройте главную страницу: каталог и форма должны загрузиться без ключа и без базы данных.
  2. Выберите «Ведущий · Астана» и нажмите «Найти подрядчиков»: ожидаются три карточки в порядке из сценария dense ниже.
  3. Проверьте остальные три сценария ниже и альтернативы при пустой выдаче. Для API-запроса подставьте URL своего сервера в $baseUrl.
  4. Без ключа ожидается explanationMode: "fallback"; с рабочим ключом и успешным ответом OpenAI — "ai". При ошибке провайдера fallback допустим; порядок подрядчиков сохраняется.

Git push и деплой — отдельные действия. git push origin main отправляет коммиты в репозиторий организаторов. vercel --prod публикует приложение и сам по себе не отправляет код в GitHub. Для сдачи исходников используется репозиторий; действующий сайт служит дополнительным демо. При подключённой Git-интеграции Vercel деплой может запускаться автоматически после push.

Production-проверки

Перед технической сдачей выполните из корня:

npm run check
npm run build
node --test tests/matching.test.mjs tests/ai-explanations.test.mjs
git diff --check
git diff --stat

check запускает TypeScript и ESLint; build собирает production-версию. Для локального запуска собранной версии используйте npm run start.

Два тестовых файла содержат 31 тест: официальный CSV, четыре demo-сценария, стабильный порядок, успешный AI-план, неверные ID/факты, отсутствие ключа, ошибки, отказ и таймауты. AI-тесты подменяют HTTP-вызов провайдера; им не нужен реальный ключ, они не выполняют платные запросы. Прохождение тестов не заменяет проверку живого OpenAI и браузера на production.

Четыре demo-сценария

Источник точных запросов и ожиданий: data/fixtures/matching-scenarios.json. Все запросы отправляются в POST /api/match с Content-Type: application/json. В этих сценариях язык и длительность не указаны.

1. Плотная категория — dense

{
  "city": "Астана",
  "date": "2026-11-14",
  "eventFormat": "корпоратив",
  "category": "Ведущий",
  "budgetKzt": 1500000
}

Ожидается MATCHED, три результата в порядке HK-37181 → HK-80581 → HK-97041. В городе пять профилей категории; исключены один занятый и один неподходящий по формату: busy=1, format=1.

2. Редкая категория — rare

{
  "city": "Алматы",
  "date": "2026-11-15",
  "eventFormat": "корпоратив",
  "category": "Флорист",
  "budgetKzt": 1500000
}

Ожидается MATCHED, одна карточка HK-39372. Из двух флористов один занят: busy=1. Сообщение результата честно указывает: «Нашлось только 1: остальные кандидаты не прошли условия»; дополнительные карточки не создаются.

3. Кандидаты есть, но все исключены — empty

{
  "city": "Алматы",
  "date": "2026-11-14",
  "eventFormat": "корпоратив",
  "category": "Флорист",
  "budgetKzt": 1500000
}

Ожидается NO_ELIGIBLE, matches: []. В городе два флориста: один занят, другой не подходит по формату; busy=1, format=1. Это не состояние отсутствующей категории.

4. Тот же запрос на другую дату — otherDate

{
  "city": "Астана",
  "date": "2026-11-15",
  "eventFormat": "корпоратив",
  "category": "Ведущий",
  "budgetKzt": 1500000
}

Сравните с dense: изменена только дата. Ожидается MATCHED, три результата в порядке HK-58385 → HK-37181 → HK-97041. HK-80581 свободен 14 ноября, но занят 15 ноября; HK-58385 занят 14 ноября, но свободен 15 ноября. Разница подтверждается busy_dates официального CSV. Исключения: busy=1, format=1.

Во всех четырёх сценариях остальные счётчики исключения равны нулю. Повторение каждого запроса должно сохранять ID и порядок независимо от explanationMode.

Запуск demo-запроса в PowerShell

$baseUrl = 'http://localhost:3000'
$fixtures = Get-Content -Raw -Encoding UTF8 data/fixtures/matching-scenarios.json | ConvertFrom-Json
$body = $fixtures.dense.request | ConvertTo-Json -Compress
Invoke-RestMethod -Uri "$baseUrl/api/match" -Method Post -ContentType 'application/json; charset=utf-8' -Body ([System.Text.Encoding]::UTF8.GetBytes($body))

Замените dense на rare, empty или otherDate. Для облачной проверки замените $baseUrl на адрес своего деплоя или https://qamqor-hackathon.vercel.app (без завершающего /). В форме вводятся те же значения. Даты fixtures фиксированы: когда они станут прошедшими, ограничение календаря в UI может мешать ручному демо; автоматические тесты от текущей даты не зависят.

Ограничения текущей версии

  • Официальный статический датасет из 66 профилей: доступность и цены берутся из CSV и не обновляются в реальном времени. Цена указана как «от»; часть данных помечена как восстановленная или синтетическая.
  • В техническую версию не входят бронирование, заявки, регистрация, личные кабинеты и админ-модерация.
  • Смета хранится только в памяти открытой вкладки и исчезает после обновления страницы; финальные цены и дополнительные услуги не рассчитываются.
  • Неизвестный max_hours не исключает кандидата по длительности; описание даёт только совпадение слов, а не семантическую оценку опыта.
  • Целевой ориентир кейса — ответ до 10 секунд. Таймаут AI ограничен 6 секундами; общее время также зависит от сети, холодного старта и сервера и не является гарантированным SLA.
  • Проверки AI с подменённым HTTP подтверждают обработку контракта и fallback, но не доступность OpenAI для конкретного аккаунта. Свежие назначения и статус production-проверки ведутся в docs/ACTIVE_WORK.md.

Следующий этап после технической проверки

Планируется подключение Supabase, авторизации, анкеты подрядчика и админ-модерации. Это следующий этап развития, а не реализованные функции текущей сдачи.

Звёзды
⭐ 0
Форки
0
Язык
HTML
Последний push
в прошлом месяце
Коммиты
50
Авторы
3
Файлы
48
Ветки
4

Авторы коммитов

Muhamedali Kamenov 40Dinmukhammed 5Pavlo 5

История коммитов

  • 2026-09-23 17:50docs: document reproducible setup and deploymentMuhamedali Kamenov
  • 2026-09-23 17:39feat: suggest verified dates and budgets for empty searchesMuhamedali Kamenov
  • 2026-09-23 17:22fix: reject malformed match requests with clear validationMuhamedali Kamenov
  • 2026-09-23 17:19feat: browse all contractor categories and prevent stale resultsMuhamedali Kamenov
  • 2026-09-23 17:08feat: build event estimate from matched contractorsMuhamedali Kamenov
  • 2026-09-23 16:59feat: add dataset marketplace and guided matchingMuhamedali Kamenov
  • 2026-09-23 16:49feat: personalize matching and show budget breakdownMuhamedali Kamenov
  • 2026-09-23 16:17docs: complete final compliance auditMuhamedali Kamenov
Все коммиты (50)
  • 2026-09-23 16:07feat: clarify available filters and partial resultsMuhamedali Kamenov
  • 2026-09-23 16:01fix: constrain AI focus to verified factsMuhamedali Kamenov
  • 2026-09-23 16:01merge: record production acceptance resultsMuhamedali Kamenov
  • 2026-09-23 15:59merge: finalize technical submission readmeMuhamedali Kamenov
  • 2026-09-23 15:55docs: finalize technical submission readmeDinmukhammed
  • 2026-09-23 15:53docs: record production acceptance resultsPavlo
  • 2026-09-23 15:45docs: define final QA deliverableMuhamedali Kamenov
  • 2026-09-23 15:40docs: move team to final submission checksMuhamedali Kamenov
  • 2026-09-23 15:20fix: add safe AI fallback diagnosticsMuhamedali Kamenov
  • 2026-09-23 15:18docs: mark AI agent integration verifiedMuhamedali Kamenov
  • 2026-09-23 15:17merge: add resilient AI explanation agentMuhamedali Kamenov
  • 2026-09-23 15:14fix: expose AI explanation modeDinmukhammed
  • 2026-09-23 15:13docs: mark agent UI verifiedMuhamedali Kamenov
  • 2026-09-23 15:12merge: show agent explanation modeMuhamedali Kamenov
  • 2026-09-23 15:11Merge remote-tracking branch 'origin/main' into feat/ai-dataDinmukhammed
  • 2026-09-23 15:09feat: show AI explanation modePavlo
  • 2026-09-23 15:05feat: add resilient AI matching explanationsDinmukhammed
  • 2026-09-23 15:01docs: assign agent UI task to DaurenMuhamedali Kamenov
  • 2026-09-23 14:49docs: prioritize resilient AI explanation layerMuhamedali Kamenov
  • 2026-09-23 14:47docs: mark data scenarios verifiedMuhamedali Kamenov
  • 2026-09-23 14:46merge: add required matching demo scenariosMuhamedali Kamenov
  • 2026-09-23 14:44docs: mark verified UI task completeMuhamedali Kamenov
  • 2026-09-23 14:43test: add required matching demo scenariosDinmukhammed
  • 2026-09-23 14:43merge: integrate verified UI statesMuhamedali Kamenov
  • 2026-09-23 14:30docs: record production deploymentMuhamedali Kamenov
  • 2026-09-23 14:21Merge remote-tracking branch 'origin/main' into sync-uiMuhamedali Kamenov
  • 2026-09-23 14:17merge: synchronize team ownership rulesMuhamedali Kamenov
  • 2026-09-23 14:17docs: enforce task ownership across agentsMuhamedali Kamenov
  • 2026-09-23 14:13merge: add single-owner work protocolMuhamedali Kamenov
  • 2026-09-23 14:13docs: assign single owner for active workMuhamedali Kamenov
  • 2026-09-23 14:11merge: integrate search UI with matching coreMuhamedali Kamenov
  • 2026-09-23 14:11feat: integrate contractor search interfaceMuhamedali Kamenov
  • 2026-09-23 14:10feat: integrate contractor search UIPavlo
  • 2026-09-23 14:02Merge remote-tracking branch 'origin/feat/ui' into feat/coreMuhamedali Kamenov
  • 2026-09-23 13:58Merge remote-tracking branch 'origin/main' into feat/uiPavlo
  • 2026-09-23 13:57merge: integrate deterministic matching coreMuhamedali Kamenov
  • 2026-09-23 13:56Merge remote-tracking branch 'origin/feat/core' into feat/coreMuhamedali Kamenov
  • 2026-09-23 13:55feat: add deterministic contractor matching coreMuhamedali Kamenov
  • 2026-09-23 13:41feat: add contractor search formPavlo
  • 2026-09-23 13:32docs: define isolated branch workflowMuhamedali Kamenov
  • 2026-09-23 13:22docs: define matching pipeline and team startupMuhamedali Kamenov
  • 2026-09-23 13:19chore: add case 79 brief dataset and team planMuhamedali Kamenov
Структура репозитория (48 файлов)
.env.example
.gitignore
AGENTS.md
README.md
data/fixtures/matching-scenarios.json
data/hackathon-dataset-anonymized.csv
data/hackathon-dataset-preview.html
docs/ACTIVE_WORK.md
docs/CASE.md
docs/COORDINATION.md
docs/GIT_WORKFLOW.md
docs/HOURLY_PROGRESS.md
docs/IMPLEMENTATION.md
docs/OFFICIAL_INSTRUCTIONS.md
docs/PRODUCTION_QA.md
docs/TASKS.md
docs/TEAM_START.md
eslint.config.mjs
next-env.d.ts
next.config.ts
package-lock.json
package.json
postcss.config.mjs
prompts/ALI.md
prompts/DAUREN.md
prompts/DIMASH.md
public/file.svg
public/globe.svg
public/next.svg
public/vercel.svg
public/window.svg
src/app/api/match/route.ts
src/app/components/ContractorCard.tsx
src/app/components/SearchForm.module.css
src/app/components/SearchForm.tsx
src/app/favicon.ico
src/app/globals.css
src/app/layout.tsx
src/app/page.tsx
src/lib/ai-explanations.server.ts
src/lib/csv.ts
src/lib/matcher.ts
src/lib/types.ts
tests/AI-1.md
tests/DATA-1.md
tests/ai-explanations.test.mjs
tests/matching.test.mjs
tsconfig.json
Ветки (4)

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.