Skip to content

feat(inference): add MiniMax native embedding adapter - #1653

Open
AlexStocks wants to merge 4 commits into
oceanbase:masterfrom
AlexStocks:feat/minimax-embedding-adapter
Open

AlexStocks wants to merge 4 commits into
oceanbase:masterfrom
AlexStocks:feat/minimax-embedding-adapter

Conversation

@AlexStocks

Copy link
Copy Markdown
Contributor

Which issue or RFC does this PR close?

Closes #1642.

Rationale for this change

MiniMax exposes /v1/embeddings under an OpenAI-looking path but speaks a different dialect, so it cannot be used as an "OpenAI-compatible" embedding endpoint. PowerContext's only embedding path goes through Pydantic AI's OpenAI client, which sends {"model","input","dimensions"} and parses OpenAI's {data:[{embedding}]}. MiniMax requires {"model","texts","type"} and answers {"vectors","total_tokens","base_resp"} — returning HTTP 200 with a non-zero base_resp.status_code on business errors. The OpenAI client therefore fails while parsing, and the operator only sees a generic "provider rejected" message that does not point at the real cause.

The result was that users who already run generation on openai-chat:MiniMax-M3 could not complete their inference configuration. This PR adds the missing adapter, which is the solution proposed in the issue.

What changes are included in this PR?

New adapter — src/powercontext/builtin/inference/minimax.py

MiniMaxEmbeddingModel is a self-contained EmbeddingModel implementation that:

  • issues the native request body (texts + type: "db", with the provider prefix stripped from model);
  • parses vectors and validates the returned vector count against the number of inputs;
  • maps HTTP failures to InferenceUnavailableError, and a non-zero base_resp.status_code (including the 2013 envelope from the issue) to InferenceUnavailableError / InvalidInferenceOutputError — so a null or partial vector is never handed to the index;
  • owns an httpx.AsyncClient registered on the runtime exit stack, so it is closed with the rest of the resources;
  • exposes is_minimax_embedding() for host/prefix detection.

Routing — src/powercontext/builtin/runtime/composition.py

_embedding_models() detects a MiniMax endpoint — api.minimaxi.com / api.minimax.io host, or an explicit minimax model prefix — and routes to the new adapter via a small _minimax_embedding_models() helper that mirrors the existing provider path (operational + readiness instances, EmbeddingProfile, batch size, timeout). The existing OpenAI/Anthropic path is untouched, and no new configuration knob is introduced. The existing base-URL guard in the helper was widened to also require the embedding model, which narrows the type for the profile construction.

Tests

  • tests/builtin/inference/test_minimax_embedding.py (new) — request body shape, response parsing, the base_resp business-error envelope, HTTP errors, and vector-count validation.
  • tests/builtin/runtime/test_composition_embedding.py — routing test asserting that a MiniMax host selects MiniMaxEmbeddingModel and that the request actually reaching the transport is the native shape.

No change to the OpenAI embedding path, to any persisted format, or to the EmbeddingProfile contract.

Are there any user-facing changes?

Yes, one — MiniMax can now be configured as an embedding backend using the existing generic variables:

POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_MODEL=openai:embo-01
POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_BASE_URL=https://api.minimaxi.com/v1
POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_HEADERS='{"Authorization":"Bearer <key>"}'
POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_PROFILE_ID=minimax-embo-01-v1
POWERCONTEXT_SERVER_INFERENCE_EMBEDDING_DIMENSION=1536

Notes for reviewers:

  • The provider prefix stays openai: — selection is by base-URL host / model prefix, so no new provider name is needed and existing configs keep working.
  • MODEL + PROFILE_ID + DIMENSION remain a three-way contract, exactly as for every other embedding provider.
  • No breaking change to public APIs or persisted formats.
  • This does not touch docs/. A note listing MiniMax among supported embedding backends may be worth a follow-up.

How was this change tested?

# Tests (19)
.venv/Scripts/python.exe -m pytest \
  tests/builtin/inference/test_minimax_embedding.py \
  tests/builtin/runtime/test_composition_embedding.py -q
# 19 passed

# Lint / format / types
.venv/Scripts/ruff.exe check        <the 4 changed files>   # All checks passed!
.venv/Scripts/ruff.exe format --check <the 4 changed files> # 4 files already formatted
.venv/Scripts/ty.exe check --python-version 3.11 <the 4 changed files>  # All checks passed!

Beyond the unit tests, the change was validated end to end against the live MiniMax endpoint, on a personal server built from this branch:

  1. started the server with the configuration shown above — /health/ready reports inference.embedding: ready;
  2. wrote a Memory entry, then issued a POST /v1/memory/search with mode: "vector" using a query that shares no meaningful literal overlap with the stored text;
  3. the entry came back with "matched_by": ["vector"].

Because retrieval only matched through the vector channel, this confirms that both the write-side and the query-side embeddings were really produced by MiniMax (1536 dimensions) rather than by any keyword path. The failing behaviour described in #1642 no longer reproduces.

AI usage statement

AI assistance: code. The adapter, the runtime routing, and the tests were drafted with an AI coding agent (WorkBuddy). The full diff was reviewed, and every command listed above was executed locally against the commit in this PR before submission.

AlexStocks and others added 2 commits September 18, 2026 18:55
MiniMax's /v1/embeddings endpoint is not OpenAI-compatible: it takes {"model","texts","type":"db"} and returns {"vectors","total_tokens","base_resp":{"status_code"}}; on business errors it still answers HTTP 200 with a non-zero base_resp.status_code. PowerContext's default OpenAI embedder cannot parse this shape, so embedding over MiniMax failed.

Add MiniMaxEmbeddingModel, a self-contained adapter that issues the native request, validates the returned vector count, and raises InferenceUnavailableError on HTTP or business-level errors. Route MiniMax endpoints to it from the runtime composition layer via host/model detection (is_minimax_embedding) without altering the OpenAI path.

See oceanbase#1642.

AI assistance: code

Human verification: read the full diff and ran pytest / ruff / ty
Lore: MiniMax requires document embeddings for stored content and query embeddings for retrieval queries.

Constraint: Preserve existing EmbeddingModel fallback and recall-gate query-vector reuse; only providers that expose embed_query take the provider-specific path.

Tested: uv run pytest tests/builtin/inference/test_minimax_embedding.py tests/builtin/inference/test_pydantic_ai.py tests/builtin/artifacts/memory/test_service.py tests/builtin/runtime/test_composition_embedding.py tests/builtin/runtime/test_topic_memory_processing.py -q; uv run ty check changed files; uv run ruff check changed files.

Not-tested: full uv run ty check and uv run ruff check . were polluted by untracked local .worktrees/.workbuddy directories in this workspace.

Co-authored-by: OmX <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

MiniMax detection and error handling need tightening to avoid misrouting and loss or misclassification of actionable provider and response errors.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds native MiniMax embedding support, provider routing, query embedding dispatch, and regression coverage.

Changes:

  • Implements and validates MiniMax embedding requests and responses.
  • Routes MiniMax hosts and models through the native adapter.
  • Adds query-aware embedding support across memory workflows.
  • Adds adapter, routing, and retrieval tests.
File summaries
File Summary
tests/builtin/runtime/test_topic_memory_processing.py Topic-memory query embedding tests
tests/builtin/runtime/test_composition_embedding.py MiniMax routing tests
tests/builtin/inference/test_pydantic_ai.py Query embedding forwarding tests
tests/builtin/inference/test_minimax_embedding.py MiniMax adapter tests
tests/builtin/artifacts/memory/test_service.py Memory query-path tests
src/powercontext/builtin/runtime/topic_memory_processing.py Topic-memory query embeddings
src/powercontext/builtin/runtime/composition.py MiniMax detection and construction
src/powercontext/builtin/runtime/application.py Query embedding integration
src/powercontext/builtin/inference/usage.py Query usage reporting
src/powercontext/builtin/inference/pydantic_ai.py Query input propagation
src/powercontext/builtin/inference/protocols.py Query embedding protocol
src/powercontext/builtin/inference/minimax.py Native MiniMax adapter and detection
src/powercontext/builtin/inference/__init__.py Query embedding exports
src/powercontext/builtin/artifacts/memory/service.py Memory query embeddings
Review details

Suppressed comments (2)

src/powercontext/builtin/inference/minimax.py:115

  • The catch-all converts every unexpected exception, including adapter programming errors and unexpected response-shape bugs, into a transient InferenceUnavailableError. This hides actionable defects and can cause callers to retry failures that are not transient; restrict this branch to expected JSON/transport failures and let unexpected exceptions propagate (as the Pydantic AI adapter does via _map_error).
        except Exception as error:
            raise InferenceUnavailableError("embed") from error

src/powercontext/builtin/inference/minimax.py:139

  • A malformed JSON response raises ValueError here and is then caught by _embed's broad exception handler as InferenceUnavailableError. That misclassifies a provider-output contract violation (and can trigger the caller's transient fallback) instead of the InvalidInferenceOutputError used for other malformed MiniMax responses; catch JSON decoding errors and map them explicitly.
        data = response.json()
  • Files reviewed: 14/14 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/powercontext/builtin/inference/minimax.py Outdated
Comment thread src/powercontext/builtin/inference/minimax.py Outdated
AlexStocks and others added 2 commits September 18, 2026 19:20
Lore: MiniMax malformed provider output should remain distinguishable from transient availability failures.

Constraint: Keep transport and provider business errors mapped to unavailable while letting adapter programming errors propagate.

Tested: uv run pytest tests/builtin/inference/test_minimax_embedding.py tests/builtin/inference/test_pydantic_ai.py tests/builtin/artifacts/memory/test_service.py tests/builtin/runtime/test_composition_embedding.py tests/builtin/runtime/test_topic_memory_processing.py -q; uv run ruff check src/powercontext/builtin/inference/minimax.py tests/builtin/inference/test_minimax_embedding.py; uv run ty check src/powercontext/builtin/inference/minimax.py tests/builtin/inference/test_minimax_embedding.py; git diff --check.

Co-authored-by: OmX <[email protected]>
Lore: MiniMax embeds return provider-specific business errors inside base_resp even when HTTP status is 200.

Constraint: Keep the existing unavailable error category for retry/fallback behavior while preserving provider code/message for operators.

Tested: uv run pytest tests/builtin/inference/test_minimax_embedding.py tests/builtin/runtime/test_composition_embedding.py -q; uv run pytest tests/builtin/inference/test_minimax_embedding.py tests/builtin/inference/test_pydantic_ai.py tests/builtin/artifacts/memory/test_service.py tests/builtin/runtime/test_composition_embedding.py tests/builtin/runtime/test_topic_memory_processing.py tests/builtin/runtime/test_topic_memory_application.py -q; uv run ruff check src/powercontext/builtin/inference/errors.py src/powercontext/builtin/inference/minimax.py tests/builtin/inference/test_minimax_embedding.py tests/builtin/runtime/test_composition_embedding.py; uv run ty check src/powercontext/builtin/inference/errors.py src/powercontext/builtin/inference/minimax.py tests/builtin/inference/test_minimax_embedding.py tests/builtin/runtime/test_composition_embedding.py; git diff --check.

Co-authored-by: OmX <[email protected]>
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.

feat: support MiniMax embedding backend (non-OpenAI-compatible /v1/embeddings)

2 participants