Skip to main content

adapter-ops

Extend LLM and embedding adapters in unstract/sdk1. Use when adding new adapters (LLM or embedding), removing adapters, adding/removing models to existing adapters, or editing adapter configurations. Supports OpenAI-compatible providers, cloud providers (AWS Bedrock, VertexAI, Azure), and self-hosted models (Ollama).

Source facts

Repository
Zipstack/unstract
Last source activity
August 20, 2026 at 06:00
Detected SKILL.md language
English
Stars
7,278
Forks
719

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
14 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
adapter-ops
description
Extend LLM and embedding adapters in unstract/sdk1. Use when adding new adapters (LLM or embedding), removing adapters, adding/removing models to existing adapters, or editing adapter configurations. Supports OpenAI-compatible providers, cloud providers (AWS Bedrock, VertexAI, Azure), and self-hosted models (Ollama).
# Unstract Adapter Extension Skill This skill provides workflows and automation for extending LLM and embedding adapters in the `unstract/sdk1` module. ## Supported Operations | Operation | Command | Description | |-----------|---------|-------------| | Add LLM Adapter | `scripts/init_llm_adapter.py` | Create new LLM provider adapter | | Add Embedding Adapter | `scripts/init_embedding_adapter.py` | Create new embedding provider adapter | | Remove Adapter | Manual deletion | Remove adapter files and parameter class | | Add/Remove Models | `scripts/manage_models.py` | Modify available models in JSON schema | | Edit Adapter | Manual edit | Modify existing adapter behavior | | Check for Updates | `scripts/check_adapter_updates.py` | Compare adapters against LiteLLM features | ## Quick Reference ### File Locations ``` unstract/sdk1/src/unstract/sdk1/adapters/ ├── base1.py # Parameter classes (add new ones here) ├── llm1/ # LLM adapters │ ├── {provider}.py # Adapter implementation │ └── static/{provider}.json # UI schema └── embedding1/ # Embedding adapters ├── {provider}.py # Adapter implementation └── static/{provider}.json # UI schema ``` ### ID Format Adapter IDs follow the pattern: `{provider}|{uuid4}` - Example: `openai|502ecf49-e47c-445c-9907-6d4b90c5cd17` - Generate UUID: `python -c "import uuid; print(uuid.uuid4())"` ### Model Prefix Convention LiteLLM requires provider prefixes on model names: | Provider | Prefix | Example | |----------|--------|---------| | OpenAI | `openai/` | `openai/gpt-4` | | Azure | `azure/` | `azure/gpt-4-deployment` | | Anthropic | `anthropic/` | `anthropic/claude-3-opus` | | Bedrock (Converse/Invoke) | `bedrock/` | `bedrock/anthropic.claude-v2` | | Bedrock Mantle (OpenAI-compatible) | `bedrock_mantle/` | `bedrock_mantle/openai.gpt-5.6-terra` | | VertexAI | `vertex_ai/` | `vertex_ai/gemini-pro` | | Ollama | `ollama_chat/` | `ollama_chat/llama2` | | Mistral | `mistral/` | `mistral/mistral-large` | | Anyscale | `anyscale/` | `anyscale/meta-llama/Llama-2-70b` | ## Workflows ### Adding a New LLM Adapter 1. **Run initialization script**: ```bash python .claude/skills/adapter-ops/scripts/init_llm_adapter.py \ --provider newprovider \ --name "New Provider" \ --description "New Provider LLM adapter" \ --auto-logo ``` **Logo options**: - `--auto-logo`: Search for potential logo sources (Clearbit, GitHub) and display suggestions. Does NOT auto-download - you must verify and use `--logo-url` to download. - `--logo-url URL`: Download logo from a verified URL (supports SVG and raster images) - `--logo-file PATH`: Copy logo from local file (supports SVG and raster images) **Logo image settings** (optimized for sharp rendering): - SVG conversion: 4800 DPI density, 8-bit depth, 512x512 pixels - Raster images: Resized to 512x512 with LANCZOS resampling - Requires ImageMagick for SVG conversion (`sudo pacman -S imagemagick`) **GitHub logo URL tip**: When downloading logos from GitHub, always use the raw URL: - ❌ `https://github.com/user/repo/blob/main/logo.svg` - ✅ `https://raw.githubusercontent.com/user/repo/main/logo.svg` Logos are saved to: `frontend/public/icons/adapter-icons/{ProviderName}.png` 2. **Add parameter class to `base1.py`** (if provider has unique parameters): ```python class NewProviderLLMParameters(BaseChatCompletionParameters): """See https://docs.litellm.ai/docs/providers/newprovider.""" api_key: str # Add provider-specific fields @staticmethod def validate(adapter_metadata: dict[str, "Any"]) -> dict[str, "Any"]: adapter_metadata["model"] = NewProviderLLMParameters.validate_model(adapter_metadata) return NewProviderLLMParameters(**adapter_metadata).model_dump() @staticmethod def validate_model(adapter_metadata: dict[str, "Any"]) -> str: model = adapter_metadata.get("model", "") if model.startswith("newprovider/"): return model return f"newprovider/{model}" ``` 3. **Update adapter class** to inherit from new parameter class: ```python from unstract.sdk1.adapters.base1 import BaseAdapter, NewProviderLLMParameters class NewProviderLLMAdapter(NewProviderLLMParameters, BaseAdapter): # ... implementation ``` 4. **Customize JSON schema** in `llm1/static/newprovider.json` for UI configuration 5. **Test the adapter**: ```python from unstract.sdk1.adapters.adapterkit import Adapterkit kit = Adapterkit() adapters = kit.get_adapters_list() # Verify new adapter appears ``` ### Adding a New Embedding Adapter 1. **Run initialization script**: ```bash python .claude/skills/adapter-ops/scripts/init_embedding_adapter.py \ --provider newprovider \ --name "New Provider" \ --description "New Provider embedding adapter" \ --auto-logo ``` Same logo options as LLM adapter: `--auto-logo` (search only), `--logo-url`, `--logo-file` 2. **Add parameter class to `base1.py`** (if needed): ```python class NewProviderEmbeddingParameters(BaseEmbeddingParameters): """See https://docs.litellm.ai/docs/providers/newprovider.""" api_key: str embed_batch_size: int | None = 10 @staticmethod def validate(adapter_metadata: dict[str, "Any"]) -> dict[str, "Any"]: adapter_metadata["model"] = NewProviderEmbeddingParameters.validate_model(adapter_metadata) return NewProviderEmbeddingParameters(**adapter_metadata).model_dump() @staticmethod def validate_model(adapter_metadata: dict[str, "Any"]) -> str: return adapter_metadata.get("model", "") ``` 3. **Update adapter class and JSON schema** ### Removing an Adapter 1. **Delete adapter file**: `llm1/{provider}.py` or `embedding1/{provider}.py` 2. **Delete JSON schema**: `llm1/static/{provider}.json` or `embedding1/static/{provider}.json` 3. **Remove parameter class** from `base1.py` (if dedicated class exists) 4. **Verify removal**: Run `Adapterkit().get_adapters_list()` to confirm ### Adding/Removing Models from Existing Adapter 1. **Edit JSON schema** (`static/{provider}.json`): ```json { "properties": { "model": { "type": "string", "title": "Model", "default": "new-default-model", "description": "Available models: model-1, model-2, model-3" } } } ``` 2. **For dropdown selection**, use enum: ```json { "properties": { "model": { "type": "string", "title": "Model", "enum": ["model-1", "model-2", "model-3"], "default": "model-1" } } } ``` 3. **Run management script** for automated updates: ```bash python .claude/skills/adapter-ops/scripts/manage_models.py \ --adapter llm \ --provider openai \ --action add-enum \ --models "gpt-4-turbo,gpt-4o-mini" ``` ### Editing Adapter Behavior Common modifications: 1. **Add reasoning/thinking support**: - Add `enable_thinking` boolean field to JSON schema - Add conditional reasoning config in `validate()` method - See `AnthropicLLMParameters` in `base1.py` for the `thinking` shape - **The shape is provider-specific.** Anthropic models take a `thinking` block with a token budget; most other reasoning-capable families take `reasoning_effort`. Emitting the wrong one is a silent no-op — LiteLLM drops it. `AWSBedrockLLMParameters` has to serve both, so see `_apply_bedrock_reasoning_config` for how it branches on the model family. 2. **Add custom field mapping**: ```python @staticmethod def validate(adapter_metadata: dict[str, "Any"]) -> dict[str, "Any"]: # Map custom field names to expected names if "custom_field" in adapter_metadata: adapter_metadata["expected_field"] = adapter_metadata["custom_field"] # Continue validation... ``` 3. **Add conditional fields in JSON schema**: ```json { "allOf": [ { "if": { "properties": { "feature_enabled": { "const": true } } }, "then": { "properties": { "feature_config": { "type": "string" } }, "required": ["feature_config"] } } ] } ``` ### Checking for Adapter Updates Compare existing adapter schemas against known LiteLLM features to identify potential updates: 1. **Run the update checker**: ```bash # Check all adapters python .claude/skills/adapter-ops/scripts/check_adapter_updates.py # Check specific adapter type python .claude/skills/adapter-ops/scripts/check_adapter_updates.py --adapter llm python .claude/skills/adapter-ops/scripts/check_adapter_updates.py --adapter embedding # Check specific provider python .claude/skills/adapter-ops/scripts/check_adapter_updates.py --provider openai # Output as JSON python .claude/skills/adapter-ops/scripts/check_adapter_updates.py --json ``` 2. **Review the report**: - 🟡 **NEEDS UPDATE**: Adapters with missing parameters or outdated features - ✅ **UP TO DATE**: Adapters matching known LiteLLM features - ❌ **ERRORS**: Adapters that couldn't be analyzed (missing schema, etc.) 3. **Common update types identified**: - **Missing parameters**: New configuration options (e.g., `dimensions` for embeddings) - **Reasoning/Thinking support**: Enable reasoning for models like o1, o3, Claude 3.7+, Magistral - **Outdated defaults**: Default models that have been superseded 4. **After identifying updates**: - Update JSON schema in `static/{provider}.json` - Update parameter class in `base1.py` if validation logic changes - Consult LiteLLM docs for implementation details (URLs provided in report) 5. **Update the feature database** (`check_adapter_updates.py`): - Edit `LITELLM_FEATURES` dict to add new providers or parameters - Keep `known_params`, `reasoning_models`, `thinking_models`, `latest_models` current - Add documentation URLs for reference ## Validation Checklist Before submitting adapter changes: - [ ] Adapter class inherits from correct parameter class AND `BaseAdapter` - [ ] `get_id()` returns unique `{provider}|{uuid}` format - [ ] `get_metadata()` returns dict with `name`, `version`, `adapter`, `description`, `is_active` - [ ] `get_provider()` matches the static JSON filename (`static/{get_provider()}.json`) - [ ] **CRITICAL: the model string produced by `validate_model()` resolves in LiteLLM's cost map** (see below) - [ ] `get_adapter_type()` returns correct `AdapterTypes.LLM` or `AdapterTypes.EMBEDDING` - [ ] JSON schema has `adapter_name` as required field - [ ] `validate()` method adds correct model prefix - [ ] `validate_model()` method handles prefix idempotently (doesn't double-prefix) - [ ] All static methods decorated with `@staticmethod` - [ ] Icon path follows pattern `/icons/adapter-icons/{Name}.png` ### Model Prefix Verification (MANDATORY) Cost is looked up from the **validated model string**, not from `get_provider()`. The string that `validate_model()` produces (e.g. `mistral/mistral-embed`) is passed straight to `litellm.cost_per_token()`. If LiteLLM's cost map has no entry for it, the lookup raises, the exception is swallowed, and usage records **$0**. The lookup sites: | Path | Site | |------|------| | LLM | `unstract/sdk1/src/unstract/sdk1/audit.py` — `cost_per_token(model=model_name)` | | Embedding | `unstract/sdk1/src/unstract/sdk1/usage_handler.py` — `litellm.cost_per_token(...)` | `model_name` is `self._cost_model or self.kwargs["model"]` — the prefixed string. Both call sites catch every exception and fall back to `0.0`, so a miss is **silent**. It will not fail a
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub