vet

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 88099d461b0fb2d94783e5103a50adc95fc5ab2d
parent 6df7fd9acbfba3fdbe70bd2a76cf1c59b3ddaf94
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date:   Fri, 20 Feb 2026 08:26:02 +0000

Update models.json specification to add temperature boolean (#112)

* Update models.json specification to add an additional temperature boolean for models that do and don't support the param

* Updated support for temp

* Updated readme to make temperature explicit

* Make supports_temperature a required field in models.json

* Formatting

* Removed useless tests

---------

Co-authored-by: Andrew Laack <andrew@laack.co>
Co-authored-by: OpenCode <opencode@users.noreply.github.com>
Diffstat:
M.vet/models.json | 38++++++++++++++++++++++++++++++++------
MREADME.md | 6++++--
Mdev/build.sh | 2+-
Mdev/run.sh | 2+-
Mvet/cli/config/loader.py | 1+
Mvet/cli/config/loader_test.py | 80++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mvet/cli/config/schema.py | 1+
Mvet/cli/models_test.py | 40++++++++++++++++++++++++++++++++++------
Mvet/imbue_core/agents/configs.py | 1+
Mvet/imbue_core/agents/llm_apis/build_apis.py | 1+
Mvet/imbue_core/agents/llm_apis/openai_compatible_api.py | 6++++--
11 files changed, 149 insertions(+), 29 deletions(-)

diff --git a/.vet/models.json b/.vet/models.json @@ -1,5 +1,25 @@ { "providers": { + "zen": { + "name": "Zen", + "api_type": "openai_compatible", + "base_url": "https://opencode.ai/zen/v1", + "api_key_env": "ZEN_API_KEY", + "models": { + "big-pickle": { + "model_id": "big-pickle", + "context_window": 200000, + "max_output_tokens": 128000, + "supports_temperature": true + }, + "gpt-5.2-codex": { + "model_id": "gpt-5.2-codex", + "context_window": 272000, + "max_output_tokens": 128000, + "supports_temperature": false + } + } + }, "anthropic": { "name": "Anthropic", "api_type": "openai_compatible", @@ -9,17 +29,20 @@ "sonnet": { "model_id": "claude-sonnet-4-5-20250929", "context_window": 200000, - "max_output_tokens": 16384 + "max_output_tokens": 16384, + "supports_temperature": true }, "haiku": { "model_id": "claude-haiku-4-5-20251001", "context_window": 200000, - "max_output_tokens": 16384 + "max_output_tokens": 16384, + "supports_temperature": true }, "opus": { "model_id": "claude-opus-4-6", "context_window": 200000, - "max_output_tokens": 16384 + "max_output_tokens": 16384, + "supports_temperature": true } } }, @@ -32,7 +55,8 @@ "gpt-5.2": { "model_id": "gpt-5.2-2025-12-11", "context_window": 128000, - "max_output_tokens": 16384 + "max_output_tokens": 16384, + "supports_temperature": true } } }, @@ -45,12 +69,14 @@ "gpt-oss-120b": { "model_id": "openai/gpt-oss-120b", "context_window": 131072, - "max_output_tokens": 65536 + "max_output_tokens": 65536, + "supports_temperature": true }, "kimi-k2": { "model_id": "moonshotai/kimi-k2-instruct-0905", "context_window": 262144, - "max_output_tokens": 16384 + "max_output_tokens": 16384, + "supports_temperature": true } } } diff --git a/README.md b/README.md @@ -167,12 +167,14 @@ Vet supports custom model definitions using OpenAI-compatible endpoints via JSON "gpt-5.2": { "model_id": "openai/gpt-5.2", "context_window": 400000, - "max_output_tokens": 128000 + "max_output_tokens": 128000, + "supports_temperature": true }, "kimi-k2": { "model_id": "moonshotai/kimi-k2", "context_window": 131072, - "max_output_tokens": 32768 + "max_output_tokens": 32768, + "supports_temperature": true } } } diff --git a/dev/build.sh b/dev/build.sh @@ -8,7 +8,7 @@ if [ "$1" = "claude" ]; then IMAGE_NAME="vet-claude" fi -sudo docker build \ +docker build \ --build-arg INSTALL_CLAUDE=$INSTALL_CLAUDE \ -t $IMAGE_NAME \ dev/. diff --git a/dev/run.sh b/dev/run.sh @@ -8,7 +8,7 @@ fi [ -f .env ] || { echo '.env file not found, please create one before proceeding'; exit 1; } -sudo docker run -it \ +docker run -it \ --mount type=bind,source="$(pwd)",target=/app \ --env-file .env \ "$IMAGE_NAME" bash diff --git a/vet/cli/config/loader.py b/vet/cli/config/loader.py @@ -161,6 +161,7 @@ def build_language_model_config(model_id: str, user_config: ModelsConfig): custom_api_key_env=provider.api_key_env or "", custom_context_window=model_config.context_window, custom_max_output_tokens=model_config.max_output_tokens, + custom_supports_temperature=model_config.supports_temperature, ) diff --git a/vet/cli/config/loader_test.py b/vet/cli/config/loader_test.py @@ -90,6 +90,7 @@ def test_load_single_config_file_loads_valid_config(tmp_path: Path) -> None: "model_id": "test-model-v1", "context_window": 128000, "max_output_tokens": 16384, + "supports_temperature": True, } }, } @@ -174,6 +175,7 @@ def test_load_models_config_loads_project_config(tmp_path: Path) -> None: "project-model": { "context_window": 128000, "max_output_tokens": 16384, + "supports_temperature": True, } }, } @@ -203,6 +205,7 @@ def test_load_models_config_project_overrides_global(tmp_path: Path) -> None: "global-model": { "context_window": 128000, "max_output_tokens": 16384, + "supports_temperature": True, } }, } @@ -227,6 +230,7 @@ def test_load_models_config_project_overrides_global(tmp_path: Path) -> None: "project-model": { "context_window": 128000, "max_output_tokens": 16384, + "supports_temperature": True, } }, } @@ -249,15 +253,27 @@ def test_get_user_defined_model_ids_extracts_all_ids() -> None: base_url="http://localhost:8080/v1", api_key_env="KEY1", models={ - "model-a": ModelConfig(context_window=128000, max_output_tokens=16384), - "model-b": ModelConfig(context_window=128000, max_output_tokens=16384), + "model-a": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ), + "model-b": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ), }, ), "provider2": ProviderConfig( base_url="http://localhost:8081/v1", api_key_env="KEY2", models={ - "model-c": ModelConfig(context_window=128000, max_output_tokens=16384), + "model-c": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ), }, ), } @@ -274,12 +290,24 @@ def test_get_provider_for_model_finds_provider() -> None: "provider1": ProviderConfig( base_url="http://localhost:8080/v1", api_key_env="KEY1", - models={"model-a": ModelConfig(context_window=128000, max_output_tokens=16384)}, + models={ + "model-a": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ) + }, ), "provider2": ProviderConfig( base_url="http://localhost:8081/v1", api_key_env="KEY2", - models={"model-b": ModelConfig(context_window=128000, max_output_tokens=16384)}, + models={ + "model-b": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ) + }, ), } ) @@ -296,7 +324,13 @@ def test_get_provider_for_model_returns_none_for_unknown() -> None: "provider1": ProviderConfig( base_url="http://localhost:8080/v1", api_key_env="KEY1", - models={"model-a": ModelConfig(context_window=128000, max_output_tokens=16384)}, + models={ + "model-a": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ) + }, ), } ) @@ -313,7 +347,13 @@ def test_validate_api_key_passes_when_key_is_set() -> None: name="Test Provider", base_url="http://localhost:8080/v1", api_key_env="TEST_API_KEY", - models={"model-a": ModelConfig(context_window=128000, max_output_tokens=16384)}, + models={ + "model-a": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ) + }, ), } ) @@ -329,7 +369,13 @@ def test_validate_api_key_raises_when_key_not_set() -> None: name="Test Provider", base_url="http://localhost:8080/v1", api_key_env="MISSING_KEY", - models={"model-a": ModelConfig(context_window=128000, max_output_tokens=16384)}, + models={ + "model-a": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ) + }, ), } ) @@ -357,15 +403,27 @@ def test_get_models_by_provider_groups_models() -> None: base_url="http://localhost:11434/v1", api_key_env="OLLAMA_KEY", models={ - "llama3.2:latest": ModelConfig(context_window=128000, max_output_tokens=16384), - "qwen:7b": ModelConfig(context_window=32768, max_output_tokens=8192), + "llama3.2:latest": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ), + "qwen:7b": ModelConfig( + context_window=32768, + max_output_tokens=8192, + supports_temperature=True, + ), }, ), "openrouter": ProviderConfig( base_url="https://openrouter.ai/api/v1", api_key_env="OPENROUTER_KEY", models={ - "anthropic/claude-3": ModelConfig(context_window=200000, max_output_tokens=16384), + "anthropic/claude-3": ModelConfig( + context_window=200000, + max_output_tokens=16384, + supports_temperature=True, + ), }, ), } diff --git a/vet/cli/config/schema.py b/vet/cli/config/schema.py @@ -13,6 +13,7 @@ class ModelConfig(BaseModel): model_id: str | None = None context_window: int max_output_tokens: int + supports_temperature: bool class ProviderConfig(BaseModel): diff --git a/vet/cli/models_test.py b/vet/cli/models_test.py @@ -20,8 +20,16 @@ SAMPLE_USER_CONFIG = ModelsConfig( base_url="http://localhost:8080/v1", api_key_env="CUSTOM_KEY", models={ - "my-custom-model": ModelConfig(context_window=128000, max_output_tokens=16384), - "another-model": ModelConfig(context_window=128000, max_output_tokens=16384), + "my-custom-model": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ), + "another-model": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ), }, ) } @@ -94,7 +102,13 @@ def test_validate_model_id_validates_user_defined_model() -> None: "custom": ProviderConfig( base_url="http://localhost:8080/v1", api_key_env="CUSTOM_KEY", - models={"my-custom-model": ModelConfig(context_window=128000, max_output_tokens=16384)}, + models={ + "my-custom-model": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ) + }, ) } ) @@ -135,8 +149,16 @@ def test_get_models_by_provider_includes_user_defined_providers() -> None: base_url="http://localhost:11434/v1", api_key_env="OLLAMA_KEY", models={ - "llama3.2:latest": ModelConfig(context_window=128000, max_output_tokens=16384), - "qwen:7b": ModelConfig(context_window=32768, max_output_tokens=8192), + "llama3.2:latest": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ), + "qwen:7b": ModelConfig( + context_window=32768, + max_output_tokens=8192, + supports_temperature=True, + ), }, ) } @@ -157,7 +179,13 @@ def test_get_models_by_provider_user_provider_overrides_builtin_with_same_name() name="anthropic", base_url="http://localhost:8080/v1", api_key_env="CUSTOM_KEY", - models={"custom-model": ModelConfig(context_window=128000, max_output_tokens=16384)}, + models={ + "custom-model": ModelConfig( + context_window=128000, + max_output_tokens=16384, + supports_temperature=True, + ) + }, ) } ) diff --git a/vet/imbue_core/agents/configs.py b/vet/imbue_core/agents/configs.py @@ -67,6 +67,7 @@ class OpenAICompatibleModelConfig(LanguageModelGenerationConfig): custom_api_key_env: str custom_context_window: int custom_max_output_tokens: int + custom_supports_temperature: bool = True def count_tokens(self, text: str) -> int: """Count tokens using approximation since we don't have access to the model's tokenizer.""" diff --git a/vet/imbue_core/agents/llm_apis/build_apis.py b/vet/imbue_core/agents/llm_apis/build_apis.py @@ -31,6 +31,7 @@ def build_language_model_from_config( api_key_env=config.custom_api_key_env, context_window=config.custom_context_window, max_output_tokens=config.custom_max_output_tokens, + supports_temperature=config.custom_supports_temperature, cache_path=config.cache_path, is_caching_inputs=config.is_caching_inputs, is_running_offline=config.is_running_offline, diff --git a/vet/imbue_core/agents/llm_apis/openai_compatible_api.py b/vet/imbue_core/agents/llm_apis/openai_compatible_api.py @@ -8,6 +8,7 @@ from loguru import logger from openai import AsyncOpenAI from openai import AsyncStream from openai import InternalServerError +from openai import NOT_GIVEN from openai import NotGiven from openai._exceptions import APIConnectionError from openai._exceptions import BadRequestError @@ -58,6 +59,7 @@ class OpenAICompatibleAPI(LanguageModelAPI): max_output_tokens: int | None = None is_conversational: bool = True presence_penalty: float = 0.0 + supports_temperature: bool = True # this shouldn't really ever even be used, but just in case stop_token_log_probability: float = math.log(0.9999) @@ -127,7 +129,7 @@ class OpenAICompatibleAPI(LanguageModelAPI): with self._exception_handler(prompt): client = self._get_client() - temperature: NotGiven | float = params.temperature + temperature: NotGiven | float = params.temperature if self.supports_temperature else NOT_GIVEN api_result = await client.chat.completions.create( model=self.model_name, @@ -190,7 +192,7 @@ class OpenAICompatibleAPI(LanguageModelAPI): with self._exception_handler(prompt): client = self._get_client() - temperature: NotGiven | float = params.temperature + temperature: NotGiven | float = params.temperature if self.supports_temperature else NOT_GIVEN api_result = await client.chat.completions.create( model=self.model_name,