Skip to content
StulkovLDPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

LocalScript - локальный Lua-генератор для MWS Octapi

Агент пишет Lua-скрипт для LowCode-платформы MWS Octapi по описанию задачи на русском или английском. Всё крутится локально через Ollama. Ни prompt, ни код, ни wf.vars за пределы машины не уходят.

Быстрый старт

macOS / Linux:

curl -fsSL https://raw.githubusercontent.com/StulkovLD/hack_mts/master/install.sh | bash

Windows (PowerShell):

iwr -useb https://raw.githubusercontent.com/StulkovLD/hack_mts/master/install.ps1 | iex

Один скрипт делает всё, что нужно на чистой машине: проверяет Python 3.10+, git и Ollama, дотягивает чего не хватает через штатный инсталлер ОС (brew / apt / curl|sh / winget), клонирует репо в ~/localscript и запускает launch.py. Первый запуск 3-5 минут, пока тянется модель ~5 ГБ. Повторный ~5 секунд, бутстрап скипает готовые шаги.

Ручной путь, если установщиком пользоваться не хочется:

git clone https://github.com/StulkovLD/hack_mts.git localscript
cd localscript
ollama pull qwen2.5-7b-instruct-q6_k:latest
pip install -r requirements.txt
python3 launch.py

После старта Chainlit UI открывается на http://localhost:8000. REST-сервер /generate поднимается отдельно командой python3 -m localscript.run_server на :8080.

Требования: Python 3.10+, Ollama, git, GPU с 8 GB VRAM.

Модель и параметры запуска

ollama pull qwen2.5-7b-instruct-q6_k:latest

Параметры зафиксированы в src/localscript/config.py и совпадают с ТЗ:

Параметр Значение
num_ctx 4096
num_predict 256
batch 1
parallel 1
GPU offload 100%, без CPU offload

num_ctx / num_predict / num_batch передаются в Ollama per-request из llm.py. parallel - это server-level env OLLAMA_NUM_PARALLEL, его нельзя задать per-request. launch.py при спавне своего ollama serve передаёт OLLAMA_NUM_PARALLEL=1 и OLLAMA_NUM_BATCH=1 в окружение (см. src/localscript/bootstrap.py). Если у тебя уже запущен свой ollama serve с другими значениями, bootstrap напечатает warning и попросит перезапустить его так:

export OLLAMA_NUM_PARALLEL=1
export OLLAMA_NUM_BATCH=1
ollama serve &

Peak VRAM. Qwen2.5-7B в квантизации Q6_K даёт ~6 GB peak при заданных параметрах. Остаётся запас 2 GB до лимита 8 GB. Проверяется во время генерации:

nvidia-smi --query-gpu=memory.used --format=csv

Выбор 7B Q6_K - компромисс: Q6_K работает практически без деградации относительно fp16, при этом комфортно влезает в 8 GB.

Все вызовы идут через локальный Ollama на localhost:11434. Внешних AI-API (OpenAI, Anthropic, Gemini, любые прокси) в runtime нет. Проверяется grep'ом по src/:

grep -r -i "openai\|anthropic\|api\.openai\|api\.anthropic" src/
# пусто

Архитектура pipeline

Один диалог с моделью, четыре роли переключаются внутри общего контекста:

router  ->  coder  ->  repair*  ->  reviewer
             ^            |
             +-- pattern card (retrieval из локальной KB)
  • router классифицирует задачу и возвращает JSON-план: type, source, target, return (scalar / array / table), confidence, опциональный clarify. Если confidence < 0.3, агент задаёт уточняющий вопрос вместо генерации наугад.
  • coder пишет Lua по few-shot подсказке. Подсказка - pattern card из локальной KB, подобранная под type задачи.
  • repair до двух итераций чинит код по структурированному фидбеку валидатора.
  • reviewer выносит финальный verdict pass/fail. В eval-режиме выключается флагом skip_reviewer=True.

Зачем один диалог на четыре роли. KV-cache переиспользуется между стадиями, экономит токены и время. Четыре независимых вызова тянули бы системный промпт заново каждый раз.

Если после двух попыток repair код всё ещё не пас, pipeline отдаёт лучший из полученных вариантов с флагом ok=false. Агент не молчит, не зацикливается, не маскирует провал.

Валидация

Между стадиями coder и repair работает src/localscript/validator.py - четыре уровня проверки in-process, без вызовов наружу:

Уровень Инструмент Что ловит
Синтаксис lupa.load() (Lua 5.4 bundled) Parse-ошибки, незакрытые блоки, битые кавычки
Статанализ luaparser AST walker Undefined globals, опечатки в wf и _utils, в том числе в мёртвых ветках
Домен Встроенные линты Octapi wf.vars paths, 1-based индексация массивов, ~= вместо !=, _utils.array.new() для новых массивов, запрет os.* / io.*
Семантика Lua sandbox + _result assert'ы Shape результата (scalar/array/table), обязательные поля, реальное исполнение кода на контексте задачи

Результат валидации уходит в repair как структурированный фидбек: какие линты упали, какие глобалы undefined, что ожидалось в _result. Repair не гадает - чинит конкретику.

lupa тащит bundled Lua 5.4 prebuilt wheel'ами под macOS, Linux, Windows. Системный lua ставить не нужно, brew / apt / choco не дёргаем.

Локальная база знаний

src/localscript/knowledge.py - 14 PatternCard-ов, по одной на типовой паттерн Octapi:

direct_return, mutate_records, format_datetime, ensure_array, filter_new_array, augment_wf_vars, augment_existing_code, json_multi_field, multi_var_augment, epoch_convert, string_extract, sort_records, nested_collect, aggregate_reduce.

Каждая карточка содержит slug, task_types, keywords, why (зачем делать именно так), rules (правила Octapi), example_task, example_code - эталонный Lua на 10-80 строк.

Retrieval - функция retrieve_pattern_cards(task_text, task_type):

  1. Точное совпадение task_type из router'а - плюс 10 к score.
  2. Совпадение ключевых слов в тексте задачи - плюс 2 за каждое.
  3. Top-1 попадает в coder как few-shot пример.
  4. Fallback на direct_return, если ничего не сматчилось.

Никаких внешних векторных баз, embedding-моделей, API. Вся подборка работает на JSON-карточках в репозитории, эталоны лежат в git и воспроизводятся байт-в-байт.

API /generate

Контракт из docs/assets/localscript-openapi.yaml (SSOT организаторов):

python3 -m localscript.run_server
# слушает http://localhost:8080

POST /generate, тело {"prompt": "<текст задачи>"}, ответ {"code": "<сырой Lua>"}.

Пример на первой публичной задаче:

curl -s -X POST http://localhost:8080/generate \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Из полученного списка email получи последний.\n```json\n{\"wf\":{\"vars\":{\"emails\":[\"a@x.ru\",\"b@x.ru\",\"c@x.ru\"]}}}\n```"
  }'
# {"code":"return wf.vars.emails[#wf.vars.emails]"}

Если в prompt вместе с текстом задачи приложен JSON-контекст (в ```json-блоке или инлайн {wf: ...}), pipeline его извлекает автоматически и прогоняет семантическую валидацию на реальных данных. Без контекста работают синтаксис, статанализ и доменные линты - семантический уровень деградирует, остальное остаётся.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages