clan_lib: Add llm unit tests
This commit is contained in:
289
pkgs/clan-cli/clan_lib/llm/llm_test.py
Normal file
289
pkgs/clan-cli/clan_lib/llm/llm_test.py
Normal file
@@ -0,0 +1,289 @@
|
||||
from collections.abc import Callable
|
||||
|
||||
import pytest
|
||||
from clan_cli.tests.fixtures_flakes import nested_dict
|
||||
from clan_lib.flake.flake import Flake
|
||||
from clan_lib.llm.llm import (
|
||||
OpenAIFunctionSchema,
|
||||
aggregate_openai_function_schemas,
|
||||
llm_final_decision_to_inventory_instances,
|
||||
)
|
||||
from clan_lib.llm.schemas import FunctionCallType, clan_module_to_openai_spec
|
||||
from clan_lib.services.modules import list_service_modules
|
||||
|
||||
|
||||
@pytest.mark.with_core
|
||||
def test_clan_module_to_llm_func(
|
||||
clan_flake: Callable[..., Flake],
|
||||
) -> None:
|
||||
# ATTENTION! This method lacks Typechecking
|
||||
config = nested_dict()
|
||||
# explicit module selection
|
||||
# We use this random string in test to avoid code dependencies on the input name
|
||||
config["inventory"]["instances"]["foo"]["module"]["input"] = (
|
||||
"Y2xhbi1jaW9yZS1uZXZlci1kZXBlbmQtb24tbWU"
|
||||
)
|
||||
config["inventory"]["instances"]["foo"]["module"]["name"] = "sshd"
|
||||
# input = null
|
||||
config["inventory"]["instances"]["bar"]["module"]["input"] = None
|
||||
config["inventory"]["instances"]["bar"]["module"]["name"] = "sshd"
|
||||
|
||||
config["inventory"]["machines"] = {
|
||||
"machine1": {
|
||||
"tags": ["production", "backup"],
|
||||
},
|
||||
"machine2": {
|
||||
"tags": ["client"],
|
||||
},
|
||||
"machine3": {
|
||||
"tags": ["client"],
|
||||
},
|
||||
}
|
||||
config["inventory"]["tags"] = {
|
||||
"production": [],
|
||||
"backup": [],
|
||||
"client": [],
|
||||
}
|
||||
|
||||
# Omit input
|
||||
config["inventory"]["instances"]["baz"]["module"]["name"] = "sshd"
|
||||
# external input
|
||||
flake = clan_flake(config)
|
||||
|
||||
service_modules = list_service_modules(flake)
|
||||
|
||||
# Module(usage_ref={'name': 'borgbackup', 'input': None}, info=ModuleInfo(manifest=ModuleManifest(name='borgbackup', description='Efficient, deduplicating backup program with optional compression and secure encryption.', categories=['System'], features={'API': True}), roles={'client': Role(name='client', description='A borgbackup client that backs up to all borgbackup server roles.'), 'server': Role(name='server', description='A borgbackup server that stores the backups of clients.')}), native=True, instance_refs=[]),
|
||||
borgbackup_service = next(
|
||||
m for m in service_modules.modules if m.usage_ref.get("name") == "borgbackup"
|
||||
)
|
||||
|
||||
assert borgbackup_service is not None
|
||||
|
||||
available_machines = ["machine1", "machine2", "server1"]
|
||||
available_tags = ["production", "backup", "client"]
|
||||
|
||||
generated_tool_func = clan_module_to_openai_spec(
|
||||
borgbackup_service, available_tags, available_machines
|
||||
)
|
||||
|
||||
expected_tool_func: OpenAIFunctionSchema = {
|
||||
"type": "function",
|
||||
"name": "borgbackup",
|
||||
"description": "Efficient, deduplicating backup program with optional compression and secure encryption.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"module": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
},
|
||||
"roles": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"client": {
|
||||
"type": "object",
|
||||
"description": "A borgbackup client that backs up to all borgbackup server roles.",
|
||||
"properties": {
|
||||
"machines": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(machine1|machine2|server1)$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"description": 'Machines to assign this role to. Format: each machine name is a key with an empty object {} as value. Example: {"wintux": {}, "gchq-local": {}}',
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(production|backup|client)$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"description": 'Tags to assign this role to. Format: each tag name is a key with an empty object {} as value. Example: {"all": {}, "nixos": {}}',
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"server": {
|
||||
"type": "object",
|
||||
"description": "A borgbackup server that stores the backups of clients.",
|
||||
"properties": {
|
||||
"machines": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(machine1|machine2|server1)$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"description": 'Machines to assign this role to. Format: each machine name is a key with an empty object {} as value. Example: {"wintux": {}, "gchq-local": {}}',
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(production|backup|client)$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"description": 'Tags to assign this role to. Format: each tag name is a key with an empty object {} as value. Example: {"all": {}, "nixos": {}}',
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
"required": ["roles"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"strict": True,
|
||||
}
|
||||
|
||||
assert generated_tool_func == expected_tool_func
|
||||
|
||||
certificate_service = next(
|
||||
m for m in service_modules.modules if m.usage_ref.get("name") == "certificates"
|
||||
)
|
||||
assert certificate_service is not None
|
||||
|
||||
generated_tool_func2 = clan_module_to_openai_spec(
|
||||
certificate_service, available_tags, available_machines
|
||||
)
|
||||
|
||||
expected_tool_func2: OpenAIFunctionSchema = {
|
||||
"type": "function",
|
||||
"name": "certificates",
|
||||
"description": "Sets up a PKI certificate chain using step-ca",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"module": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
},
|
||||
"roles": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ca": {
|
||||
"type": "object",
|
||||
"description": "A certificate authority that issues and signs certificates for other machines.",
|
||||
"properties": {
|
||||
"machines": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(machine1|machine2|server1)$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"description": 'Machines to assign this role to. Format: each machine name is a key with an empty object {} as value. Example: {"wintux": {}, "gchq-local": {}}',
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(production|backup|client)$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"description": 'Tags to assign this role to. Format: each tag name is a key with an empty object {} as value. Example: {"all": {}, "nixos": {}}',
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"default": {
|
||||
"type": "object",
|
||||
"description": "A machine that trusts the CA and can get certificates issued by it.",
|
||||
"properties": {
|
||||
"machines": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(machine1|machine2|server1)$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"description": 'Machines to assign this role to. Format: each machine name is a key with an empty object {} as value. Example: {"wintux": {}, "gchq-local": {}}',
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(production|backup|client)$": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"description": 'Tags to assign this role to. Format: each tag name is a key with an empty object {} as value. Example: {"all": {}, "nixos": {}}',
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
"required": ["roles"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"strict": True,
|
||||
}
|
||||
|
||||
assert generated_tool_func2 == expected_tool_func2
|
||||
|
||||
aggregate = aggregate_openai_function_schemas(flake)
|
||||
|
||||
assert len(aggregate.tools) >= 2
|
||||
|
||||
|
||||
def test_llm_final_decision_to_inventory_conversion() -> None:
|
||||
"""Test conversion of LLM final decision to inventory format."""
|
||||
final_decision: list[FunctionCallType] = [
|
||||
{
|
||||
"id": "toolu_01XHjHUMzZVTcDCqaYQJEWu5",
|
||||
"call_id": "toolu_01XHjHUMzZVTcDCqaYQJEWu5",
|
||||
"type": "function_call",
|
||||
"name": "matrix-synapse",
|
||||
"arguments": '{"roles": {"default": {"machines": {"gchq-local": {}}}}}',
|
||||
},
|
||||
{
|
||||
"id": "toolu_01TsjKZ87J3fi6RNzNzu33ff",
|
||||
"call_id": "toolu_01TsjKZ87J3fi6RNzNzu33ff",
|
||||
"type": "function_call",
|
||||
"name": "monitoring",
|
||||
"arguments": '{"module": { "input": "qubasas-clan" }, "roles": {"telegraf": {"tags": {"all": {}}}}}',
|
||||
},
|
||||
]
|
||||
assert isinstance(final_decision, list)
|
||||
|
||||
expected = [
|
||||
{
|
||||
"module": {
|
||||
"input": None,
|
||||
"name": "matrix-synapse",
|
||||
},
|
||||
"roles": {"default": {"machines": {"gchq-local": {}}}},
|
||||
},
|
||||
{
|
||||
"module": {
|
||||
"input": "qubasas-clan",
|
||||
"name": "monitoring",
|
||||
},
|
||||
"roles": {"telegraf": {"tags": {"all": {}}}},
|
||||
},
|
||||
]
|
||||
|
||||
result = llm_final_decision_to_inventory_instances(final_decision)
|
||||
assert result == expected
|
||||
1957
pkgs/clan-cli/clan_lib/llm/test_process_chat_turn.py
Normal file
1957
pkgs/clan-cli/clan_lib/llm/test_process_chat_turn.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user