Release 0.17.1a1 - #89
Open
github-actions[bot] wants to merge 91 commits into
Open
Conversation
chore: Configure Renovate
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>
…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>
* fix: loader never passes toolbox_id; plugins own their id
* fix: loader always calls cls(config=cfg, bus=bus), no fallback
The OPM ToolBox contract puts toolbox_id back where it belongs: a
class attribute the plugin declares itself, validated at construction.
The loader's job is simply to call the constructor the same way every
other OPM plugin factory does — cls(config=cfg, bus=bus) — with no
try/except duck-typing on the signature.
_load_toolboxes() now sources config from settings.persona_config (the
same top-level config blob solver plugins already receive, keyed by
plugin name per the existing self.llm_solver: {...} convention) since
there is no dedicated per-toolbox config section in Settings yet. bus
is always None: this server has no message bus of its own.
Test stub ToolBoxes now declare a class-level toolbox_id and take
config/bus like the real contract; dropped the test asserting the old
no-argument cls() + toolbox_id-kwarg fallback behavior.
Authored with Claude Code.
* fix: test stubs pass toolbox_id via super().__init__, not class attribute
Maintainer rejected the class-attribute toolbox_id design. The loader in
tools.py was already correct (cls(config=cfg, bus=bus), no signature
guessing) and is unchanged. Test stub ToolBoxes (FakeToolBox, BoomBox x3)
now supply toolbox_id through super().__init__(toolbox_id=..., config=,
bus=) like real plugins will. Reworks the "missing class toolbox_id is
skipped" test into "a plugin that fails to construct (e.g. forgets to
forward toolbox_id to the base) is still skipped by the loader" — there
is no more class-attribute requirement to validate.
* fix: require ovos-plugin-manager>=2.11.1a1 for the config kwarg
mcp 2.x removed mcp.server.fastmcp.FastMCP; fastmcp (Apache-2.0) is the maintained continuation. Also fixes remote reachability: mcp 1.x rejected non-localhost Host headers with 421, so the mounted endpoint could not be exposed behind a proxy.
One process loads a directory of personas; clients pick one with the OpenAI `model` field, and the model-listing endpoints enumerate them. Includes a test-isolation fix: the persona registry is process-global, so a module building a real app left it populated for later modules whose standalone routers rely on the empty-registry fallback.
Examples used chat_module, which the loader does not read at all (ovos_persona resolves handlers first, then solvers as the legacy fallback), so copy-pasted personas silently loaded no plugins. Updates the examples to handlers and links the technical manual for the full schema.
Client-supplied OpenAI tools are relayed for the caller to execute; the persona's own ToolBox plugins are executed server-side in a bounded agentic loop. Includes a fix for client tools shadowed by a persona tool of the same name, which were offered to the model twice and executed server-side instead of relayed.
_load_toolboxes passed the entire persona config blob to every ToolBox
plugin. Both shipped implementations read their settings straight off
`config` -- MCPToolBox wants transport/command/url, UTCPToolBox likewise --
so the keys were missing, the constructor raised KeyError('command'),
discover_tools() swallowed it into a warning, and the persona served zero
tools with no visible error.
Plugins now get the section keyed by their plugin name, the same shape solver
plugins already receive. The full blob is still passed when the persona
defines no section, so a toolbox that self-locates keeps working.
Verified against a real MCP server: the registry goes from [] to ['echo'],
and the server-side tool loop then executes it end to end.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ace/append) A client-supplied system message is handled per the persona's system_prompt_strategy: ignore (default), replace, or append. The strategy is applied before the chat engine sees the messages, so it overrides engine-level allow_system_prompts — documented, since a persona that opted into merging client prompts needs 'append' to keep that behaviour.
MCP now mounts only when the server is started with --mcp, matching the other three OVOS servers, so installing the mcp extra no longer silently exposes a tool endpoint. The ImportError guard now logs a warning naming the install command instead of passing silently. BREAKING: deployments relying on auto-mount lose /mcp until they pass --mcp.
…eady receive (#86) The legacy completions endpoint accepts the OpenAI `user` field and the A2A executor receives the protocol's `context_id`, but both discarded it before calling run_chat/run_stream, so in CHAT_MEMORY=transparent mode every caller on those two surfaces landed in the process-global "default" memory bucket and read back each other's history. `context_id` is the A2A conversation identifier (a collection of tasks and messages), not a per-task id, so it is stable across the turns of one conversation. Anonymous requests are unchanged: they still fall back to the documented single default session, which transparent mode documents as single-user only.
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.
Human review requested!