Проблема: st.rerun() внутри with tab_*: блокировал выполнение других вкладок,
кнопки не работали, экран выглядел пустым.
Решение: простая синхронная архитектура:
- _invoke_agent() запускает агент в потоке и ждёт t.join()
- UI показывает st.spinner() пока агент работает
- События собираются через AgentEventCollector (список, не очередь)
- Показываются после завершения в раскрывающемся логе
- Нет polling, нет rerun-цикла, нет флагов chat_running/pipe_running
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Проблема: blocking while-цикл блокировал Streamlit, кнопка «Стоп» не работала
(Streamlit не может обработать клик пока скрипт заблокирован), кнопка мигала
каждые 0.3с из-за динамического ключа.
Решение: rerun-based polling — каждая «итерация» это полный rerun скрипта:
- chat_running / pipe_running флаги в session_state
- thread + queue живут в session_state между рерандами
- time.sleep(0.5) → st.rerun() вместо while-цикла
- кнопка «Стоп» рендерится нормально и реагирует мгновенно
- «⏳ Агент работает... Nс» при простое >15с
- лог событий накапливается в session_state.chat_events
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- RetryOnRateLimitMiddleware: при 429 шлёт события rate_limit_wait/retry
в глобальный UI-канал (set_ui_event_queue) — без него не меняет поведение
- ui.py: рендерит ⏳ rate_limit_wait и 🔄 rate_limit_retry в лог событий
- ui.py: показывает «Агент работает... Nс» если нет событий >15с
- ui.py: кнопка «Стоп» прерывает ожидание в чате и pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MCP-инструменты после загрузки получают префикс mcp__journal-bh-professor__.
tools.get("tasks_list") всегда возвращал None → статус не загружался.
Исправлено в ui.py (вкладка Статус) и cli.py (команда status).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- src/agent/solve_tools.py: новые LLM-субагенты validate_teacher_comment и
generate_code_solution — агент сам решает когда их вызывать
- src/agent/solve_prompts.py: централизованные промпты для субагентов решения
- src/agent/gitea_tools.py: добавлен gitea_list_files для чтения файлов репо
- src/agent/agent.py: SOLVE_TOOLS подключены к homework_direct_agent и rework_agent
- src/agent/prompts.py: промпты переработаны в capability-based формат (без жёстких шагов)
- cli.py: единая точка входа (solve / run / status)
- ui.py: Streamlit UI с реал-тайм отображением вызовов инструментов
- requirements.txt: добавлен streamlit>=1.35.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BroJS stores teacher comments in submission.grade.feedback, not in
the top-level comments array (which is always empty). Fixed priority:
submission.grade.feedback -> submission.feedback -> data.feedback -> data.comments
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
New flow for rework with comments:
1. Read current code files from Gitea
2. LLM validates if comment is justified or a trap
3a. TRAP (invalid): add objection to README.md, submit without code changes
3b. VALID: analyze fixes/defenses, regenerate code
Added: _VALIDATE_PROMPT, _OBJECTION_TEMPLATE, validate_comment(),
gitea_read(), gitea_list_files()
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Analyze prompt now asks for 4-part structured argument per defense:
ZAMECHANIE / NEOBKHODIMOST / OPTIMALNOST / ALTERNATIVY
Generated code gets DESIGN DECISION / NECESSITY / OPTIMALITY /
ALTERNATIVES CONSIDERED comment blocks instead of bare NOTE.
Agent argues that choices were deliberate, necessary and optimal -
not just correct.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
BroJS clears the answer field when rejecting a task, so repo_url was
always None on rework. Now we check Gitea API directly — if the repo
exists, it's a rework regardless of BroJS answer state.
Comments are still read from BroJS task_get.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add analyze_comments() that calls LLM to evaluate each teacher comment:
- valid criticism -> fixes list (LLM will fix these)
- incorrect/misunderstood -> defenses list (LLM adds NOTE: comments in code)
Verdict: needs_fixes | already_correct | mixed
solve() now prints analysis verdict and counts before generation.
Commit message on rework includes verdict + fix/defense counts.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Explicitly forbid langchain_ollama/OllamaEmbeddings in _PROMPT.
ChromaDB template now uses OpenAIEmbeddings via OpenRouter.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- reconfigure() вместо нового TextIOWrapper — не ломается при редиректе
- _is_429() с рекурсивным обходом ExceptionGroup (anyio оборачивает 429)
- except BaseException в _load_mcp / mcp_call для перехвата ExceptionGroup
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- solve_task: add fetch_all_tasks() for monitoring all task statuses
- console.py: tasks command now uses fetch_all_tasks with formatted table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- solve_task.py: add fetch_todo_tasks(), _parse_todo_tasks(), run_all()
- run_all() auto-fetches all todo tasks from BroJS and solves each one
- run_pipeline.py: rewrite to just call run_all() from solve_task
- supports: python run_pipeline.py (auto), run_pipeline.py <id...> (targeted)
- TARGET_IDS list for hardcoded targets without CLI args
- no deepagents imports = no double MCP load on startup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Switch transport to streamable_http (matches solve_task.py)
- Add _persistent_client global to reuse MultiServerMCPClient across calls
- Add exponential backoff 15→30→60→120→240s on 429 responses
- Add _INTER_CALL_DELAY=1.5s between consecutive MCP calls to prevent burst
- Add _is_429() helper for clean 429 detection
- Recreate client on non-429 errors to recover from stale sessions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added _force_submit() and _is_submitted() helpers. After the agent
finishes writing code, the pipeline checks if the task was actually
submitted. If not (LLM failed to call tools), the pipeline submits
directly via MCP — no LLM involved in the critical submit step.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Token usage is now tracked on platform.brojs.ru.
Uses the same JOURNAL_TOKEN (jrnl_...) for both MCP and inference.
Removed OPENAI_API_KEY from .env.example.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Changed homework_doing_instructions delimiter from """ to ''' to avoid
SyntaxError caused by docstrings inside code examples within the string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
prompts.py:
- Added detailed technical patterns for deepagents, FastMCP, LangGraph,
HumanInTheLoop, RAG with Qdrant, stream mode, text game
- LLM always via OpenRouter (never hub.pull/Ollama/hardcode)
- FastMCP correct pattern (module-level, NOT inside class)
- create_agent not compatible with AgentExecutor - documented
- DuckDuckGo search pattern (no API key needed)
pipeline.py:
- Added _invoke_with_retry: auto-retry on 429 rate limit (up to 5x, 90s backoff)
- Added TASK_PAUSE (15s) between tasks to reduce rate limit pressure
- Progress logging: per-task status messages
- Imported asyncio and re
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AgentMiddleware is a plain Generic class, not a Pydantic BaseModel,
so class-level field declarations don't work. Explicit __init__ required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>