from __future__ import annotations
import ctypes
import ctypes.util
import os
import platform
import sys
from pathlib import Path
from typing import Optional
from .errors import AcbError, LibraryNotFoundError
ACB_OK: int = 0
ACB_ERR_IO: int = -1
ACB_ERR_INVALID: int = -2
ACB_ERR_NOT_FOUND: int = -3
ACB_ERR_OVERFLOW: int = -4
ACB_ERR_NULL_PTR: int = -5
_ERROR_MESSAGES: dict[int, str] = {
ACB_ERR_IO: "A filesystem I/O operation failed",
ACB_ERR_INVALID: "An invalid argument was provided",
ACB_ERR_NOT_FOUND: "The requested item was not found",
ACB_ERR_OVERFLOW: "The output buffer was too small",
ACB_ERR_NULL_PTR: "A required pointer argument was null",
}
def _lib_filename() -> str:
system = platform.system()
if system == "Darwin":
return "libagentic_codebase.dylib"
elif system == "Windows":
return "agentic_codebase.dll"
else:
return "libagentic_codebase.so"
def _find_library() -> str:
env_path = os.environ.get("AGENTIC_CODEBASE_LIB")
if env_path and os.path.isfile(env_path):
return env_path
lib_name = _lib_filename()
repo_root = Path(__file__).resolve().parent.parent.parent.parent
for profile in ("release", "debug"):
candidate = repo_root / "target" / profile / lib_name
if candidate.is_file():
return str(candidate)
found = ctypes.util.find_library("agentic_codebase")
if found:
return found
raise LibraryNotFoundError(
[str(repo_root / "target" / p / lib_name) for p in ("release", "debug")]
)
def _load_library() -> ctypes.CDLL:
lib = ctypes.CDLL(_find_library())
lib.acb_graph_open.argtypes = [ctypes.c_char_p]
lib.acb_graph_open.restype = ctypes.c_void_p
lib.acb_graph_free.argtypes = [ctypes.c_void_p]
lib.acb_graph_free.restype = None
lib.acb_graph_unit_count.argtypes = [ctypes.c_void_p]
lib.acb_graph_unit_count.restype = ctypes.c_uint64
lib.acb_graph_edge_count.argtypes = [ctypes.c_void_p]
lib.acb_graph_edge_count.restype = ctypes.c_uint64
lib.acb_graph_dimension.argtypes = [ctypes.c_void_p]
lib.acb_graph_dimension.restype = ctypes.c_uint32
lib.acb_graph_get_unit_name.argtypes = [
ctypes.c_void_p, ctypes.c_uint64, ctypes.c_char_p, ctypes.c_uint32, ]
lib.acb_graph_get_unit_name.restype = ctypes.c_int32
lib.acb_graph_get_unit_type.argtypes = [
ctypes.c_void_p, ctypes.c_uint64, ]
lib.acb_graph_get_unit_type.restype = ctypes.c_int32
lib.acb_graph_get_unit_file.argtypes = [
ctypes.c_void_p, ctypes.c_uint64, ctypes.c_char_p, ctypes.c_uint32, ]
lib.acb_graph_get_unit_file.restype = ctypes.c_int32
lib.acb_graph_get_unit_complexity.argtypes = [
ctypes.c_void_p, ctypes.c_uint64, ]
lib.acb_graph_get_unit_complexity.restype = ctypes.c_float
lib.acb_graph_get_unit_language.argtypes = [
ctypes.c_void_p, ctypes.c_uint64, ]
lib.acb_graph_get_unit_language.restype = ctypes.c_int32
lib.acb_graph_get_unit_stability.argtypes = [
ctypes.c_void_p, ctypes.c_uint64, ]
lib.acb_graph_get_unit_stability.restype = ctypes.c_float
lib.acb_graph_get_edges.argtypes = [
ctypes.c_void_p, ctypes.c_uint64, ctypes.POINTER(ctypes.c_uint64), ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_float), ctypes.c_uint32, ]
lib.acb_graph_get_edges.restype = ctypes.c_int32
return lib
_lib: Optional[ctypes.CDLL] = None
def _get_lib() -> ctypes.CDLL:
global _lib
if _lib is None:
_lib = _load_library()
return _lib
def _check(rc: int) -> None:
if rc < 0:
msg = _ERROR_MESSAGES.get(rc, f"Unknown FFI error code {rc}")
raise AcbError(msg, code=rc)