How to Integrate Additional LLM Providers Beyond Ollama and Gemini in Hiring Agent
The hiring-agent repository abstracts LLMs behind a unified LLMProvider protocol, allowing you to add new providers by extending the ModelProvider enum in models.py, implementing a provider class with a chat() method, updating the MODEL_PROVIDER_MAPPING dictionary in prompt.py, and wiring the factory in initialize_llm_provider.
The interviewstreet/hiring-agent project uses a provider-based architecture to support multiple Large Language Model backends. While it ships with built-in support for Ollama and Google Gemini, the modular design makes it straightforward to integrate additional LLM providers beyond Ollama and Gemini without modifying the core evaluation logic.
Architecture Overview
The system relies on four key components that work together to route requests to the appropriate backend:
-
ModelProviderenum – Defined inmodels.pyat line 6, this enum enumerates supported backends. Currently includesOLLAMAandGEMINIvalues that you can extend with new entries. -
Provider classes – Concrete implementations like
OllamaProvider(line 71) andGeminiProvider(line 13) inmodels.pythat wrap vendor SDKs and normalize responses to a common format. -
MODEL_PROVIDER_MAPPING– Located inprompt.pyat line 46, this dictionary maps specific model names (e.g.,gemini-2.5-pro) to their correspondingModelProviderenum values. -
initialize_llm_provider– Factory function inllm_utils.pyat line 40 that instantiates the correct provider class based on the model name, falling back to Ollama when necessary.
When you integrate additional LLM providers beyond Ollama and Gemini, these four touchpoints ensure the rest of the codebase—including score calculation and PDF handling—continues to function without modification.
Step-by-Step Implementation Guide
Follow these steps to add a new LLM backend to the system.
Step 1: Extend the ModelProvider Enum
Add a new enum value to the ModelProvider class in main/models.py to represent your provider:
from enum import Enum
class ModelProvider(Enum):
OLLAMA = "ollama"
GEMINI = "gemini"
OPENAI = "openai" # New entry for your provider
Step 2: Implement the Provider Class
Create a provider class in main/models.py that implements the LLMProvider protocol. The class must expose a chat(self, model, messages, options=None, **kwargs) method that returns a dictionary with a normalized response structure:
from typing import List, Dict, Any
class OpenAIProvider:
"""OpenAI API provider implementation."""
def __init__(self, api_key: str):
import openai
openai.api_key = api_key
self.client = openai
def chat(
self,
model: str,
messages: List[Dict[str, str]],
options: Dict[str, Any] = None,
**kwargs,
) -> Dict[str, Any]:
# Convert generic message format to provider-specific format
payload = {"model": model, "messages": messages}
if options:
payload.update(options) # e.g., temperature, top_p
response = self.client.ChatCompletion.create(**payload)
# Normalize to Ollama-like dict expected by downstream code
return {
"message": {
"role": "assistant",
"content": response.choices[0].message.content
}
}
Step 3: Update the Model-to-Provider Mapping
Register your model names in main/prompt.py by adding entries to MODEL_PROVIDER_MAPPING:
from main.models import ModelProvider
MODEL_PROVIDER_MAPPING = {
# Existing entries...
"gpt-4o-mini": ModelProvider.OPENAI,
"gpt-4o": ModelProvider.OPENAI,
}
Step 4: Configure Environment Variables
Add any required API keys or configuration variables to main/prompt.py alongside existing environment variable definitions:
import os
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
Step 5: Wire the Factory Function
Update the initialize_llm_provider function in main/llm_utils.py to instantiate your new provider when the corresponding enum value is detected:
from main.prompt import MODEL_PROVIDER_MAPPING, OPENAI_API_KEY
from main.models import ModelProvider, OpenAIProvider
import logging
logger = logging.getLogger(__name__)
def initialize_llm_provider(model_name: str) -> Any:
model_provider = MODEL_PROVIDER_MAPPING.get(model_name)
if model_provider == ModelProvider.GEMINI:
# Existing Gemini initialization...
from main.models import GeminiProvider
return GeminiProvider()
elif model_provider == ModelProvider.OPENAI:
if not OPENAI_API_KEY:
logger.warning("OpenAI API key not found. Falling back to Ollama.")
# Fall through to default Ollama behavior
else:
logger.info(f"Using OpenAI provider with model {model_name}")
return OpenAIProvider(api_key=OPENAI_API_KEY)
# Default Ollama branch
from main.models import OllamaProvider
return OllamaProvider()
Key Files for LLM Integration
When you integrate additional LLM providers beyond Ollama and Gemini, these three files constitute the complete integration surface:
-
main/models.py– Contains theModelProviderenum and provider class definitions (OllamaProvider,GeminiProvider, and your new implementations). -
main/prompt.py– Houses theMODEL_PROVIDER_MAPPINGdictionary, default model configuration, and environment variable loading for API keys. -
main/llm_utils.py– Implements theinitialize_llm_providerfactory function that routes model names to the appropriate provider instances.
Summary
-
Provider Protocol: The system expects a
chat()method returning a normalized dictionary with amessagekey containingroleandcontent. -
Four Touchpoints: Enum extension, class implementation, mapping registration, and factory wiring are the only changes required to integrate additional LLM providers beyond Ollama and Gemini.
-
Zero Downstream Changes: After implementation, existing evaluation pipelines automatically route requests through your new provider when configured via
DEFAULT_MODELor specific model name parameters. -
Environment Configuration: Store sensitive credentials in environment variables loaded through
prompt.pyto maintain security consistency with existing providers.
Frequently Asked Questions
How does the system handle provider fallback if my new LLM is unavailable?
The initialize_llm_provider function in main/llm_utils.py typically defaults to Ollama when a provider initialization fails or when API keys are missing. You can implement similar fallback logic by checking for missing credentials and logging warnings before falling through to the default Ollama branch.
Can I add cloud providers like Azure OpenAI or Anthropic Claude using the same pattern?
Yes. The architecture supports any LLM service that offers a Python SDK. Implement the chat() method to translate the generic message format into provider-specific API calls, then normalize the response to match the {"message": {"role": "assistant", "content": "..."}} structure expected by the evaluation pipeline.
Where should I store API keys for new providers?
Store API keys as environment variables in main/prompt.py following the pattern used for existing providers. Import these variables into main/llm_utils.py within the initialize_llm_provider function to pass them to your provider class constructor.
Do I need to modify the resume evaluation logic when adding a new provider?
No. The evaluation logic in interviewstreet/hiring-agent calls initialize_llm_provider and interacts with the returned object only through the LLMProvider protocol. As long as your provider implements the chat() method with the correct signature and return format, the scoring and PDF processing modules require no changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →