STATEK
Custom Providers

STATEK Custom Providers

Custom providers let an application use STATEK with model APIs that are not built into the framework.

There are two supported paths:

  • register a standard OpenAI-compatible chat-completions provider with add_provider(...)
  • provide a custom LLM_API implementation and register that class with STATEK

In both cases, registration happens in application or worker startup code before jobs start running.

Register an OpenAI-Compatible Provider

Use this path when the upstream API accepts an OpenAI-compatible chat-completions request.

from statek import add_provider
 
add_provider(
    "SELLTIME",
    api_url="https://selltime.ai/llm_api",
    api_key="...",
)

After registration, use the provider name in agent metadata:

# MODEL: model-1
# PROVIDER: SELLTIME
 
# System Prompt
You write Python against the objects available in context.

add_provider(...) stores api_url and api_key as provider settings. The default implementation sends requests with the OpenAI-compatible payload shape described on Model Providers.

Environment-Based Settings

You can also register only the provider name and let STATEK read settings from the environment:

SELLTIME_API_URL=https://selltime.ai/llm_api
SELLTIME_API_KEY=XYZ
from statek import add_provider
 
add_provider("SELLTIME")

Environment variable names start with the provider name. STATEK creates provider settings when both {PROVIDER}_API_URL and {PROVIDER}_API_KEY are present.

Optional provider fields use the same prefix:

SELLTIME_RESPONSE_FORMAT_FILE=./response-format.json
SELLTIME_USE_PROMPT_CACHING=false

Direct Provider Arguments

Provider-specific values can also be passed directly to add_provider(...):

from statek import add_provider
 
add_provider(
    "CUSTOM_OPENAI",
    api_url="https://models.example.com/v1/chat/completions",
    api_key="...",
    custom_payload="value",
    timeout_hint=30,
)

For the default OpenAI-compatible implementation, extra kwargs are included in the JSON request payload. Settings kwargs are not included in the payload:

  • api_url
  • api_key
  • response_format_file
  • use_prompt_caching

For custom implementations, kwargs are passed to the provider constructor. The implementation decides how to use them.

⚠️

Do not pass secrets as extra payload kwargs unless the upstream provider explicitly expects them in the request body. Keep API keys in settings, environment variables, or a managed secret system.

add_provider(...)

The registration API is:

def add_provider(name: str, llm_api_impl: Type[LLM_API] = None, **kwargs):
    ...

Arguments:

  • name: provider name. It is matched case-insensitively and must not collide with built-in provider names, built-in aliases, or an already registered custom provider.
  • llm_api_impl: optional implementation class for the LLM_API interface. If omitted, STATEK uses the default OpenAI-compatible implementation.
  • kwargs: optional settings and provider-specific constructor arguments.

Registration performs only the name uniqueness check. Bad credentials, missing settings, unsupported provider options, request-shape mismatches, and incomplete custom implementations surface when the provider is used.

When api_url and api_key are passed directly, STATEK creates LLM_API_Settings during registration. If either one is missing, provider settings are loaded later through the normal environment/settings flow.

If the same kwarg is supplied at registration and later through LLM_API.get(...), the later LLM_API.get(...) value wins for that provider instance.

Implement a Custom LLM_API

Use a custom implementation when the provider is not OpenAI-compatible or when you need custom authentication, payload formatting, response parsing, or usage accounting.

from statek import LLM_API, LLM_Response, LLM_Stats, add_provider
 
class CustomAPI(LLM_API):
    def __init__(self, settings, **kwargs):
        self.settings = settings
        self.kwargs = kwargs
 
    def _build_request_payload(
        self,
        system_prompt=None,
        model=None,
        metadata=None,
        tools=None,
        chat_history=None,
        chat_style=None,
        temperature=None,
        enable_reasoning=False,
    ):
        return {
            "model": self.require_model(model),
            "input": system_prompt or "",
        }
 
    async def _process_request(
        self,
        system_prompt=None,
        model=None,
        metadata=None,
        tools=None,
        chat_history=None,
        chat_style=None,
        temperature=None,
        enable_reasoning=False,
    ):
        payload = self._build_request_payload(
            system_prompt=system_prompt,
            model=model,
            metadata=metadata,
            tools=tools,
            chat_history=chat_history,
            chat_style=chat_style,
            temperature=temperature,
            enable_reasoning=enable_reasoning,
        )
 
        # Send payload to your provider and parse the response here.
        # This example returns a fixed response to show the required shape.
        return LLM_Response(
            text="print('hello from custom provider')",
            stats=LLM_Stats(
                total_bytes_sent=0,
                total_bytes_received=0,
                cost=0,
                input_tokens=0,
                output_tokens=0,
                cached_tokens=0,
            ),
            call_requests=None,
        )
 
add_provider(
    "CUSTOM",
    llm_api_impl=CustomAPI,
    api_url="https://models.example.com/custom",
    api_key="...",
    vendor_option="value",
)

LLM_API.process_request(...) handles shared validation and tool-scope selection, then calls your _process_request(...). Your implementation should:

  • build the provider-specific request payload
  • send the HTTP request or call the provider SDK
  • parse text responses into LLM_Response.text
  • parse tool calls into LLM_Response.call_requests when the provider supports tools
  • return LLM_Stats with usage data when available

Operational Notes

Register custom providers during process startup before creating or running jobs. Provider registration is in memory for the current Python process.

The provider factory and settings helpers are cached for long-running workers. In tests or dynamic configuration scenarios, clear the relevant caches before expecting changed environment variables or registrations to be visible.

Provider setup does not sandbox agent-executed Python. Keep sandboxing, permissioning, secrets discipline, resource limits, logging, and provider quota controls in your application and deployment layer.

Where to go next

Read Model Providers for built-in provider setup, Configuration for settings precedence, Security for credentials and runtime boundaries, and API Reference for add_provider(...) and LLM_API.