Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
2181b9e
PoC of dynamic SUTs
rogthefrog May 1, 2025
750ba44
start implementing dynamic SUTs
rogthefrog May 1, 2025
f412e02
noop; remove incorrect comment
rogthefrog May 1, 2025
6c23ee7
added sut name extraction
rogthefrog May 1, 2025
fe26042
complete support for dynamic SUTs: look up alternative providers for …
rogthefrog May 1, 2025
f2ea883
bump huggingface-hub to use the new inference provider API
rogthefrog May 1, 2025
da75c1a
finish dynamic SUT integration; minor refactor for consistency
rogthefrog May 1, 2025
1cfa4c9
unit test for SUT maker
rogthefrog May 1, 2025
542d851
print -> logging
rogthefrog May 2, 2025
195247c
ensure the model name doesn't include extra prefixes
rogthefrog May 2, 2025
2c21185
more distinguishable error messages
rogthefrog May 2, 2025
f09608a
mypy
rogthefrog May 2, 2025
49b2784
dynamic sut names are now handled like sut IDs
rogthefrog May 2, 2025
b900c41
SUT IDs are required
rogthefrog May 2, 2025
fc59141
add test for make_sut
rogthefrog May 2, 2025
c534c03
add warning to log if we aren't able to register all the dynamic suts…
rogthefrog May 2, 2025
723be4c
add tests for dynamic suts
rogthefrog May 2, 2025
9ab6e77
noop; add return types
rogthefrog May 5, 2025
a6a9687
refactor factory method out of parent class; add return type hints; a…
rogthefrog May 6, 2025
2604e56
remove dependency that belongs in modelgauge
rogthefrog May 6, 2025
0b121b0
improve error handling, such that the user knows what exactly went wr…
rogthefrog May 6, 2025
b2b4d66
add type hint, simplify method by using abc.abstractmethod
rogthefrog May 6, 2025
2dfcf2d
not in use
rogthefrog May 6, 2025
f948fc5
refactor dynamic SUT registration outside of the run module
rogthefrog May 7, 2025
e33b7b8
remove duplicate test after refactor
rogthefrog May 7, 2025
6b75d9e
add list_suts to modelbench for convenience
rogthefrog May 7, 2025
0f1a301
intermediate checkpoint for new SUT UID scheme
rogthefrog May 8, 2025
40ec508
partial refactor: change factory to finder
rogthefrog May 10, 2025
db35d6d
refresh lock file
rogthefrog Jun 2, 2025
90b75ca
refresh lock file
rogthefrog Jun 3, 2025
f530d64
refactor and simplify, using SUTMetadata
rogthefrog Jun 3, 2025
ef74f99
update test to use final sut id schema
rogthefrog Jun 3, 2025
75b5ca3
fix test that failed when the random selections were both spaces
rogthefrog Jun 3, 2025
010dc1d
update dynamic SUT scheme
rogthefrog Jun 3, 2025
0da4c7c
update test with new dynamic sut name schema; separate tests for clar…
rogthefrog Jun 3, 2025
ef2cf05
distinguish dynamic SUTs from non-dynamic erroneous SUTs
rogthefrog Jun 3, 2025
55bf060
refactor: move sut ID parsing out of DynamicSUTMaker and into SUTMeta…
rogthefrog Jun 4, 2025
a4ae9b6
more informative error message
rogthefrog Jun 4, 2025
119983e
refactor: move sut id validation out of modelbench so it can be share…
rogthefrog Jun 5, 2025
671760c
refactor SUTMetadata to DynamicSUTMetadata
rogthefrog Jun 9, 2025
fa425df
refactor validate_sut_uid and use validate_uid for everything like be…
rogthefrog Jun 9, 2025
904ffaf
add tests for uid validators
rogthefrog Jun 10, 2025
42bcd6a
refactor *Maker to *Factory
rogthefrog Jun 10, 2025
ffcc2cc
continue refactor *Maker to *Factory
rogthefrog Jun 10, 2025
aefb50b
rename vendor to maker for clarity
rogthefrog Jun 10, 2025
863f0e4
noop; remove duplicate import
rogthefrog Jun 10, 2025
e484d89
add dynamic SUT support to more modelgauge commands
rogthefrog Jun 10, 2025
55dd06d
change maker and model parsing
rogthefrog Jun 10, 2025
98ef7a1
refactor exception name; add dynamic SUT for together
rogthefrog Jun 10, 2025
13d5771
add huggingface (direct) dynamic SUT
rogthefrog Jun 10, 2025
c8e521a
remove a test that tested a validation rule that no longer exists
rogthefrog Jun 10, 2025
1a01f55
noop; fix comment
rogthefrog Jun 10, 2025
e3961cf
add -s short option to run-sut so it behaves like the other commands
rogthefrog Jun 12, 2025
2a741f7
add short option for test
rogthefrog Jun 12, 2025
0a8196e
fix bug that always showed SUT uids as options, even if the erroneous…
rogthefrog Jun 12, 2025
bc9c384
refresh poetry lock file
rogthefrog Jun 12, 2025
94f0886
🤜 mypy
rogthefrog Jun 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions plugins/huggingface/modelgauge/suts/huggingface_sut_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import logging

