Skip to content

AbstractCore v2.30.0

Choose a tag to compare

@github-actions github-actions released this 26 Oct 21:17
· 152 commits to main since this release
652f29f

First published release since 2.26.3: it also ships the changes listed under 2.26.4
through 2.29.0 below. The AbstractVoice, AbstractVision, AbstractMusic and Abstract3D
plugins are pinned to their latest releases.

Added

  • Voice navigation by engine and language: llm.voice.list_voices(provider=..., model=..., language=...), list_cloned_voices(...) and voice_catalog(language=...); GET /v1/audio/voices
    and the new GET /v1/voice/clones take language. Every voice record carries voice_id (the
    value for voice=), name, kind (profile | clone), provider, model, languages and the native
    language when known; native speakers of the requested language come first.

  • Languages per engine: llm.voice.list_languages(kind=..., provider=...) and
    GET /v1/audio/languages list the languages of every TTS, STT and cloning engine without loading
    a model.

  • Cloned-voice management: llm.voice.get_cloned_voice, rename_cloned_voice,
    set_cloned_voice_reference_text, delete_cloned_voice, and GET|PATCH|DELETE /v1/voice/clones/{voice_id}. Built-in voices are read-only (422); an unknown id is 404.
    PATCH, DELETE and POST /v1/voice/design need server authentication (or
    ABSTRACTCORE_SERVER_ALLOW_UNAUTHENTICATED=1); a provider key does not authorize them.

  • Voice design: llm.voice.design_voice(description, name=..., language=..., text=...) and
    POST /v1/voice/design create a reusable voice from a written description (Qwen3-TTS
    VoiceDesign, then cloned; needs the qwen3-tts and stt extras of AbstractVoice, included in
    abstractcore[apple] and abstractcore[gpu]).

  • one_shot on /v1/audio/speech: the whole text as one generation, so the take keeps one
    voice (Qwen3-TTS voices, up to 1000 characters). The stream route and remote models answer 422.

  • llm.voice.compatibility_catalog(): feature support per provider and model.

  • Guide: choosing a model for knowledge-graph extraction:
    a measured comparison of local and hosted models on ontology-constrained entity and relationship
    extraction (precision, coverage, time), the prompt rules and second-pass strategies that change
    results, and recommendations.

  • Registry entries: claude-haiku-5-5 (1M context, 128K output, adaptive thinking, effort
    low..xhigh plus max) and gpt-6-luna (1,050,000 context, 128,000 output,
    reasoning_effort none..xhigh plus max), from the vendors' model pages (2026-10-25).

  • Mailbox access is an explicit choice: EmailAccount.access, "read" (Read only) or "write"
    (Read and write), stored in to_dict() / from_dict(); access_text says it in words.
    can_write is the choice (it was "IMAP and SMTP"); can_send is can_write and an outgoing
    server set. An account stored without it keeps what it could do: "write" with an outgoing
    server, else "read". EmailAccountStore.set_access(access, *, smtp=None, test=True) changes it
    without connecting again (a new outgoing server is signed in to first with the stored sign-in);
    public() has access / access_text; abstractcore email status and the terminal console's
    Email line say "Read only" / "Read and write".

Changed

  • Plugin floors: abstractvoice>=0.18.2, abstractvision>=0.3.34, abstractmusic>=0.1.17,
    abstract3d>=0.3.2 (base install and the apple, gpu, all-apple, all-gpu and vision-*
    extras).
  • POST /v1/voice/clone returns the stored record of a local clone under voice (name, engine,
    reference text, meta).
  • A one_shot TTS request the voice backend refuses (too long, unsupported engine) answers 422
    with the backend's reason.
  • A voice request for an engine missing from this install (AbstractVoice names the extra to
    install) answers 501 with the install command instead of 500.
  • A read-only mailbox refuses changes and sends in plain words: "The mailbox is
    read-only: automations only read it." / "Turn on Read and write for to change it or
    send from it." (email_mailbox_read_only). guarded_send on a read-only account with an
    outgoing server now refuses with EmailMailboxReadOnly before connecting.

Fixed

  • The HuggingFace provider's MoE experts fallback no longer raises ModuleNotFoundError when
    transformers is not installed; it answers "not applicable" as intended.
  • A configured reasoning default overrode the caller's reasoning_effort=: with
    reasoning: minimal on the text route, generate(..., reasoning_effort="high") (or "none")
    ran as thinking="minimal", mapped to the model's "low", and sent reasoning_effort: "low". Precedence is now thinking= >
    a provider-native field on the call (reasoning_effort=, reasoning=, think=, sent as
    given) > the configured effort > nothing. The configured effort also applies only to an
    instance the route describes (same provider or endpoint profile, same model; a reasoning-only
    route applies everywhere): it used to reach every model on the machine. The planner behind
    prompt_cache_prepare_modules(thinking=None) follows the same rule. Metadata gains
    thinking_source (thinking | reasoning_effort | configured_default); a raw
    reasoning_effort= is reported as thinking_requested, and as thinking_effective on
    providers that forward it verbatim; the route summary reports an unapplied default as
    reasoning: None, reasoning_source: not_applied:<reason>.
  • An explicit output budget was cut to a guessed ceiling on unregistered models:
    max_tokens=12000 on a model missing from model_capabilities.json was clamped to the
    4096 default_capabilities guess, truncating JSON output. For an unregistered model the per-call
    budget is now sent as given and the server enforces its real limit; registered models and
    constructor caps still clamp, with the warning naming the actual source. A GGUF n_ctx clamp
    stays in force for unregistered local models.
  • LM Studio thinking= reached the model only for "off": the control rode
    chat_template_kwargs, which LM Studio's OpenAI-compatible endpoint ignores, while metadata
    reported thinking_effective="on". The unified control now travels as the top-level
    reasoning_effort ("none" = off, the declared level, or "medium" for on), which LM Studio
    maps into the chat template on plain, message-history and tool-history requests, sync and
    async. thinking="off" no longer appends the assistant
    no-think prefill on top (it rendered the empty think block twice); the prefill remains the
    off switch only after the server rejected reasoning_effort. Models without a surface LM
    Studio can drive (Kimi thinking param, budget-only, surface-less) are no longer claimed.
    thinking= no longer routes to the native /api/v1/chat endpoint, which dropped the control
    for history/tool/async requests while still claiming it; reasoning= keeps that lane.
  • vLLM thinking controls never reached the server: they were posted inside a literal
    extra_body JSON field, which vLLM ignores. chat_template_kwargs, reasoning_effort,
    thinking_token_budget and the other vLLM extensions are now sent at the top level, and a
    caller's reasoning_effort= / chat_template_kwargs= are forwarded. The template switch is
    sent under the name the model declares (no guessed enable_thinking claimed for models that
    have none); models with levels and no switch (DeepSeek V4, Mistral Small 4) get
    reasoning_effort; GPT-OSS gets reasoning_effort; Seed-OSS gets thinking_budget;
    Nemotron low_effort is sent; a token budget alone is no longer reported as an applied level.
  • OpenRouter ignored thinking=: it is now sent as OpenRouter's reasoning object
    (effort / enabled), with the lowest declared effort (warned) for models whose reasoning
    cannot be disabled.
  • MLX text-lane usage counted the prompt with a character estimate although the tokenizer
    was loaded. The prompt is now counted with
    the loaded tokenizer exactly as mlx_lm tokenizes it; the estimate is used only when no
    tokenizer can count.