Skip to content

Release 0.3.0 - #98

Merged
piotrekno1 merged 113 commits into
mainfrom
release/0.3.0
Oct 24, 2026
Merged

piotrekno1 merged 113 commits into
mainfrom
release/0.3.0

Conversation

@piotrekno1

@piotrekno1 piotrekno1 commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Release 0.3.0 to main. Tag v0.3.0 on the merge commit.

Highlights (full list in CHANGELOG):

  • New integrations: mlx_whisper and faster_whisper, OpenAI responses.create() and embeddings.create(), Anthropic messages.stream().
  • The llama.cpp integration is renamed llama_cpp (gguf remains an alias) and now tracks chat and completion calls.
  • Instrumentation ownership: init(dsn=...) with the runner's DSN reuses its client. A conflicting second client warns, and --strict-integrations raises (Release 0.3.0 #96).
  • Fixes: duplicate events from flush() (Release 0.3.0 #96), transformers double counting, timm and ONNX model naming, Hugging Face hub patch importing unused transformers modules (Stop the Hugging Face hub patch from importing unused transformers modules #97).

This PR also moves the Hugging Face hub entry from Unreleased into the 0.3.0 section, since #97 merged before tagging.

Verification on this branch

  • 910 passed, 28 skipped on Python 3.10, 3.13 and 3.14. Ruff is clean. The wheel and sdist pass twine check. check_tag_version.py, the release notes script and llms.txt all run.
  • All examples ran against local ingest through a logging proxy. Every batch was accepted, with no rejected events and no duplicate event IDs. This covers local models, both Whisper backends, attachments, OpenAI, Responses, embeddings, Anthropic, agentic, llm_api, the cli wrapper, FastAPI with --reload, Django with waitress, and the README snippets.
  • Checks for the fixes: the demo app's init() pattern under wildedge run, the same-DSN, other-DSN and strict cases, the flush race script (0 duplicates), a real fork(), and the Hugging Face hub patch (no extra imports, download functions still tracked).

🤖 Generated with Claude Code

piotrekno1 and others added 30 commits September 30, 2026 12:07
Merge release/0.2.2 into devel
The gguf integration patched only Llama.__call__, but
create_chat_completion() runs llama.create_completion() through the chat
handler, so chat apps produced no inference events. Patch
create_completion instead: __call__, create_completion and
create_chat_completion all run it, so each call is tracked once.

patch_instance_call_once takes a method name (default __call__).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…N comes from

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README: PyPI badge, links to the app, docs and blog, and where the DS…
mlx 0.22.0 is gone from PyPI, which broke the daily run. Current pins move to the latest releases; numpy 2.2.6 is the newest that covers 3.10 to 3.13, with 3.10 overrides for onnxruntime and keras. Anthropic had pins and a test but was missing from the CI matrix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
create_chat_completion now exposes its messages to the create_completion it
runs, so chat events get prompt_type="chat" and turn_index while tracking
stays in one place. Non-streamed calls report finish_reason, every call
reports its sampling arguments with the method's defaults filled in, and with
attachments enabled the prompt or messages and the reply are attached.

Closes #64
Every compat test used importorskip, so a broken install skipped and the
job passed (ultralytics min on 3.12/3.13 did), and half of them only called
instrument(). Each test now loads a real model with the pinned library and
checks the load and inference events, without network access: canned HTTP
for openai and anthropic, small committed ONNX and GGUF files, and models
built from config for the rest. A conftest hook fails any skip.

Bugs found are marked xfail(strict=True) with their issue: #64, #70, #71,
#72, #73. tests/compat is left out of the regular pytest run.
…el PRs

The workflow's matrix was hand-written and drifted from the script
(anthropic was missing), and pull requests into devel never ran compat.
compat_matrix.py now builds the matrix: a pr profile with each
integration's min and current pins, and the full nightly profile. A verify
step checks the installed versions match the pins. tests/test_compat_matrix.py
keeps the script, the compat tests and docs/compatibility.md in step.
ultralytics 8.0.0 imports pkg_resources without declaring setuptools, and
Python 3.12+ environments no longer include it. The old compat test skipped
here; with skips now failing, the full run caught it.
Every devel pull request running the matrix is more than needed; devel gets
the nightly run. Pull requests into main or a release branch run the full
matrix, so the separate pr profile is gone.
The gguf compat test fetches the 1.2 MB TinyStories model from a pinned
Hugging Face revision on first use and checks its sha256. A broken link or
changed file fails the test with the URL to update. Models are cached in
tests/compat/.cache, which the gguf jobs keep between runs.
Fix the compat matrix and make it test each integration
Replaces the #64 xfail with compat tests for create_completion (stop reason
and generation config with defaults), create_chat_completion (prompt_type,
turn_index) and streamed chat, each tracked once. They fail on devel and
pass with this branch on llama-cpp-python 0.2.90 and 0.3.36.
The example only called llm(...). It now runs three streamed
create_chat_completion turns and one create_completion, so it exercises the
chat tracking from this PR.
Lint was only run by hand, so misplaced imports and similar slips reached
review. CI gets a lint job running ruff check and ruff format --check, and
the rule set adds bugbear (B), comprehensions (C4), PIE, import placement
(PLC0415), pytest style (PT), RET, RUF and SIM. Test doubles may keep
mutable class attributes (RUF012).
Mostly mechanical: sorted __all__, removed unused noqa directives, dropped
else after return, dict literals, itertools.pairwise, ClassVar on the client's
class-level tables, contextlib.suppress in the hub cache walk, and an
explicit zip strict flag (strict where lengths must match, not where a list
can fall back to empty). llm_api imports get_client at module level: the
import cycle its noqa mentioned no longer exists.
Imports move to module level, except numpy, which stays optional through
pytest.importorskip; the sampler test reads platforms._sampler at call time
since start_sampler replaces it. Also tuple parametrize names, split
composite asserts, combined with statements, match= on pytest.raises, raise
from None in fake async iterators, and a duplicate parametrize case removed.
Track llama-cpp chat and completion calls in the gguf integration
Ingest rejects the whole batch with 422 when a span attribute value is a
list or dict, so one bad value dropped every unrelated event sent with it.
SpanEvent.to_dict now sends such values as JSON strings and logs one
warning per key.

