Ollama

Integrate with open source LLMs using Ollama. With an instance of Ollama running locally you can use this integration to have a conversation in an Incident, download models, and create new models.

Utilities · Ollama

Details

IDOllama
ProviderOpen Source
CategoryUtilities
From Version6.0.0
Docker Imagedemisto/python3:3.12.8.3296088
Supported ModulesAgentix XSIAM

README

Integrate with open source LLMs using Ollama. With an instance of Ollama running locally you can use this integration to have a conversation in an Incident, download models, and create new models.

Configure Ollama in Cortex

Parameter Description Required
Protocol HTTP or HTTPS False
Server hostname or IP Enter the Ollama IP or hostname True
Port The port Ollama is running on True
Path By default Ollama’s API path is /api, but you may be running it behind a proxy with a different path. True
Trust any certificate (not secure) Trust any certificate (not secure) False
Use system proxy settings Use system proxy settings False
Cloudflare Access Client Id If Ollama is running behind CLoudflare ZeroTrust, provide the Service Access ID here. False
Cloudflare Access Client Secret If Ollama is running behind CLoudflare ZeroTrust, provide the Service Access Secret here. False
Default Model Some commands allow you to specify a model. If no model is provided, this value will be used. False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

ollama-list-models


Get a list of all available models

Base Command

ollama-list-models

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

Path Type Description
ollama.models unknown Output of the command

ollama-model-pull


Pull a model

Base Command

ollama-model-pull

Input

Argument Name Description Required
model Name of model to pull. See https://ollama.com/library for a list of options. Optional

Context Output

Path Type Description
ollama.pull unknown Output of the command

ollama-model-delete


Delete a model

Base Command

ollama-model-delete

Input

Argument Name Description Required
model The name of the model to delete. Optional

Context Output

Path Type Description
ollama.delete unknown Output of the command

ollama-conversation


General chat command that tracks the conversation history in the Incident.

Base Command

ollama-conversation

Input

Argument Name Description Required
model The model name. Optional
message The message to be sent. Required

Context Output

Path Type Description
ollama.history unknown Output of the command

ollama-model-info


Show information for a specific model.

Base Command

ollama-model-info

Input

Argument Name Description Required
model name of the model to show. Optional

Context Output

Path Type Description
ollama.show unknown Output of the command

ollama-model-create


Create a new model from a Modelfile.

Base Command

ollama-model-create

Input

Argument Name Description Required
model name of the model to create. Required
model_file contents of the Modelfile. Required

Context Output

Path Type Description
ollama.create unknown Output of the command

ollama-generate


Generate a response for a given prompt with a provided model. Conversation history IS NOT tracked.

Base Command

ollama-generate

Input

Argument Name Description Required
model The model name. Optional
message The message to be sent. Optional

Context Output

Path Type Description
ollama.generate unknown Output of the command

Configuration parameters

  • protocol — Protocol
  • host — Server hostname or IP (required)
  • port — Port (required)
  • path — Path (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • cf_id — Cloudflare Access Client Id
  • cf_secret — Cloudflare Access Client Secret
  • default_model — Default Model

Commands (7)

  • ollama-conversation

    General chat command that tracks the conversation history in the Incident.

  • ollama-generate

    Generate a response for a given prompt with a provided model. Conversation history IS NOT tracked.

  • ollama-list-models

    Get a list of all available models.

  • ollama-model-create

    Create a new model from a Modelfile.

  • ollama-model-delete

    Delete a model.

  • ollama-model-info

    Show information for a specific model.

  • ollama-model-pull

    Pull a model.

import math

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401

""" CLIENT CLASS """


class Client(BaseClient):
    """Client class to interact with the service API

    This Client implements API calls, and does not contain any Demisto logic.
    Should only do requests and return data.
    It inherits from BaseClient defined in CommonServer Python.
    Most calls use _http_request() that handles proxy, SSL verification, etc.
    For this HelloWorld implementation, no special attributes defined
    """

    def list_local_models(self):
        response = self._http_request("GET", "tags")
        return response

    def pull_model(self, model_name):
        response = self._http_request("POST", "pull", json_data={"name": model_name, "stream": False})
        return response

    def delete_model(self, model_name):
        response = self._http_request("DELETE", "delete", json_data={"name": model_name})
        return response

    def create_model(self, model_name, model_file):
        response = self._http_request("POST", "create", json_data={"name": model_name, "modelfile": model_file, "stream": False})
        return response

    def show_model_info(self, model_name):
        response = self._http_request("POST", "show", json_data={"name": model_name})
        return response

    def generate(self, model_name, message):
        response = self._http_request("POST", "generate", json_data={"model": model_name, "prompt": message, "stream": False})
        return response

    def chat(self, model_name, history):
        response = self._http_request("POST", "chat", json_data={"model": model_name, "messages": history, "stream": False})
        return response


""" HELPER FUNCTIONS """


def convert_size(size_bytes):
    if size_bytes == 0:
        return "0B"
    size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
    i = int(math.floor(math.log(size_bytes, 1024)))
    p = math.pow(1024, i)
    s = round(size_bytes / p, 2)
    return f"{s} {size_name}"


""" COMMAND FUNCTIONS """


def test_module(client: Client, params) -> str:
    try:
        client.list_local_models()
        return "ok"
    except DemistoException as e:
        if "Forbidden" in str(e):
            return "Authorization Error: make sure API Key is correctly set"
        raise DemistoException(str(e))


def list_local_models_command(client):
    """
    List models that are available locally.
    """

    response = client.list_local_models()

    results = []
    for item in response["models"]:
        new_item = {"Name": item["name"], "Size": convert_size(item["size"])}
        results.append(new_item)

    readable = tableToMarkdown(
        name="List Local Models",
        t=results,
        metadata="Click here to access the models available for download: [here](https://ollama.com/library).",
        removeNull=True,
    )
    return CommandResults(
        readable_output=readable, outputs_prefix="ollama.models", outputs_key_field="ollama.models", outputs=response
    )


def pull_model_command(client, model_name):
    """
    Download a model from the ollama library.
    Cancelled pulls are resumed from where they left off, and multiple calls will share the same download progress.
    """

    response = client.pull_model(model_name)

    if response["status"] == "success":
        readable = f"Successfully pulled the **{model_name}** model."

        return CommandResults(
            readable_output=readable, outputs_prefix="ollama.pull", outputs_key_field="ollama.pull", outputs=response
        )
    else:
        readable = f"Failed to pull **{model_name}**."
        return CommandResults(
            readable_output=readable, outputs_prefix="ollama.pull", outputs_key_field="ollama.pull", outputs=response
        )


def show_model_info_command(client, model_name):
    """
    Show information about a model including details, modelfile, template, parameters, license, and system prompt.
    """

    response = client.show_model_info(model_name)

    # return json.dumps(response, indent=4)

    readable = tableToMarkdown("results", response)

    return CommandResults(
        readable_output=readable, outputs_prefix="ollama.show", outputs_key_field="ollama.show", outputs=response
    )


def delete_model_command(client, model_name):
    """
    Delete a model and its data.
    """

    response = client.delete_model(model_name)

    if response is None:
        readable = f"Successfully deleted the **{model_name}** model."

        return CommandResults(
            readable_output=readable, outputs_prefix="ollama.delete", outputs_key_field="ollama.delete", outputs=response
        )
    else:
        readable = f"Failed to delete **{model_name}**."
        return CommandResults(
            readable_output=readable, outputs_prefix="ollama.delete", outputs_key_field="ollama.delete", outputs=response
        )


def create_model_command(client, model_name, model_file):
    """
    Create a model from a Modelfile.
    """

    response = client.create(model_name, model_file)

    readable = f"Successfully created **{model_name}**"

    return CommandResults(
        readable_output=readable, outputs_prefix="ollama.create", outputs_key_field="ollama.create", outputs=response
    )


def generate_command(client, model_name, message):
    """
    Generate a response for a given prompt with a provided model.
    """

    response = client.generate(model_name, message)
    readable = f"`{model_name}`: {response['response']}"

    return CommandResults(
        readable_output=readable,
        outputs_prefix="ollama.generate",
        outputs_key_field="ollama.generate",
        outputs=response["response"],
    )


def conversation_command(client, model_name, message, history):
    """
    Generate the next message in a chat with a provided model.
    """

    if history == {}:
        response = client.generate(model_name, message)
        readable = f"`{model_name}`: {response['response']}"

        return CommandResults(
            readable_output=readable,
            outputs_prefix="ollama.history",
            outputs_key_field="ollama.history",
            outputs=[{"role": "user", "content": message}, {"role": "assistant", "content": response["response"]}],
        )

    else:
        history.append({"role": "user", "content": message})

        response = client.chat(model_name, history)

        readable = f"`{model_name}`: {response['message']['content']}"
        readable = f"{response['message']['content']}"

        return CommandResults(
            readable_output=readable,
            outputs_prefix="ollama.history",
            outputs_key_field="ollama.history",
            outputs=[{"role": "user", "content": message}, response["message"]],
        )


""" MAIN FUNCTION """


def main() -> None:  # pragma: no cover
    """
    main function, parses params and runs command functions
    """

    params = demisto.params()
    args = demisto.args()
    command = demisto.command()
    context = demisto.context()

    protocol = params.get("protocol", "https")
    host = params.get("host", "localhost")
    port = params.get("port", 11434)
    path = params.get("path", "/api")
    base_url = f"{protocol}://{host}:{port}{path}"

    verify_certificate = not params.get("insecure", False)
    proxy = params.get("proxy", False)

    demisto.debug(f"Command being called is {command}")
    try:
        cf_client_id = params.get("cf_id", None)
        cf_client_key = params.get("cf_secret", None)
        default_model = params.get("default_model", None)
        model_name = args.get("model", default_model)

        headers = {}
        if cf_client_id is not None and cf_client_key is not None:
            headers = {"CF-Access-Client-Id": cf_client_id, "CF-Access-Client-Secret": cf_client_key}

        client = Client(base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy, timeout=300)

        if command == "test-module":
            # This is the call made when pressing the integration Test button.
            result = test_module(client, params)
            return_results(result)

        elif command == "ollama-list-models":
            result = list_local_models_command(client)
            return_results(result)

        elif command == "ollama-model-pull":
            result = pull_model_command(client, model_name)
            return_results(result)

        elif command == "ollama-model-delete":
            result = delete_model_command(client, model_name)
            return_results(result)

        elif command == "ollama-model-create":
            model_file = args.get("model_file", None)
            result = create_model_command(client, model_name, model_file)
            return_results(result)

        elif command == "ollama-model-info":
            result = show_model_info_command(client, model_name)
            return_results(result)

        elif command == "ollama-generate":
            message = args.get("message", None)
            result = generate_command(client, model_name, message)
            return_results(result)

        elif command == "ollama-conversation":
            message = args.get("message", None)
            history = context.get("ollama", {}).get("history", {})
            result = conversation_command(client, model_name, message, history)
            return_results(result)

        else:
            raise NotImplementedError(f"Command {command} is not implemented")

    # Log exceptions and return errors
    except Exception as e:
        return_error(f"Failed to execute `{command}` command.\nError:\n{e!s}")


""" ENTRY POINT """


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()