Open
Conversation
…egration tests
ToolCallScorer:
- normalize_tool_call(): OpenAI and Anthropic format support
- enum validation
- minimum/maximum range validation
- fix: bool no longer passes as integer
- fix: tool_call={} no longer silently dropped
- typo hints for misspelled tool names
Client bug fix (found by reading nullwatch source):
- list_spans/list_evals/list_runs now unwrap {"items": [...]} correctly
(was always returning [] against real nullwatch)
Tests: 33 → 60 + 19 integration tests (auto-skip without live server)
New: examples/live_demo.py — end-to-end demo with ollama + lettucedetect
- make RAG hallucination verdict respect fail_threshold - improve nullclaw/nullwatch run correlation in Telegram bot
… and CLI - NullwatchClient: env vars (NULLWATCH_URL, NULLWATCH_API_KEY), api_key/Bearer auth, redact hook, capabilities(), flush(), close(), context-manager support - Buffered mode: flush_at threshold, thread-safe buffer, bulk ingest on flush - Decorators: @client.trace and @client.atrace for sync/async functions - Provider helpers: Span.record_openai_usage(), record_anthropic_usage(), record_tokens(), record_cost() - nullwatch/testing.py: MemoryTransport with assert_span_recorded(), assert_eval_recorded(), assert_no_failed_evals() - nullwatch/cli.py: ping, ingest-span, ingest-eval, run commands - pyproject.toml: CLI entrypoint nullwatch-py = nullwatch.cli:main - tests/test_new_features.py: 46 new tests, all 114 pass
…reshold - Rewrote README to document all implemented features: ToolCallGroundingScorer, decorators (@trace/@atrace), buffered mode, provider helpers, MemoryTransport testing utils, CLI, redaction - Fixed test_short_hallucinated_span_fails_by_default: added explicit fail_threshold=0.05 so the 11% hallucinated-char ratio triggers a fail (the scorer correctly uses ratio-based threshold, not any-span-fails logic)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Контекст
Этот PR подготовлен командой TNS_MODERATION в рамках WB × OpenSource Hackathon по треку nullclaw.
Команда:
Что добавляет этот PR
nullwatch-py— Python SDK для nullwatch с нулевыми обязательными зависимостями в ядре.У nullwatch уже есть хороший HTTP API и CLI, но нет SDK ни для одного языка — каждая интеграция требует ручного написания raw HTTP-запросов или вызовов CLI. Этот SDK закрывает этот gap и добавляет три eval scorer-а для реальных классов ошибок агентов.
Почему это хороший вклад
urllib,json,dataclasses) — никаких зависимостей по умолчаниюRAGHallucinationScorerзакрывает конкретный observability gap: агенты с RAG теперь могут автоматически проверять, корректен ли ответ на основании контекста, и сразу отправлять результат как nullwatch evalToolCallScorerловит распространённый класс ошибок агентов (выдуманные названия инструментов, опечатки в аргументах, неправильные типы) и формирует структурированные evals без ML-моделиToolCallGroundingScorer— семантическое дополнение кToolCallScorer: проверяет, что значения аргументов взяты из реального контекста, а не выдуманы моделью. Два бэкенда: zero-deps keyword-эвристика и LLM-судья (любой OpenAI-совместимый API, например локальный Ollama)Evalи подключаются напрямую к существующему эндпоинту/v1/evalsСостав
1.
NullwatchClient— HTTP-клиентingest_span(span)/ingest_spans(spans)— POST в/v1/spansи/v1/spans/bulkingest_eval(eval_)— POST в/v1/evalslist_spans(),list_evals(),list_runs(),get_run()— GET с фильтрамиclient.span(run_id, operation, ...)— контекст-менеджер: автоматически завершает span и отправляет его, при исключении ставитstatus="error"client.trace(operation)/client.atrace(operation)— декораторы для sync и async функцийis_alive()/health()/capabilities()— проверка доступности сервераraise_on_error=Falseдля fire-and-forget инструментацииbuffered=True+flush_at=N— батчевая отправка через/v1/spans/bulkredact=— хук для зачистки секретов перед отправкой2.
RAGHallucinationScorerИспользует LettuceDetect (
lettucedect-large-modernbert-en-v1, ModernBERT token classifier, F1=79.2% на RAGTruth) для определения, какие части ответа LLM не подтверждены извлечённым контекстом.Возвращает nullwatch
Evalс:eval_key = "rag_hallucination"score— степень уверенности ответа (1.0 = полностью подтверждён контекстом)verdict—"pass"/"fail"на основе доли «галлюцинированных» символов и настраиваемогоfail_thresholdnotes— конкретные span-ы с указанием уверенности моделиmeta— структурированный словарь для downstream-анализаМодель загружается при первом вызове, последующие вызовы переиспользуют детектор.
3.
ToolCallScorerСхемный валидатор tool call, сгенерированных LLM. ML-модель не нужна.
Ловит:
minimum/maximum)function.arguments(формат OpenAI)Поддерживает форматы OpenAI, Anthropic
tool_useи компактную nullwatch-схему. Score = доля валидных вызовов в тёрне.4.
ToolCallGroundingScorerСемантическое дополнение к
ToolCallScorer— проверяет, что значения аргументов есть в предоставленном контексте, а не придуманы моделью.Два бэкенда:
keyword(по умолчанию, zero deps) — извлекает слова из значений аргументов и проверяет их по контексту; операционные аргументы (пути, URL, shell-команды,timeout,max_resultsи т.п.) пропускаются автоматическиllm— отправляет структурированный промпт в любой OpenAI-совместимый API (например локальный Ollama сqwen3:0.6b) для глубокой семантической проверкиВместе с
ToolCallScorerпокрывает как структурные, так и семантические галлюцинации в tool call.5.
BaseScorerАбстрактный базовый класс для кастомных scorer-ов. Реализуй
eval_key,scorer_nameиscore()— и любая логика оценки подключается к nullwatch.6. Provider helpers
Best-effort адаптеры для извлечения метрик из ответов провайдеров без импорта их SDK:
7. Testing utilities
MemoryTransport— in-memory замена HTTP-слоя для тестов без запущенного nullwatch:8. CLI
Что сознательно не входит в этот PR
Использование
Установка
Также доступно на Test PyPI как ранний preview.
Валидация
Все тесты используют локальный mock HTTP-сервер — запущенный nullwatch для прогона не требуется.
Пример реального использования — nullclaw-python-tg-bot
В рамках хакатона мы также собрали
nullclaw-python-tg-bot— Telegram-бот поверх nullclaw, который используетnullwatch-pyкак единственный способ взаимодействия с nullwatch.Бот демонстрирует полный стек в действии:
nullwatchnullclawollamabotnullwatch-pySDK внутриЧто демонстрирует бот:
/rag <question>— отвечает на вопрос из контекста и сразу прогоняетRAGHallucinationScorer, результат уходит в nullwatch как eval/tool <request>— отправляет запрос в nullclaw-агента, валидирует сгенерированные tool calls черезToolCallScorer+ToolCallGroundingScorer/status— health check всех сервисов черезclient.is_alive()client.span(...)— span автоматически отправляется в nullwatchЭто живое подтверждение того, что SDK работает с реальным nullclaw-агентом и выполняет заданный функционал без ошибок.
Текущий статус
Готов к ревью.