Spaces:
Paused
Paused
| # -*- coding: utf-8 -*- | |
| """The embedding model card class.""" | |
| from __future__ import annotations | |
| import copy | |
| from typing import Literal, Self, Type | |
| import yaml | |
| from pydantic import BaseModel, Field | |
| class EmbeddingModelCard(BaseModel): | |
| """A card describing an embedding model's capabilities. | |
| Mirrors :class:`~agentscope.model.ModelCard` but tailored for | |
| embedding models. Uses ``input_types`` / ``output_types`` to | |
| describe model capabilities, and ``parameter_schema`` (built from | |
| the embedding class's ``Parameters`` + YAML ``parameter_overrides``) | |
| to tell the frontend which knobs the user can adjust. | |
| The output type ``application/x-embedding`` indicates that the | |
| model produces dense vector embeddings. | |
| """ | |
| type: Literal["embedding_model"] = "embedding_model" | |
| """The card type, always ``"embedding_model"``.""" | |
| name: str = Field(description="The model name used in API calls.") | |
| """The model name (e.g. ``"text-embedding-3-small"``).""" | |
| label: str = Field(description="Human-readable label for the frontend.") | |
| """Display label (e.g. ``"Text Embedding 3 Small"``).""" | |
| status: Literal["active", "deprecated", "sunset"] = Field( | |
| default="active", | |
| description="The model lifecycle status.", | |
| ) | |
| """The model status.""" | |
| input_types: list[str] = Field( | |
| default=["text/plain"], | |
| description="Supported input media types.", | |
| ) | |
| """Supported input types (e.g. ``["text/plain"]``, | |
| ``["text/plain", "image/jpeg", "image/png"]``).""" | |
| output_types: list[str] = Field( | |
| default=["application/x-embedding"], | |
| description="Supported output media types.", | |
| ) | |
| """Output types. ``application/x-embedding`` for vector output.""" | |
| dimensions: int = Field( | |
| ..., | |
| description="Default output vector dimensions for this model.", | |
| gt=0, | |
| ) | |
| """The default output dimensions for this model. | |
| First-class top-level field — kept outside of | |
| :attr:`parameter_schema` so that callers can rely on a strongly | |
| typed ``int`` rather than the soft ``parameter_schema['properties'] | |
| ['dimensions']['default']`` lookup. | |
| """ | |
| supported_dimensions: list[int] | None = Field( | |
| default=None, | |
| description=( | |
| "If set, the only dimensions this model can produce. " | |
| "``None`` means dimensions are fixed at " | |
| ":attr:`dimensions` and cannot be overridden." | |
| ), | |
| ) | |
| """Optional set of allowed output dimensions. | |
| Set for Matryoshka-style models (e.g. OpenAI's | |
| ``text-embedding-3-*``) that can be truncated to a smaller size. | |
| ``None`` indicates a fixed-dimension model. | |
| """ | |
| context_size: int | None = Field( | |
| default=None, | |
| description="Maximum input length (in tokens) per request.", | |
| gt=0, | |
| ) | |
| """Maximum input context size, if known.""" | |
| parameter_schema: dict = Field( | |
| default_factory=dict, | |
| description=( | |
| "JSON Schema for user-configurable parameters " | |
| "(built from the Parameters class + YAML overrides)." | |
| ), | |
| ) | |
| """The parameter schema sent to the frontend for form rendering. | |
| Empty ``properties`` means nothing to configure (e.g. fixed | |
| dimensions).""" | |
| parameter_overrides: dict[str, dict] = Field( | |
| default_factory=dict, | |
| description="Raw parameter overrides from the YAML file.", | |
| ) | |
| """The raw parameter overrides, preserved for reference.""" | |
| def from_yaml( | |
| cls, | |
| yaml_path: str, | |
| parameter_class: Type[BaseModel], | |
| ) -> Self: | |
| """Load an embedding model card from a YAML file. | |
| Merges the base ``parameter_class`` JSON Schema with | |
| ``parameter_overrides`` from the YAML — identical to the | |
| approach used by :meth:`~agentscope.model.ModelCard.from_yaml`. | |
| Args: | |
| yaml_path (`str`): | |
| Path to the YAML file. | |
| parameter_class (`Type[BaseModel]`): | |
| The ``Parameters`` class from the embedding model subclass. | |
| Returns: | |
| `EmbeddingModelCard`: The loaded model card. | |
| """ | |
| with open(yaml_path, "r", encoding="utf-8") as f: | |
| config = yaml.safe_load(f) | |
| if "dimensions" not in config: | |
| raise ValueError( | |
| f"Embedding model card {yaml_path!r} is missing the " | |
| f"required top-level 'dimensions' field.", | |
| ) | |
| # Build parameter schema from the Parameters class | |
| base_schema = parameter_class.model_json_schema() | |
| properties = copy.deepcopy(base_schema.get("properties", {})) | |
| # Apply parameter_overrides (same logic as ModelCard.from_yaml) | |
| overrides = config.get("parameter_overrides", {}) | |
| for param_name, override in overrides.items(): | |
| if override is None: | |
| # null means remove | |
| properties.pop(param_name, None) | |
| continue | |
| if isinstance(override, dict): | |
| if override.get("hidden"): | |
| properties.pop(param_name, None) | |
| continue | |
| # Simple dict merge | |
| if param_name in properties: | |
| properties[param_name] = { | |
| **properties[param_name], | |
| **override, | |
| } | |
| final_schema = { | |
| "type": "object", | |
| "properties": properties, | |
| "required": base_schema.get("required", []), | |
| } | |
| return cls( | |
| name=config["name"], | |
| label=config["label"], | |
| status=config.get("status", "active"), | |
| input_types=config.get("input_types", ["text/plain"]), | |
| output_types=config.get( | |
| "output_types", | |
| ["application/x-embedding"], | |
| ), | |
| dimensions=config["dimensions"], | |
| supported_dimensions=config.get("supported_dimensions"), | |
| context_size=config.get("context_size"), | |
| parameter_schema=final_schema, | |
| parameter_overrides=overrides, | |
| ) | |