clan_lib: Add clan_module_to_llm_function for ai integration

This commit is contained in:
Qubasa
2025-10-03 12:32:33 +02:00
parent 4f00a22921
commit b56230e3a7
2 changed files with 374 additions and 0 deletions

View File

@@ -0,0 +1,144 @@
from typing import Any, Literal, TypedDict
from clan_lib.services.modules import Module
JSONSchemaType = Literal[
"array", "boolean", "integer", "null", "number", "object", "string"
]
JSONSchemaFormat = Literal[
# Dates and Times
"date-time",
"time",
"date",
"duration",
# Email Addresses
"email",
"idn-email",
# Hostnames
"hostname",
"idn-hostname",
# IP Addresses
"ipv4",
"ipv6",
# Resource Identifiers
"uuid",
"uri",
"uri-reference",
"iri",
"iri-reference",
# URI Template
"uri-template",
# JSON Pointer
"json-pointer",
"relative-json-pointer",
# Regular Expressions
"regex",
]
class JSONSchemaProperty(TypedDict, total=False):
type: JSONSchemaType | list[JSONSchemaType]
format: JSONSchemaFormat
description: str | None
enum: list[str] | None
items: dict[str, Any] | None
properties: dict[str, "JSONSchemaProperty"] | None
patternProperties: dict[str, "JSONSchemaProperty"] | None
required: list[str] | None
additionalProperties: bool | dict[str, Any] | None
class JSONSchemaParameters(TypedDict, total=False):
type: JSONSchemaType
properties: dict[str, JSONSchemaProperty]
required: list[str]
additionalProperties: bool
class LLMFunctionSchema(TypedDict):
type: Literal["function"]
name: str
description: str
parameters: JSONSchemaParameters
strict: bool
def clan_module_to_llm_function(
module: Module, available_tags: list[str], available_machines: list[str]
) -> LLMFunctionSchema:
# Create individual role schemas with descriptions
role_properties = {}
for role_name, role_info in module.info.roles.items():
role_properties[role_name] = JSONSchemaProperty(
type="object",
description=role_info.description,
properties={
"machines": JSONSchemaProperty(
type="object",
patternProperties={
f"^({'|'.join(available_machines)})$": JSONSchemaProperty(
type="object",
additionalProperties=False,
)
},
additionalProperties=False,
description="Machines for this role with empty configuration objects",
),
"tags": JSONSchemaProperty(
type="object",
patternProperties={
f"^({'|'.join(available_tags)})$": JSONSchemaProperty(
type="object",
additionalProperties=False,
)
},
additionalProperties=False,
description="Tags for this role with empty configuration objects",
),
},
additionalProperties=False,
)
module_name = module.usage_ref.get("name")
if not isinstance(module_name, str):
msg = "Module name must be a string"
raise TypeError(msg)
module_input = module.usage_ref.get("input")
if module_input is not None and not isinstance(module_input, str):
msg = "Module input must be a string or None"
raise TypeError(msg)
module_properties = {}
if module_input is not None:
module_properties["input"] = JSONSchemaProperty(
type="string",
description=(
"Source / Input name of the module, e.g. 'clan-core' or null for built-in modules"
),
enum=[module_input],
)
return LLMFunctionSchema(
type="function",
name=module.usage_ref["name"],
description=module.info.manifest.description,
parameters=JSONSchemaParameters(
type="object",
properties={
"module": JSONSchemaProperty(
type="object",
properties=module_properties,
),
"roles": JSONSchemaProperty(
type="object",
properties=role_properties,
additionalProperties=False,
),
},
required=["roles"],
additionalProperties=False,
),
strict=True,
)