Closes #61
Send non-scalar span attributes as JSON strings
close() stopped the uploader without a final pass, so attachments captured
in the last few seconds stayed on disk while their events were sent. With a
timeout, close() and flush() now drain the attachment store in the time left
after the event flush. Each request's timeout is capped at that deadline, and
a lock keeps the background thread and the drain from sending the same
attachment.

Closes #62
Upload buffered attachments on close and flush with a timeout
main_graph, the default graph name from newer torch.onnx.export, wasn't in
the generic list, so every Piper voice registered as one model called
main_graph. It now falls back to the session's file name, which also gives a
manually registered session an id. model_version 0, negative or INT64_MAX
counts as unset.

Closes #63
Improve README quickstart and add demo preview
Add compact trace and SQL previews to README
openai 1.30.0 passes proxies= to httpx, which httpx 0.28 removed.
…s a change

Drops the llama.cpp line about automatic prompt and reply attachments, which
the explicit-attachments change removed.
The fork hooks hold the drain lock across fork() so a child never inherits
it locked.
init(dsn=...) now reuses the default client when the DSN points to the same
project. A second client enabling integrations while another live client owns
them logs a warning and leaves automatic tracking with the owner, as 0.2.2
did silently. wildedge run --strict-integrations keeps the RuntimeError.
getattr on transformers' lazy modules imported unused model modules and
logged a debug line for each one that failed to import.
Stop the Hugging Face hub patch from importing unused transformers modules
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Changelog preview for v0.3.0

Preview of the release notes that will be generated when this is tagged.

Added

  • mlx_whisper and faster_whisper integrations track local Whisper model
    loads and transcriptions, including faster-whisper's
    BatchedInferencePipeline. Transcriptions record duration, language, word
    count and audio metadata. Audio and transcript attachments must be supplied
    explicitly through the manual tracking API. See docs/whisper.md.
  • The OpenAI integration now tracks responses.create() for synchronous and
    asynchronous clients, including streaming. Records input/output token usage,
    cached and reasoning tokens, response status or incomplete reason, resolved
    model metadata, and time to first output delta for streams. Older OpenAI SDKs
    without Responses remain supported.
  • OpenAI sync and async embeddings.create() instrumentation records latency,
    input token usage, batch size, vector dimensions, model metadata and errors.
    Embedding vectors and raw inputs are never attached by this adapter.
    EmbeddingOutputMeta now accepts optional tokens_in for analytics.
  • Anthropic native messages.stream() helpers are now instrumented for sync
    and async clients, including event iteration, text_stream, and final-message
    helpers, with the shared streaming lifecycle contract.
  • Process-wide integrations and hub patches now support sequential SDK client
    replacement after the previous owner closes. Reinitialization replaces a
    closed default. Existing models and provider clients keep their original
    destination and must be recreated. Attempts to use a closed telemetry
    destination warn once instead of silently dropping inference and error
    events.

Changed

  • The llama.cpp Python integration is now named llama_cpp in Python, CLI,
    and environment configuration. gguf remains a compatibility alias; all
    selects the integration once under its new name. Model format metadata
    remains gguf.
  • wildedge.init(dsn=...) reuses the existing default client when the DSN
    points to the same project, for example dsn=os.environ["WILDEDGE_DSN"]
    under wildedge run. It used to create a second client.
  • Enabling an integration or hub on a second client while another live client
    owns process-wide instrumentation now logs a warning. Automatic events keep
    going to the owner's project, as before, but this used to happen without a
    word. With wildedge run --strict-integrations it raises RuntimeError.
  • Quantization labels are defined once in wildedge.quantizations and used
    by every integration. Behaviour changes:
    • MLX-LM reports N-bit weights as int4/int8, like Transformers, instead
      of q4/q8.
    • GGUF models labelled from general.file_type report the type name (for
      example q4_k_m) instead of its number, and the i8 filename suffix
      reports int8.
    • faster-whisper reports weight precision (int8, f16) instead of
      CTranslate2 compute types such as int8_float32.
  • The ultralytics integration now requires ultralytics 8.0.200 or later
    (was 8.0.0). Earlier 8.0.x releases keep their helpers under
    ultralytics.yolo.utils, and the integration turned itself off on them
    without a word. instrument("ultralytics") now logs a warning naming the
    installed version instead.

