Skip to content

[FEAT] Add Mistral embedding adapter (mistral-embed)#2158

Open
ahmedk20 wants to merge 1 commit into
Zipstack:mainfrom
ahmedk20:feat/mistral-embedding-adapter
Open

[FEAT] Add Mistral embedding adapter (mistral-embed)#2158
ahmedk20 wants to merge 1 commit into
Zipstack:mainfrom
ahmedk20:feat/mistral-embedding-adapter

Conversation

@ahmedk20

@ahmedk20 ahmedk20 commented Jul 8, 2026

Copy link
Copy Markdown

Mistral was a first-class LLM adapter but had no embedding adapter, despite shipping a real, popular embedding model (mistral-embed). This adds a native Mistral embedding adapter that routes through LiteLLM's mistral/ provider, so cost calculation works out of the box.

  • MistralEmbeddingParameters in base1.py (idempotent mistral/ prefix, required-model validation)
  • MistralEmbeddingAdapter + UI JSON schema, reusing the existing logo
  • Registered in embedding1/init.py
  • 28 unit tests mirroring the Gemini embedding coverage

Closes #2157

What

  • Adds a native Mistral embedding adapter (mistral-embed) to unstract/sdk1. Mistral was already a first-class LLM adapter but had no embedding counterpart.

  • MistralEmbeddingParameters in base1.py (idempotent mistral/ prefix, required-model validation)

  • MistralEmbeddingAdapter + UI JSON schema (embedding1/static/mistral.json), reusing the existing Mistral logo

  • Registered in embedding1/__init__.py

  • 28 unit tests mirroring the Gemini embedding coverage

Why

  • Users who standardize on Mistral for generation had to switch providers just for embeddings — a second API key, second billing relationship, and mixed providers inside one RAG pipeline.
  • The gap was a plain asymmetry: 14 LLM adapters include mistral, but the 9 embedding adapters did not, despite mistral-embed being a real, popular model.
  • Routing through LiteLLM's native mistral/ provider means cost calculation works out of the box (litellm_provider: "mistral" matches get_provider()).

