Local development
This guide covers the full local development loop: create a schema, deploy locally, feed and query documents, then iterate with new migrations. To learn how to manage schemas, see Manage schema. For the full set of flags, see the CLI reference.
Prerequisites
- The Search Toolkit Vespa plugin installed (see Installation)
- Docker with the Compose plugin installed and running, with at least 4 GB of memory available to Docker
Step 1: Create your first migration
uv run mistral-vespa generate-migration --app-dir ./vespa_app initial_schemaFill in the generated file:
from mistralai.search.toolkit.embedding import MistralEmbeddingPreset
from mistralai.search.toolkit.plugins.vespa.app.schemas.app import (
FieldDefinition,
IndexingMode,
SearchMode,
)
from mistralai.search.toolkit.plugins.vespa.migration import VespaMigration, create_schema, set_app_name
class InitialSchema(VespaMigration):
def migrate(self) -> None:
set_app_name("mynewproject")
create_schema(
name="articles",
mode=SearchMode.INDEX,
embedding_model=MistralEmbeddingPreset.MISTRAL_EMBED_DIM_1024,
indexing_mode=IndexingMode.DOCUMENT_PER_CHUNK,
default_query_profile_name="hybrid-profile",
# The standard chunk fields (content, embedding, identity, metadata) are
# added automatically. `fields` only declares your extra fields.
fields=[
FieldDefinition.TextField(name="title"),
],
)With IndexingMode.DOCUMENT_PER_CHUNK, create_schema() injects the standard chunk fields (content, embedding, identity, metadata) automatically; use fields to add your own. The legacy create_default_schema() helper is deprecated and is removed before 1.0.0.
Application name restrictions: The name passed to set_app_name() must contain only lowercase letters (a-z). Numbers, underscores, hyphens, and other special characters are not allowed.
Legacy SINGLE_DOCUMENT schemas only: if a schema includes both a multi-dimensional embedding field and a multi-dimensional text field, the plugin generates best_chunks, chunk_scores, and related helpers automatically.
Step 2: Start a local Vespa instance
uv run mistral-vespa local up --query-port 18080 --config-port 19171 --name vespa-dev| Port | Service | Use |
|---|---|---|
| 18080 | Container HTTP | Query and document feed |
| 19171 | Config server | Application deploys |
Step 3: Deploy from migrations
uv run mistral-vespa migrate \
--app-dir ./vespa_app \
--config-server http://localhost:19171 \
--query-port 18080mistral-vespa migrate builds the application package from migrations in memory and deploys it to the config server. Before you feed documents, wait until curl http://localhost:18080/ApplicationStatus returns HTTP 200.
Step 4: Feed and query documents
Feed a document:
curl -X POST \
-H "Content-Type: application/json" \
--data '{
"fields": {
"title": "Document 1"
}
}' \
"http://localhost:18080/document/v1/articles/articles/docid/doc1"Run a search:
curl -H "Content-Type: application/json" \
--data '{"yql": "select * from sources * where true"}' \
"http://localhost:18080/search/"Step 5: Modify the schema
Create a new migration to evolve the schema:
uv run mistral-vespa generate-migration \
--app-dir ./vespa_app \
add_view_countUpdate the generated file:
from mistralai.search.toolkit.plugins.vespa.app.schemas.app import FieldDefinition
from mistralai.search.toolkit.plugins.vespa.migration import VespaMigration, add_field
class AddViewCount(VespaMigration):
def migrate(self) -> None:
add_field("articles", FieldDefinition.CountField(name="view_count"))Preview the change:
uv run mistral-vespa migrate \
--app-dir ./vespa_app \
--config-server http://localhost:19171 \
--query-port 18080 \
--dry-runApply it (remove --dry-run):
uv run mistral-vespa migrate \
--app-dir ./vespa_app \
--config-server http://localhost:19171 \
--query-port 18080Understand schema changes
What happens when you change the schema of a running application:
| Change | Behavior |
|---|---|
| Adding new fields | No problem. The new field has no value until documents write to it. |
| Changing how a field is indexed | Vespa triggers background reindexing. Temporary inconsistency possible. |
| Removing a field | All data and indexes for that field are removed. |
| Changing the type of a field | Data loss. Prefer adding a new field, migrating data, then removing the old one. |
For more details, see Vespa's documentation on modifying schemas.
Custom ranking profiles and extra assets
Add custom rank profiles or model files from a migration:
from pathlib import Path
from mistralai.search.toolkit.plugins.vespa.migration import VespaMigration, add_schema_rank_profiles
class AddCustomRankingProfile(VespaMigration):
def migrate(self) -> None:
add_schema_rank_profiles(
"articles",
[Path(__file__).parent.parent / "vespa-extra" / "custom.profile"],
)add_schema_model_files takes the same arguments. For document summaries and query profiles, see add_schema_custom_document_summary and add_query_profiles in the Migration helpers reference.
Optional: generate a vespa.lock snapshot
If your repository keeps a generated reference package for CI or review, generate it from the migrations:
uv run mistral-vespa generate \
--app-dir ./vespa_app \
--path ./vespa.lockRegenerate it after every schema change. Keep deploying from migrations with mistral-vespa migrate.
IDE support
Use an IDE with the Vespa plugin for syntax highlighting, code completion, navigation between functions and profiles, and error detection. See Vespa IDE support documentation.
See also
- Deploy and operate: production deployment and health checks