Fixed

  • Every error event now caps error_message at 500 characters, keeping the
    start and the end of a longer message. Integrations used to cut it to the
    first 200, which lost the cause in messages that end with it, such as
    mlx-whisper audio errors that start with the ffmpeg banner. Manual
    track_error() calls had no limit at all. provider_error_code is capped
    at 64 characters for every event.
  • The llama.cpp integration now tracks Llama.create_chat_completion() and
    Llama.create_completion(), streaming or not. It used to patch only
    Llama.__call__, so chat apps on llama-cpp-python reported model loads but
    no inference events. It now patches create_completion, which every entry
    point runs, so each call is tracked once.
  • llama.cpp inference events now include the stop reason for non-streamed
    calls, the generation config (temperature, top_p, top_k, max_tokens,
    repeat_penalty, penalties, seed, stop sequence count), and, for chat
    calls, prompt_type="chat" and turn_index measured on the last user
    message. Prompts, chat messages and replies are never attached. As before,
    attachments upload only when passed to track_inference(attachments=[...]).
  • import wildedge no longer imports every installed ML framework and API
    client. Each library is imported when its integration is activated or a
    model from it is registered, so SDK import is faster and a broken unrelated
    package no longer stops the SDK from starting.
  • Changing a model's metadata (for example register_model(..., version="v2"))
    now takes effect after a restart. With offline persistence on (the default),
    the model registry saved by an earlier process kept the first metadata ever
    registered for each model on that machine, so later changes were ignored.
    This includes anthropic models already saved as "unknown".
  • MLX-LM models now report the device they run on (mps for the Apple GPU)
    instead of always cpu. Accelerator names are defined once in
    wildedge.accelerators, and PyTorch, Transformers, Ultralytics,
    TensorFlow and Keras share their device detection.
  • timm models now use the requested architecture/variant name for model identity
    instead of the Python class name, so models such as resnet18 and resnet50 no
    longer share telemetry. Manual registration falls back to timm architecture
    metadata, and explicit model IDs retain priority.
  • Ultralytics model.predict() now records inference telemetry. Instrumentation
    patches predict, which model(...) delegates to, so both entry points are
    tracked exactly once.
  • Streaming wrappers now finalize once across exhaustion, explicit closure,
    cancellation, and iterator errors. Early closure/cancellation records an
    unsuccessful inference with partial metadata when available. TTFT ignores
    lifecycle/role-only chunks; telemetry callbacks cannot break iteration and
    retain the trace context captured at stream creation. Remote API streams
    ending without their completion marker record STREAM_INCOMPLETE.
  • A span attribute whose value is a list, dict or other non-scalar no longer
    gets the whole event batch rejected. Ingest only accepts scalar attribute
    values and answered 422 for the batch, dropping unrelated loads, inferences
    and spans. Such values are now sent as JSON strings, with one warning per
    key.
  • close(timeout=...) and flush(timeout=...) now upload buffered
    attachments in the time left after sending events. close() used to stop
    the attachment uploader without a final upload, so a short-lived process
    lost the attachments of its last few seconds (the uploader runs every 5 s)
    while its events still referenced them. close() without a timeout still
    leaves them on disk for the next start.
  • ONNX models exported with the default main_graph graph name (newer
    torch.onnx.export, Piper TTS voices) are now named after their file, so
    different voices no longer show up as one model called main_graph. A
    session created without an id falls back to the file name too, and
    model_version 0, negative or INT64_MAX (the exporters' "unset") is sent as
    unknown instead of 9223372036854775807.
  • transformers pipelines called with a string or a list record one inference
    event per call instead of two. pipeline() loads the model before
    Pipeline.__init__ runs, so the model was registered with its own forward
    hooks, and each pipeline call was tracked by the pipeline and again by the
    model inside it. That model's hooks now skip forward passes inside a
    tracked call of its pipeline; other models the pipeline runs, and direct
    calls to the model, are still tracked. Generator and dataset inputs, which
    run lazily, are not covered yet (transformers pipelines with generator or dataset inputs record events before inference runs #78).
  • flush() no longer sends a batch the background sender is already sending.
    Both used to send the same events and then each removed a batch from the
    queue, so events were duplicated and, depending on timing, later events
    could be removed without being sent. This showed up when a process started
    with events saved by an earlier run and flushed soon after.
  • Enabling the huggingface hub no longer imports unused transformers model
    modules. To find references to hf_hub_download and snapshot_download,
    the hub tracker looked up the name on every loaded module, and transformers'
    lazy modules import code on lookup. With transformers loaded this imported
    about 140 extra modules, took about 0.15 s and logged about 430 "failed to
    patch" debug lines. The tracker now reads each module's namespace instead.

Full Changelog: v0.2.2...v0.3.0

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Coverage report

  • Total: 90% (12173/13583 lines)

Top uncovered files

  • wildedge/integrations/transformers.py: 63.2% (111 lines missed)
  • wildedge/integrations/mlx.py: 43.0% (98 lines missed)
  • wildedge/integrations/ultralytics.py: 67.8% (84 lines missed)
  • wildedge/integrations/pytorch.py: 64.5% (70 lines missed)
  • wildedge/platforms/macos.py: 57.1% (67 lines missed)

The integrations cut error messages to ERROR_MSG_MAX_LEN and provider codes
to PROVIDER_ERROR_CODE_MAX_LEN before calling track_error(). Manual calls
sent the full text. ErrorEvent now applies both limits when serializing, so
every error event gets them.
Measured across 37 typical failures, 9 messages were over 200 characters.
Several put their cause at the end. mlx-whisper audio errors start with the
ffmpeg banner, and torch state_dict errors end with the shapes. The
integrations kept only the first 200 characters.

ErrorEvent now keeps the start and the end of a message over 500
characters, with an ellipsis between. The integrations and decorators no
longer cut the message themselves, so the limit is applied once.
The notes listed every commit subject since the previous tag, including
merge commits and release housekeeping such as changelog moves. They are
now the curated CHANGELOG.md section for the version. A missing or empty
section fails the build instead of publishing empty notes.
Cap error fields in every error event and build release notes from CHANGELOG.md
0.2.x already uploaded only explicit attachments. Automatic GGUF capture
was added and removed during 0.3.0 development and never shipped, so the
entry described no change for users upgrading.
@piotrekno1
piotrekno1 merged commit 3de0b85 into main Oct 24, 2026
95 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant