Skip to content

Release 0.13.3a2 - #69

Open
github-actions[bot] wants to merge 64 commits into
masterfrom
release-0.13.3a2
Open

Release 0.13.3a2#69
github-actions[bot] wants to merge 64 commits into
masterfrom
release-0.13.3a2

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Human review requested!

renovate Bot and others added 30 commits December 18, 2025 03:33
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* chore: migrate setup.py→pyproject.toml, consolidate CI workflows

Replace setup.py with pyproject.toml (dynamic version from version.py).
Add build_tests, license_tests, lint, pip_audit workflows using
OpenVoiceOS/gh-automations@dev reusable workflows. Migrate
publish_stable and release_workflow from TigreGotico/gh-automations@master
to OpenVoiceOS/gh-automations@dev; add workflow_dispatch trigger and
bot-safety guard.

split from #25

* fix: define __version__ attr required by pyproject dynamic version

* test: guard dynamic-version contract (version.py ↔ __version__ fields)

Asserts VERSION_* integers are non-negative, __version__ is correctly
derived from them, and the string matches PEP-440 format.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…t in chat.py (#40)

* fix: declare uvicorn+ovos-workshop deps; restore py3.9 compat in chat.py

Fixes #39 (partial — OPM lang-detector fallback fix in ovos-lang-detector-classics-plugin,
see fix/issue-39-cld2-exception branch there)

(a) Add uvicorn and ovos-workshop to [project].dependencies in pyproject.toml.
    Both are imported at runtime (__main__.py and ollama.py respectively) but
    were not declared, causing ImportError in clean environments.

(b) Replace two multi-line f-string expressions in streaming_completion_response
    (chat.py lines ~285 and ~302) with intermediate dict variables.
    Multi-line dict literals inside f-string {} require Python 3.12 (PEP 701)
    but the package declares requires-python >= 3.9.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Delete tests/test_issue_39.py

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…aths (#29)

* feat: vendor-prefixed OpenAI/Ollama routers + deprecated legacy paths

* test: cover deprecated paths middleware and route registration (split/router-infra)

- Verify Deprecation + Link headers on /v1 and /api legacy paths
- Verify canonical paths receive no deprecation headers
- Cover _build_successor_path mapping and passthrough
- Cover FastAPI 422 responses for malformed/missing bodies
- Smoke-test register_deprecated_routes mounts /v1/ legacy paths

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: normalize Google-style docstrings in chat and ollama routers

Remove ``(Type)`` annotations from Args/Returns sections (redundant with
function signatures). Condense multi-line boilerplate docstrings on
lifespan managers and inner streaming generators to one-liners.
No logic changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: A2A server endpoint (/a2a) with agent card + executor

* test: full A2A server coverage — agent card, executor, cancel, SDK-absent path, invalid base_url

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: improve OVOSPersonaAgentExecutor.__init__ docstring

Add summary sentence to the __init__ docstring; no logic changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
)

* feat(compat): Anthropic Claude-compatible endpoints (/anthropic/v1)

* test: full Anthropic compat coverage — schema, multi-turn, system, streaming, errors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: real-SDK e2e against live server + enable CI test execution

Add tests/e2e/test_e2e_anthropic.py driving the official anthropic SDK
(Anthropic(base_url=...)) against a live uvicorn-served app, covering
non-streaming, system prompt, multi-turn and streaming. Add anthropic to
the dev extra and wire build_tests.yml to install it and run the suite.

* test: use asyncio.run in A2A executor tests (py3.14 compatibility)

asyncio.get_event_loop() raises on Python 3.14 (and whenever a prior test
closes the main-thread loop); switch the A2A executor tests to asyncio.run
so the suite is loop-state independent.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(compat): Google Gemini-compatible endpoints (/gemini/v1beta)

* test: full Gemini compat coverage — schema, multi-turn, system-instruction, streaming, errors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: real-SDK e2e against live server + enable CI test execution

Add tests/e2e/test_e2e_gemini.py driving the official google-genai SDK
(genai.Client(http_options=HttpOptions(base_url=...))) against a live
uvicorn-served app, covering non-streaming, system instruction, multi-turn
and streaming. Add google-genai to the dev extra and wire build_tests.yml
to install it and run the suite.

* test: use asyncio.run in A2A executor tests (py3.14 compatibility)

asyncio.get_event_loop() raises on Python 3.14 (and whenever a prior test
closes the main-thread loop); switch the A2A executor tests to asyncio.run
so the suite is loop-state independent.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(compat): AWS Bedrock-compatible endpoints (/bedrock/model)

* test: full AWS Bedrock compat coverage — all model families, invoke, stream, converse, schema errors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(compat): emit AWS event-stream framing for Bedrock streaming

invoke-with-response-stream now encodes vnd.amazon.eventstream binary frames
(prelude/headers/payload with CRC32 checksums) instead of SSE, so boto3's
invoke_model_with_response_stream parses the stream natively.

Add tests/e2e/test_e2e_bedrock.py driving the official boto3 bedrock-runtime
client (invoke_model for Claude + Titan, converse, streaming). Add boto3 to
the dev extra and wire build_tests.yml to run the suite.

* test(compat): decode Bedrock stream as event-stream frames in unit tests

Update the invoke-with-response-stream unit tests to decode the
vnd.amazon.eventstream binary framing via botocore's EventStreamBuffer,
matching the streaming format boto3 consumes.

* test: use asyncio.run in A2A executor tests (py3.14 compatibility)

asyncio.get_event_loop() raises on Python 3.14 (and whenever a prior test
closes the main-thread loop); switch the A2A executor tests to asyncio.run
so the suite is loop-state independent.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(compat): HuggingFace TGI-compatible endpoints (/tgi)

* test: full TGI compat coverage — schema, generate, streaming, info/health, errors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(compat): serve TGI generation at endpoint root for InferenceClient

The maintained huggingface_hub InferenceClient posts to the bare endpoint
URL and selects streaming via the body 'stream' flag; add a root dispatch
route delegating to the shared non-streaming/streaming handlers so the
official client works against the server unchanged.

Add tests/e2e/test_e2e_tgi.py driving InferenceClient (non-stream, details,
stream) plus /health, /info and the native /generate route. Add
huggingface_hub to the dev extra and wire build_tests.yml to run the suite.

* test: use asyncio.run in A2A executor tests (py3.14 compatibility)

asyncio.get_event_loop() raises on Python 3.14 (and whenever a prior test
closes the main-thread loop); switch the A2A executor tests to asyncio.run
so the suite is loop-state independent.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
JarbasAl and others added 30 commits June 12, 2026 17:42
* docs: user-facing documentation for all API surfaces

* docs: fix broken links in index.md; add docs column to API table

Remove references to a2a.md and bedrock-models.md (not yet written).
Add a Docs column to the API surfaces table pointing to anchors in
api-compatibility.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The /openai/v1 and /ollama/api surfaces shipped with only mocked unit tests.
Add e2e tests driving the official openai and ollama clients against a live
server (chat/completions/models and chat/generate/tags, streaming included).

Fix an Ollama-compat bug the e2e surfaced: non-streaming /generate returned a
chat 'message' object instead of the 'response' field the official client
reads (the streaming path already used 'response'). Add openai+ollama to the
dev extra and enable CI test execution.
The repo had two parallel trees: tests/ (run by CI's test_path) and a stale
test/unittests/ that duplicated the compat tests already in tests/. Drop the
duplicates and keep the two unique files — test_a2a.py becomes test_a2a_unit.py
and test_compat_routers.py moves into tests/ (its /generate assertion updated
to the 'response' field the endpoint now returns). One test directory remains.
* feat: OpenAI-compatible Files + Vector Stores API on the current app factory

Re-architect the RAG subsystem onto dev's prefixed-router structure:

- Mount files (/openai/v1/files) and vector_stores (/openai/v1/vector_stores)
  routers; the /embeddings path is already served by chat_router, so the
  duplicate embeddings router is not mounted (its get_text_embeddings helper
  still powers vector-store search via a dedicated embeddings plugin).
- Initialise the SQLite schema from a single app-level lifespan (init_db);
  drop the per-router lifespans that eagerly loaded embedding plugins and
  crashed startup when those plugins were absent.
- Add the SQLite/file deps (sqlalchemy, aiosqlite, python-dotenv,
  python-multipart) to the core requirements and a 'rag' extra for the
  chunker/reranker plugins.
- Add tests/e2e/test_e2e_rag.py driving the official openai SDK against a
  live server for the files and vector-store lifecycle; enable CI test
  execution and use asyncio.run in the A2A tests for py3.14.

Replaces the pre-refactor feat/rag history, which predated the vendor-prefixed
routers, deprecated-route shims and A2A endpoint now on dev.

* feat: unify embeddings behind one swappable backend across all routers

The OpenAI /openai/v1/embeddings, Ollama /ollama/api/embed (batch) and legacy
/ollama/api/embeddings (prompt) endpoints now delegate to a single
get_embeddings_backend dependency — the embeddings analogue of the shared
persona. Configure it via TEXT_EMBEDDINGS_PLUGIN / EMBEDDINGS_URL /
EMBEDDINGS_MODEL to point at any embeddings service, with a persona-solver
fallback when no plugin is available. Adds the /ollama/api/embed endpoint and a
faithful legacy /ollama/api/embeddings shape so the official ollama client works,
and base64 encoding so the official openai client works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(rag): add real cross-plugin e2e test for GGUFEmbeddings + chromadb/qdrant RAG path

- config.py: pass `model` into gguf embeddings config (defaults to
  all-MiniLM-L6-v2) so CI uses the 46 MB 384-dim model instead of labse
- vector_stores.py: store chunk `content` in embedding metadata so search
  results can return it (was commented out, causing KeyError)
- tests/e2e/test_e2e_rag_plugins.py: real no-mock RAG e2e parametrised
  over chromadb and qdrant; uploads 3 distinct-topic docs, embeds with
  GGUFEmbeddings, searches and asserts correct doc ranks first

* feat: shared embeddings backend across all vendor surfaces

Route the Cohere embed endpoint through the shared embeddings backend and add
parity embedding endpoints to Gemini (:embedContent / :batchEmbedContents),
HuggingFace TGI (/embed), and AWS Bedrock (Titan/Cohere embed models via
/invoke), all delegating to get_embeddings_backend. Anthropic is intentionally
left without one (no first-party embeddings API).

Also make the merged server actually deployable:
- pin sqlalchemy[asyncio] (greenlet) and ovos-persona>=0.9.0a5
- pass a Session to persona.chat/stream via run_chat/run_stream (ovos-persona
  0.9 requires it); fixes a 500 on every chat endpoint
- adapt vector-store DB instantiation to plugins taking config= or path=
- fix an UnboundLocalError that masked RAG-import failures

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: cross-plugin RAG e2e + deploy-realistic e2e workflow

Add test_e2e_rag_solver: the ovos-openai-plugin OpenAIRAGSolver drives a live
server (real gguf embeddings + chromadb vector store) through the new
/vector_stores/search endpoint, then /chat/completions via the real openai chat
solver with only the upstream LLM call mocked.

Add .github/workflows/e2e.yml running tests/e2e on the full real stack
(install_extras=e2e), pulling the unreleased companions (openai-plugin rag
branch, chromadb dev) via pre_install_pip.

Update existing tests for the shared backend + Session call contract:
cohere/router_infra/a2a unit tests and vendor e2e stream mocks (accept **kwargs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: require all e2e deps, remove importorskip guards

The [e2e] extra now installs the full union of what tests/e2e imports — every
vendor client SDK (openai, ollama, cohere, google-genai, boto3, anthropic,
huggingface_hub), mcp, and the embedding/vector-store plugin stack — and all
pytest.importorskip guards are removed. A missing dependency now fails the run
loudly instead of silently skipping a test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: RAG/vector-store guide, examples, and embeddings parity

Add docs/rag.md (Files + Vector Stores + RAG flow), an examples/ directory
(openai-SDK flow, RAGSolver flow, multi-client embeddings, persona config), and
update README, docs/index.md, and docs/embeddings.md for the shared embeddings
backend and the new per-vendor embed endpoints.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: model RAG as a persona memory plugin, not a solver

Replace the client-side RAGSolver e2e with test_e2e_rag_memory: a real Persona
whose memory_module is ovos-openai-rag-memory-plugin (PersonaServerRAGMemory)
searches the live server's vector store and injects retrieved context — proving
the cross-repo path against gguf + chromadb. Update e2e.yml to install the three
unreleased companions (openai-plugin rag, chromadb dev, ovos-persona memory-config).

Docs: rewrite the RAG client section around the memory plugin, add the drop-in
OpenAI-replacement use case, and refresh examples.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: cover RAG memory inject_mode=tool in the e2e suite

Parametrize the cross-plugin memory e2e over inject_mode (system, tool): the tool
case asserts the retrieved context arrives as a MessageRole.TOOL result paired with
a matching assistant tool_call. Update the import for the renamed ovos_openai_plugin
package, and point e2e.yml's pre_install_pip at the openai opm-agents branch plus the
ovos-plugin-manager tool-calling branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: honor tools on /chat/completions instead of silently ignoring

The OpenAI chat endpoint accepted `tools`/`tool_choice`/`parallel_tool_calls` and
dropped them (echoed into the response, never acted on). Now, when `tools` is sent,
the request is routed to the persona's tool-capable chat engine (supports_tools) and
any tool_calls are returned (finish_reason="tool_calls") for the client to execute —
the server is a stateless function-calling passthrough (it does not run the tools).
Incoming assistant tool_calls / tool-role messages are rebuilt as AgentMessages so
multi-turn function-calling round-trips. If no tool-capable engine is configured, or
stream=true is combined with tools, a 501 is returned rather than silent
non-compliance. No behavior change when `tools` is absent.

Unit CI installs the ovos-plugin-manager tool-calling branch (chat.py now imports
the tool contract) until it releases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: server-side CHAT_MEMORY toggle (backend vs hosted-agent)

The chat path defaults to a stateless backend (CHAT_MEMORY=off): the client
owns conversation state and drives Files/Vector-Stores itself. Set
CHAT_MEMORY=transparent for a single-user hosted agent where the server folds
the persona's memory_module (history + RAG) into every turn via
build_conversation_context + update_history, keyed by the OpenAI `user` field.

The toggle lives in run_chat/run_stream so every vendor surface honors it
uniformly; tool/function-calling requests stay a stateless passthrough.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(deps): pin released opm>=2.7.0a1 and ovos-persona>=0.9.0a6; trim CI git-refs

The tool-call contract (ToolCall, MessageRole.TOOL) is released in
ovos-plugin-manager 2.7.0a1 and memory-plugin config passing in ovos-persona
0.9.0a6, so pin both in pyproject and drop their pre_install_pip git-refs:
build_tests no longer needs any git-ref; e2e keeps only the still-unreleased
ovos-openai-plugin@opm-agents (PR #54) and ovos-chromadb-embeddings-plugin@dev.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(deps): pin released ovos-openai-plugin>=2.0.8a1 in [e2e]; drop its git-ref

PersonaServerRAGMemory is published in ovos-openai-plugin 2.0.8a1, so add it to
the [e2e] extra and remove the openai git-ref from e2e.yml. Only the still-
unreleased ovos-chromadb-embeddings-plugin@dev remains as a pre_install_pip ref.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…p last git-ref (#58)

ovos-chromadb-embeddings-plugin 0.3.0a4 (collection-aware EmbeddingsDB API) is now on
PyPI, so pin it in the [e2e] extra and remove the final pre_install_pip git-ref from
e2e.yml. The persona agent chain is now fully git-ref-free.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mcp 2.0.0b2 dropped mcp.server.fastmcp.FastMCP, breaking the MCP
server import. The unbounded mcp>=1.0.0 pin let uv resolve that
prerelease under UV_PRERELEASE=allow, failing every build_tests job.
)

`main()` passes `--persona` (default `None`) straight into
`create_persona_app()`, which did `with open(persona_path)` unconditionally — so
running `ovos-persona-server` with no `--persona` crashed with
`TypeError: expected str, bytes or os.PathLike object, not NoneType`.

The config docstrings promise the opposite ("If not provided, LLM settings from
environment variables are used"), and `Settings.persona_config` already builds
exactly that env-var-driven default persona — but it was dead code, wired to
nothing. Use it when no persona path is provided, restoring the documented
fallback and fixing the crash.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Persona.chat(messages: List[AgentMessage], sess) and Persona.stream(...)
declare AgentMessage in their type contract and forward straight to
QuestionSolversService.chat_completion()/stream_completion(), which reads
messages[-1].content for QuestionSolver-based plugins.

run_chat()/run_stream()'s stateless path (CHAT_MEMORY=off, the default)
passed the raw OpenAI-style message dicts from the request straight
through without converting them first. Any QuestionSolver-based plugin
(e.g. ovos-solver-plugin-ddg) then crashed with:

  AttributeError: 'dict' object has no attribute 'content'

which the outer HTTP layer's token-counting code turned into a second,
more confusing error further down the stack:

  AttributeError: 'NoneType' object has no attribute 'split'

chat.py already has a _messages_to_agent() converter for the ChatEngine
path (continue_chat), it just wasn't reused here. This adds an
equivalent local helper in persona.py and applies it at both call sites.

Confirmed by constructing AgentMessage/dict objects directly against a
real QuestionSolversService instance and reproducing the exact
AttributeError with unconverted dicts.

Found while packaging this server as a Home Assistant OS Supervisor
add-on: https://github.com/andlo/haos-ovos-addons
…through tests (#68)

* refactor: unify OpenAI-dict to AgentMessage conversion in persona.py

Move _flatten_text/_role/_messages_to_agent from chat.py into persona.py
and drop the duplicate _dicts_to_agent_messages, so the stateless run_chat
and run_stream paths get the same tool_calls/tool_call_id/name-preserving,
function->tool role mapping that chat.py's tool-calling path already had.
chat.py now imports the shared helpers instead of redefining them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: assert AgentMessage contract on the stateless run_chat/run_stream path

The old tests asserted client dicts passed through unconverted; that
contract no longer holds now that run_chat/run_stream always convert to
AgentMessage. Update them to check the converted objects, and add a
regression test covering legacy role mapping, unknown-role fallback, and
content-parts flattening for both run_chat and run_stream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: update stale passthrough assertions to the AgentMessage contract

PR #67 fixed run_chat/run_stream to convert client message dicts to
AgentMessage before calling persona.chat/stream, but these tests still
asserted the old dict-passthrough contract (subscript access, string
role comparison). Assert on the converted AgentMessage objects instead,
preserving each test's original intent (role mapping, system-message
prepending, content-parts flattening).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: assert AgentMessage contract in a2a executor forwarding test

The a2a async tests only run where pytest-asyncio is active, so this
stale dict-passthrough assertion was missed by the local sweep and only
surfaced in CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants