Skip to content

Types

Core type definitions and data models used throughout the SDK.

types

JobContext

Bases: BaseModel

Source code in ivcap_service/types.py
class JobContext(BaseModel):
    job_id: str
    report: EventReporter
    job_authorization: str | None = None

    _ivcap: IVCAP | None = PrivateAttr(None)

    model_config = ConfigDict(arbitrary_types_allowed=True)

    @property
    def ivcap(self) -> IVCAP:
        if self._ivcap is None:
            self._ivcap = cast(IVCAP, IVCAP())
        return self._ivcap

    def llm_client(self, **kwargs: Any) -> "openai.OpenAI":
        """Return an OpenAI-compatible client, ready to use.

        This hides the details of how/whether to route completions through a
        LiteLLM proxy from the service code, and resolves the API key in the
        following order (first match wins):

        1. An explicit 'api_key' kwarg passed in here.
        2. 'LITELLM_PROXY' environment variable - if set, 'base_url' is
           pointed at that proxy's '/v1' endpoint and a placeholder
           'api_key' is used (the proxy itself handles
           authentication/authorization), so no real key is needed.
        3. 'OPENAI_API_KEY' environment variable - the usual variable read
           by a plain 'openai.OpenAI()' client ('OPENAI_BASE_URL' is used
           the same way for the endpoint).
        4. '~/.config/openai/api_key' - a plain text file containing just
           the key, as a local-development fallback for when you don't want
           to export environment variables.

        If none of the above yields a key (and 'LITELLM_PROXY' isn't set,
        since no key is needed in that case), a 'RuntimeError' is raised
        with guidance on how to configure one.

        Any 'kwargs' passed in are forwarded to 'openai.OpenAI(...)' and take
        precedence over everything above (e.g. pass 'base_url=...' to
        override the endpoint too).

        See also 'DEFAULT_LLM_MODEL' for the default model name to pass to
        e.g. 'client.chat.completions.create(model=..., ...)' when the
        caller/request doesn't specify one.

        Requires the optional 'openai' package to be installed.
        """
        try:
            import openai
        except ImportError as e:
            raise ImportError(
                "The 'openai' package is required to use JobContext.llm_client(). "
                "Install it with e.g. 'pip install openai'."
            ) from e

        base_url = os.getenv("LITELLM_PROXY")
        if base_url is not None:
            kwargs.setdefault("base_url", f"{base_url}/v1")
            kwargs.setdefault("api_key", "not-needed")
        elif "api_key" not in kwargs and not os.getenv("OPENAI_API_KEY"):
            api_key = _read_openai_api_key_file()
            if api_key is None:
                raise RuntimeError(
                    "No OpenAI API key found. Set the 'OPENAI_API_KEY' environment "
                    f"variable, write the key to '{_OPENAI_API_KEY_FILE}', or pass "
                    "'api_key=...' to 'llm_client()' directly."
                )
            kwargs["api_key"] = api_key
        return openai.OpenAI(**kwargs)

llm_client(**kwargs)

Return an OpenAI-compatible client, ready to use.

This hides the details of how/whether to route completions through a LiteLLM proxy from the service code, and resolves the API key in the following order (first match wins):

  1. An explicit 'api_key' kwarg passed in here.
  2. 'LITELLM_PROXY' environment variable - if set, 'base_url' is pointed at that proxy's '/v1' endpoint and a placeholder 'api_key' is used (the proxy itself handles authentication/authorization), so no real key is needed.
  3. 'OPENAI_API_KEY' environment variable - the usual variable read by a plain 'openai.OpenAI()' client ('OPENAI_BASE_URL' is used the same way for the endpoint).
  4. '~/.config/openai/api_key' - a plain text file containing just the key, as a local-development fallback for when you don't want to export environment variables.

If none of the above yields a key (and 'LITELLM_PROXY' isn't set, since no key is needed in that case), a 'RuntimeError' is raised with guidance on how to configure one.

Any 'kwargs' passed in are forwarded to 'openai.OpenAI(...)' and take precedence over everything above (e.g. pass 'base_url=...' to override the endpoint too).

See also 'DEFAULT_LLM_MODEL' for the default model name to pass to e.g. 'client.chat.completions.create(model=..., ...)' when the caller/request doesn't specify one.

Requires the optional 'openai' package to be installed.

Source code in ivcap_service/types.py
def llm_client(self, **kwargs: Any) -> "openai.OpenAI":
    """Return an OpenAI-compatible client, ready to use.

    This hides the details of how/whether to route completions through a
    LiteLLM proxy from the service code, and resolves the API key in the
    following order (first match wins):

    1. An explicit 'api_key' kwarg passed in here.
    2. 'LITELLM_PROXY' environment variable - if set, 'base_url' is
       pointed at that proxy's '/v1' endpoint and a placeholder
       'api_key' is used (the proxy itself handles
       authentication/authorization), so no real key is needed.
    3. 'OPENAI_API_KEY' environment variable - the usual variable read
       by a plain 'openai.OpenAI()' client ('OPENAI_BASE_URL' is used
       the same way for the endpoint).
    4. '~/.config/openai/api_key' - a plain text file containing just
       the key, as a local-development fallback for when you don't want
       to export environment variables.

    If none of the above yields a key (and 'LITELLM_PROXY' isn't set,
    since no key is needed in that case), a 'RuntimeError' is raised
    with guidance on how to configure one.

    Any 'kwargs' passed in are forwarded to 'openai.OpenAI(...)' and take
    precedence over everything above (e.g. pass 'base_url=...' to
    override the endpoint too).

    See also 'DEFAULT_LLM_MODEL' for the default model name to pass to
    e.g. 'client.chat.completions.create(model=..., ...)' when the
    caller/request doesn't specify one.

    Requires the optional 'openai' package to be installed.
    """
    try:
        import openai
    except ImportError as e:
        raise ImportError(
            "The 'openai' package is required to use JobContext.llm_client(). "
            "Install it with e.g. 'pip install openai'."
        ) from e

    base_url = os.getenv("LITELLM_PROXY")
    if base_url is not None:
        kwargs.setdefault("base_url", f"{base_url}/v1")
        kwargs.setdefault("api_key", "not-needed")
    elif "api_key" not in kwargs and not os.getenv("OPENAI_API_KEY"):
        api_key = _read_openai_api_key_file()
        if api_key is None:
            raise RuntimeError(
                "No OpenAI API key found. Set the 'OPENAI_API_KEY' environment "
                f"variable, write the key to '{_OPENAI_API_KEY_FILE}', or pass "
                "'api_key=...' to 'llm_client()' directly."
            )
        kwargs["api_key"] = api_key
    return openai.OpenAI(**kwargs)

BinaryResult dataclass

If the result of the tool is a non json serialisable object, return an instance of this class indicating the content-type and the actual result either as a byte array or a file handle to a binary content (open(..., "rb"))

Source code in ivcap_service/types.py
@dataclass
class BinaryResult:
    """If the result of the tool is a non json serialisable object, return an
    instance of this class indicating the content-type and the actual
    result either as a byte array or a file handle to a binary content (`open(..., "rb")`)"""

    content_type: str = Field(description="Content type of result serialised")
    content: bytes | str | BinaryIO = Field(
        description="Content to send, either as byte array or file handle"
    )

ExecutionError

Bases: BaseModel

Pydantic model for execution errors.

Source code in ivcap_service/types.py
class ExecutionError(BaseModel):
    """
    Pydantic model for execution errors.
    """

    jschema: str = Field(
        default="urn:ivcap:schema.service.error.1",
        validation_alias="$schema",
        serialization_alias="$schema",
    )
    error: str = Field(description="Error message")
    type: str = Field(description="Error type")
    traceback: str | None = Field(default=None, description="traceback")

    model_config = {
        "populate_by_name": True,
    }