import huggingface_hub as hfh
from modelgauge.auth.huggingface_inference_token import HuggingFaceInferenceToken
from modelgauge.dynamic_sut_factory import (
DynamicSUTFactory,
ModelNotSupportedError,
ProviderNotFoundError,
UnknownSUTProviderError,
)

from modelgauge.dynamic_sut_metadata import DynamicSUTMetadata
from modelgauge.secret_values import InjectSecret
from modelgauge.suts.huggingface_chat_completion import (
HuggingFaceChatCompletionDedicatedSUT,
HuggingFaceChatCompletionServerlessSUT,
)

DRIVER_NAME = "hfrelay"


def make_sut(sut_name: str, *args, **kwargs) -> tuple | None:
sut_metadata: DynamicSUTMetadata = DynamicSUTMetadata.parse_sut_uid(sut_name)
if sut_metadata.is_proxied():
if sut_metadata.driver != DRIVER_NAME:
raise UnknownSUTProviderError(f"Unknown proxy '{sut_metadata.driver}'")
return HuggingFaceChatCompletionServerlessSUTFactory.make_sut(sut_metadata)
else:
return HuggingFaceChatCompletionDedicatedSUTFactory.make_sut(sut_metadata)


def find_inference_provider_for(model_name) -> dict | None:
try:
inference_providers = hfh.model_info(model_name, expand="inferenceProviderMapping")
providers = inference_providers.inference_provider_mapping
if not providers:
raise ProviderNotFoundError(f"No provider found for {model_name}")
return providers
except hfh.errors.RepositoryNotFoundError as mexc:
logging.error(f"Huggingface doesn't know model {model_name}, or you need credentials for its repo: {mexc}")
raise ModelNotSupportedError from mexc


class HuggingFaceSUTFactory(DynamicSUTFactory):

@staticmethod
def get_secrets() -> InjectSecret:
hf_token = InjectSecret(HuggingFaceInferenceToken)
return hf_token


class HuggingFaceChatCompletionServerlessSUTFactory(HuggingFaceSUTFactory):

@staticmethod
def find(model_name, provider: str = "", find_alternative: bool = False) -> str | None:
sut_metadata: DynamicSUTMetadata = DynamicSUTMetadata.parse_sut_uid(model_name)
if not provider:
provider = sut_metadata.provider

found_provider = None
try:
inference_providers = find_inference_provider_for(sut_metadata.external_model_name())
found = inference_providers.get(provider, None) # type: ignore
if found:
found_provider = provider
else:
if find_alternative:
for alt_provider, _ in inference_providers.inference_provider_mapping.items(): # type: ignore
found_provider = str(alt_provider)
break # we just grab the first one; is that the right choice?
if not found_provider:
if provider:
msg = f"{model_name} is not available on {provider} via Huggingface"
else:
msg = f"No provider found for {model_name}"
raise ProviderNotFoundError(msg)
except Exception as e:
logging.error(f"Error looking up inference providers for {model_name} and provider {provider}: {e}")
raise
return found_provider

@staticmethod
def make_sut(sut_metadata: DynamicSUTMetadata) -> tuple | None:
logging.info(
f"Looking up serverless inference endpoints for {sut_metadata.model} on {sut_metadata.provider}..."
)
model_name = sut_metadata.external_model_name()
found_provider = HuggingFaceChatCompletionServerlessSUTFactory.find(model_name, sut_metadata.provider)
if found_provider:
sut_uid = DynamicSUTMetadata.make_sut_uid(sut_metadata)
return (
HuggingFaceChatCompletionServerlessSUT,
sut_uid,
model_name,
found_provider,
HuggingFaceSUTFactory.get_secrets(),
)
else:
logging.error(f"{sut_metadata.model} on {sut_metadata.provider} not found")
return None


class HuggingFaceChatCompletionDedicatedSUTFactory(HuggingFaceSUTFactory):

@staticmethod
def find(model_name) -> str | None:
try:
endpoints = hfh.list_inference_endpoints()
for e in endpoints:
if e.repository == model_name and e.status != "running":
try:
e.resume()
except Exception as ie:
logging.error(
f"Found endpoint for {model_name} but unable to start it. Check your token's permissions. {ie}"
)
return e.name
except Exception as oe:
logging.error(f"Error looking up dedicated endpoints for {model_name}: {oe}")
return None

@staticmethod
def make_sut(sut_metadata: DynamicSUTMetadata) -> tuple | None:
model_name = HuggingFaceChatCompletionDedicatedSUTFactory.find(sut_metadata.external_model_name())
if model_name:
sut_uid = DynamicSUTMetadata.make_sut_uid(sut_metadata)
return (HuggingFaceChatCompletionDedicatedSUT, sut_uid, model_name, HuggingFaceSUTFactory.get_secrets())
return None
2 changes: 1 addition & 1 deletion plugins/huggingface/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ packages = [{include = "modelgauge"}]

[tool.poetry.dependencies]
python = "^3.10"
huggingface-hub = "^0.29.0"
huggingface-hub = "^0.30.2"

[build-system]
requires = ["poetry-core"]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from typing import Optional
from unittest.mock import Mock, patch

import pytest
from huggingface_hub import (
ChatCompletionOutput,
Expand All @@ -10,18 +13,16 @@
InferenceEndpointStatus,
) # type: ignore
from huggingface_hub.utils import HfHubHTTPError # type: ignore
from typing import Optional
from unittest.mock import Mock, patch

from modelgauge.auth.huggingface_inference_token import HuggingFaceInferenceToken
from modelgauge.prompt import TextPrompt, ChatPrompt, ChatRole
import modelgauge.prompt
from modelgauge.sut import SUTOptions, SUTResponse, TokenProbability, TopTokens
from modelgauge.suts.huggingface_chat_completion import (
ChatMessage,
HuggingFaceChatCompletionDedicatedSUT,
HuggingFaceChatCompletionOutput,
HuggingFaceChatCompletionRequest,
HuggingFaceChatCompletionDedicatedSUT,
)


Expand Down Expand Up @@ -224,7 +225,7 @@ def test_huggingface_chat_completion_evaluate_transforms_response_correctly(mock
{
"finish_reason": "stop",
"index": 0,
"message": {"role": "assistant", "content": "response", "tool_calls": None},
"message": {"role": "assistant", "content": "response", "tool_call_id": None, "tool_calls": None},
"logprobs": None,
}
],
Expand Down
50 changes: 50 additions & 0 deletions plugins/huggingface/tests/test_huggingface_sut_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from unittest.mock import patch

import huggingface_hub as hfh

import pytest

from modelgauge.dynamic_sut_factory import (
ModelNotSupportedError,
ProviderNotFoundError,
UnknownSUTProviderError,
)

from plugins.huggingface.modelgauge.suts.huggingface_sut_factory import make_sut


def test_make_sut():
with patch(
"plugins.huggingface.modelgauge.suts.huggingface_sut_factory.HuggingFaceChatCompletionServerlessSUTFactory.find",
return_value="cohere",
):
assert make_sut(sut_name="google/gemma:cohere:hfrelay") is not None

with patch(
"plugins.huggingface.modelgauge.suts.huggingface_sut_factory.HuggingFaceChatCompletionServerlessSUTFactory.find",
return_value=None,
):
assert make_sut(sut_name="google/gemma:cohere:hfrelay") is None


def test_make_sut_bad_proxy():
with pytest.raises(UnknownSUTProviderError):
make_sut("google/gemma:cohere:bogus")


def test_make_sut_bad_provider():
with patch(
"plugins.huggingface.modelgauge.suts.huggingface_sut_factory.find_inference_provider_for",
return_value={"example": ""},
):
with pytest.raises(ProviderNotFoundError):
_ = make_sut(sut_name="google/gemma:bogus:hfrelay")


def test_make_sut_bad_model():
with patch(
"plugins.huggingface.modelgauge.suts.huggingface_sut_factory.hfh.model_info",
side_effect=hfh.errors.RepositoryNotFoundError("error"),
):
with pytest.raises(ModelNotSupportedError):
_ = make_sut(sut_name="google/fake:cohere:hfrelay")
Loading