How

  • New MistralEmbeddingParameters(BaseEmbeddingParameters) validates the required model and prepends the mistral/ prefix idempotently (won't double-prefix an already-prefixed model).
  • MistralEmbeddingAdapter follows the existing embedding-adapter contract (get_id/get_provider/get_adapter_type/etc.), returns AdapterTypes.EMBEDDING, and is auto-discovered by register_adapters().
  • Verified against LiteLLM pricing data: mistral/mistral-embedlitellm_provider: "mistral", mode: "embedding", 8192 max tokens.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

  • No. The change is purely additive: one new adapter file, one new JSON schema, one new parameter class, and one new import line. No existing adapter, schema, or shared code path is modified. Adapters are auto-registered by directory scan, so nothing else needs to change. The new provider name (mistral) already exists as an LLM provider, so cost-calculation behavior for existing adapters is untouched.

Database Migrations

  • None.

Env Config

  • None. Users supply their Mistral API key through the adapter UI like any other embedding adapter.

Relevant Docs

Related Issues or PRs

Dependencies Versions

  • No new dependencies. Uses the already-vendored LiteLLM mistral/ provider.

Notes on Testing

  • Added tests/test_mistral_embedding.py — 28 tests covering registration, ID format, provider name, JSON schema (required fields, defaults, password format), model-prefix idempotency, input-mutation safety, and validation errors (empty/whitespace/None/missing model, missing API key).
  • Full run: 52 passed (28 new + 24 Gemini embedding as a regression check).
  • Lint clean with the repo's pinned ruff 0.3.4: ruff check → all passed; ruff format --check → all files formatted.

Screenshots

N/A — no custom UI; the adapter renders through the standard auto-generated config form.

Checklist

I have read and understood the Contribution Guidelines.

Mistral was a first-class LLM adapter but had no embedding adapter,
despite shipping a real, popular embedding model (mistral-embed).
This adds a native Mistral embedding adapter that routes through
LiteLLM's `mistral/` provider, so cost calculation works out of the box.

- MistralEmbeddingParameters in base1.py (idempotent `mistral/` prefix,
  required-model validation)
- MistralEmbeddingAdapter + UI JSON schema, reusing the existing logo
- Registered in embedding1/__init__.py
- 28 unit tests mirroring the Gemini embedding coverage

Closes Zipstack#2157

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

CLAassistant commented Jul 8, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: db69058a-c40f-401c-82f3-1317339f2ae4

📥 Commits

Reviewing files that changed from the base of the PR and between cb11a6f and 16dc46f.

📒 Files selected for processing (5)
  • unstract/sdk1/src/unstract/sdk1/adapters/base1.py
  • unstract/sdk1/src/unstract/sdk1/adapters/embedding1/__init__.py
  • unstract/sdk1/src/unstract/sdk1/adapters/embedding1/mistral.py
  • unstract/sdk1/src/unstract/sdk1/adapters/embedding1/static/mistral.json
  • unstract/sdk1/tests/test_mistral_embedding.py

Summary by CodeRabbit

  • New Features

    • Added support for a new Mistral embedding adapter, including its configuration, identity, and provider details.
    • Introduced a new setup schema so it can be configured with an API key, model, and timeout settings.
  • Bug Fixes

    • Improved model handling by normalizing Mistral model names and validating required fields more strictly.
    • Added coverage to ensure adapter settings are parsed consistently and invalid input is rejected.

Walkthrough

Adds a new Mistral embedding adapter to the Unstract SDK, including a MistralEmbeddingParameters class with model validation, a MistralEmbeddingAdapter class with registry metadata, package exports, a UI JSON schema, and a corresponding test suite.

Changes

Mistral Embedding Adapter

Layer / File(s) Summary
Parameter validation
unstract/sdk1/src/unstract/sdk1/adapters/base1.py
Adds MistralEmbeddingParameters with api_key and optional embed_batch_size fields, validate() returning normalized model_dump(), and validate_model() enforcing non-empty model names prefixed with mistral/.
Adapter class and registration
unstract/sdk1/src/unstract/sdk1/adapters/embedding1/mistral.py, unstract/sdk1/src/unstract/sdk1/adapters/embedding1/__init__.py
Implements MistralEmbeddingAdapter with get_id, get_metadata, get_name, get_description, get_provider, get_icon, and get_adapter_type (EMBEDDING), and registers/exports it via the package __init__.
JSON configuration schema
unstract/sdk1/src/unstract/sdk1/adapters/embedding1/static/mistral.json
Adds a schema defining required fields (adapter_name, api_key, model) with defaults for model, password-formatted api_key, and a constrained numeric timeout.
Test coverage
unstract/sdk1/tests/test_mistral_embedding.py
Adds tests validating adapter registration, identity/provider values, schema correctness, model/API key validation behavior, and metadata output.

Estimated code review effort: 2 (Simple) | ~15 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: adding a Mistral embedding adapter for mistral-embed.
Description check ✅ Passed The description includes the required sections and covers what, why, how, risks, testing, and related issues.
Linked Issues check ✅ Passed The changes implement the requested Mistral embedding adapter, schema, provider routing, and parameter class from #2157.
Out of Scope Changes check ✅ Passed The patch appears additive and focused on the requested adapter, schema, exports, and tests with no unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a native Mistral embedding adapter (mistral-embed) to unstract/sdk1, closing a gap where Mistral was supported as an LLM provider but lacked an embedding counterpart. The implementation faithfully mirrors the existing GeminiEmbeddingAdapter pattern and routes through LiteLLM's mistral/ provider for out-of-the-box cost calculation.

  • MistralEmbeddingParameters added to base1.py with idempotent mistral/ prefix logic, empty/whitespace/None model validation, and a shallow-copy-safe validate() method consistent with GeminiEmbeddingParameters.
  • MistralEmbeddingAdapter registered in embedding1/__init__.py, with a matching static JSON schema that mirrors gemini.json (same required fields, same timeout default of 240 s, password-format API key).
  • 28 unit tests added covering registration, ID format, schema structure, model-prefix idempotency, mutation safety, and all validation-error paths.

Confidence Score: 5/5

Purely additive change; no existing adapter, schema, or shared code path is modified.

The new adapter faithfully mirrors the Gemini embedding adapter in every structural detail — parameter class, validate/validate_model logic, static JSON schema, registration, and test coverage. The unique adapter ID does not collide with the existing Mistral LLM adapter. The shallow-copy pattern in validate() correctly prevents input mutation, and all edge cases (None, whitespace, missing key, double-prefix) are explicitly tested.

No files require special attention.

Important Files Changed

Filename Overview
unstract/sdk1/src/unstract/sdk1/adapters/base1.py Adds MistralEmbeddingParameters class mirroring GeminiEmbeddingParameters; validate() correctly shallow-copies input before mutation and validate_model() handles all edge cases (None, whitespace, missing key, double-prefix). No issues.
unstract/sdk1/src/unstract/sdk1/adapters/embedding1/mistral.py New adapter class inheriting MistralEmbeddingParameters and BaseAdapter; all required abstract methods are implemented and consistent with the Gemini counterpart. ID UUID is unique from the existing Mistral LLM adapter.
unstract/sdk1/src/unstract/sdk1/adapters/embedding1/static/mistral.json JSON schema mirrors gemini.json exactly (same required fields, timeout default of 240 s, password format for api_key). embed_batch_size intentionally omitted from UI exposure; consistent with Gemini schema.
unstract/sdk1/src/unstract/sdk1/adapters/embedding1/init.py Clean import and all registration of MistralEmbeddingAdapter; adapter is also auto-discovered via register_adapters directory scan.
unstract/sdk1/tests/test_mistral_embedding.py 28 well-structured tests covering registration, schema structure, all validation paths, and mutation safety. Mirrors the Gemini test suite as intended.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class BaseModel {
        <<pydantic>>
    }
    class BaseEmbeddingParameters {
        <<abstract>>
        +str model
        +float|int|None timeout = 600
        +int|None max_retries = None
        +validate(adapter_metadata)* dict
        +validate_model(adapter_metadata)* str
    }
    class MistralEmbeddingParameters {
        +str api_key
        +int|None embed_batch_size = None
        +validate(adapter_metadata) dict
        +validate_model(adapter_metadata) str
    }
    class BaseAdapter {
        <<abstract>>
        +get_id()* str
        +get_name()* str
        +get_description()* str
        +get_provider()* str
        +get_icon()* str
        +get_adapter_type()* AdapterTypes
        +get_json_schema() str
        +get_metadata()* dict
    }
    class MistralEmbeddingAdapter {
        +get_id() str
        +get_name() str
        +get_description() str
        +get_provider() str
        +get_icon() str
        +get_adapter_type() AdapterTypes
        +get_metadata() dict
    }
    class GeminiEmbeddingAdapter {
        +get_id() str
        +get_provider() str
        +get_adapter_type() AdapterTypes
    }
    class GeminiEmbeddingParameters {
        +str api_key
        +int|None embed_batch_size = None
        +validate(adapter_metadata) dict
        +validate_model(adapter_metadata) str
    }

    BaseModel <|-- BaseEmbeddingParameters
    BaseEmbeddingParameters <|-- MistralEmbeddingParameters
    BaseEmbeddingParameters <|-- GeminiEmbeddingParameters
    MistralEmbeddingParameters <|-- MistralEmbeddingAdapter
    BaseAdapter <|-- MistralEmbeddingAdapter
    GeminiEmbeddingParameters <|-- GeminiEmbeddingAdapter
    BaseAdapter <|-- GeminiEmbeddingAdapter
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
classDiagram
    class BaseModel {
        <<pydantic>>
    }
    class BaseEmbeddingParameters {
        <<abstract>>
        +str model
        +float|int|None timeout = 600
        +int|None max_retries = None
        +validate(adapter_metadata)* dict
        +validate_model(adapter_metadata)* str
    }
    class MistralEmbeddingParameters {
        +str api_key
        +int|None embed_batch_size = None
        +validate(adapter_metadata) dict
        +validate_model(adapter_metadata) str
    }
    class BaseAdapter {
        <<abstract>>
        +get_id()* str
        +get_name()* str
        +get_description()* str
        +get_provider()* str
        +get_icon()* str
        +get_adapter_type()* AdapterTypes
        +get_json_schema() str
        +get_metadata()* dict
    }
    class MistralEmbeddingAdapter {
        +get_id() str
        +get_name() str
        +get_description() str
        +get_provider() str
        +get_icon() str
        +get_adapter_type() AdapterTypes
        +get_metadata() dict
    }
    class GeminiEmbeddingAdapter {
        +get_id() str
        +get_provider() str
        +get_adapter_type() AdapterTypes
    }
    class GeminiEmbeddingParameters {
        +str api_key
        +int|None embed_batch_size = None
        +validate(adapter_metadata) dict
        +validate_model(adapter_metadata) str
    }

    BaseModel <|-- BaseEmbeddingParameters
    BaseEmbeddingParameters <|-- MistralEmbeddingParameters
    BaseEmbeddingParameters <|-- GeminiEmbeddingParameters
    MistralEmbeddingParameters <|-- MistralEmbeddingAdapter
    BaseAdapter <|-- MistralEmbeddingAdapter
    GeminiEmbeddingParameters <|-- GeminiEmbeddingAdapter
    BaseAdapter <|-- GeminiEmbeddingAdapter
Loading

Reviews (1): Last reviewed commit: "[FEAT] Add Mistral embedding adapter (mi..." | Re-trigger Greptile

@ahmedk20

Copy link
Copy Markdown
Author

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] Add Mistral embedding adapter (mistral-embed)

2 participants