Class ClassConfig

java.lang.Object
com.tnsai.annotations.ClassConfig

public final class ClassConfig extends Object
The class configuration file an AgentSpec.config() or a RoleSpec.config() points at, read once per class and run; a host starts a run with useBaseDirectory(java.nio.file.Path).

See Config for the file. A role's knowledge and llm layer, field by field, as annotation < the roles section of its agent's file < the role's own file; bindRoles(java.lang.Class<?>, java.lang.String, java.util.Collection<java.lang.Class<?>>) records the agent's section, one per role class, an absent section counting as an empty one. This reader checks its shape and types; the meaning of each knowledge entry is checked where annotation entries are, by KnowledgeDeclarations, and the llm values by the runtime that resolves the LLM.

A host whose run file configures agents by type passes that file's Agents section to useBaseDirectory(RunFile), with the agent types of the run, and checks an edited section with checkAgents(RunFile) before saving it. Each Agents.<Type> takes the shape of an agent's file and layers over the class files: an agent's own file < its roles section < the role's own file < Agents.<Type> < Agents.<Type>.roles.<name>; levels(java.lang.Class<?>, java.lang.Class<?>) gives the chain.

Since:
0.18.0
  • Method Details

    • of

      public static ClassConfig of(Class<?> type)
      The configuration of a class, read on first use after the last useBaseDirectory(java.nio.file.Path) call.
      Parameters:
      type - an agent or role class
      Returns:
      its configuration; empty when the class has neither spec or its Config.path() is blank
      Throws:
      IllegalStateException - when the file is missing, does not parse or breaks the shape Config describes; the next call reads it again
    • baseDirectory

      public static Optional<Path> baseDirectory()
      The base directory the last useBaseDirectory(java.nio.file.Path) call set.
      Returns:
      the directory, absolute and normalized; empty when relative paths resolve against the working directory
      Since:
      0.18.6
    • useBaseDirectory

      public static void useBaseDirectory(Path base)
      Starts a new run against a base directory. From now on a relative Config.path(), and a relative FILE path a @Config file gives in a knowledge entry, resolve against base; null resolves them against the working directory again. A relative FILE Knowledge.path() written in an annotation, and one an entry the file overrides without giving path keeps, resolve against base too, when the source is read, as KnowledgePaths.resolve(java.lang.String) does. URL, blank and absolute paths are kept as written.

      Every call is a run boundary, the same directory included: the files are read again on next use, every roles section binding is dropped, the run file Agents section an earlier useBaseDirectory(RunFile) gave is dropped too, and then each ConfigReset provider drops what its module derived from the files. A ClassConfig.RoleBinding made before the call releases nothing after it. Call it between runs, with no agent initializing and no dispatch in flight: an agent initialized before the call keeps no roles section for the next run, so initialize the next run's agents after it, and a role's first build still in flight may keep what it read before the call. Retrieval already in flight completes on the corpus it started on.

      The providers are the ones the calling thread's context class loader sees, found before anything changes, so a failure to find them leaves the earlier run in place. Call it from a thread whose context class loader sees the modules in use: from one that does not, such as a worker thread given a narrower loader, those modules keep what they derived from the earlier run's files.

      Parameters:
      base - the directory, made absolute here; null for the working directory
      Throws:
      ServiceConfigurationError - when the providers cannot be found; nothing has changed then
      RuntimeException - the first failure of a ConfigReset provider, the others suppressed in it, after every provider ran
      LinkageError - the same, when the first failure is a linkage error, such as a provider missing a class it needs
    • useBaseDirectory

      public static void useBaseDirectory(ClassConfig.RunFile run)
      Starts a new run as useBaseDirectory(Path) does, with the Agents section of the run file the host read for it.

      agents is that section as JSON text: an object keyed by agent type, the binary name (com.example.AnswererAgent) or the simple name (AnswererAgent). Each value takes the fields of an agent's Config file, llm, knowledge and roles, checked as that file is, and a relative FILE path it gives resolves against the run file's base directory. For the run it starts, a type's section layers over the class files as levels(java.lang.Class<?>, java.lang.Class<?>) lists them. A role class takes one run file roles section per run, as it takes one from the agent files.

      The section is read once, here: an edit to the run file takes effect at the next call. A key selects agent types only: a role class, one with RoleSpec and no AgentSpec, takes no section of its own, and a key that gives its binary name fails, since a role takes its run settings under Agents.<Type>.roles.<name>. Two keys naming one class, and knowledge given to a class without AgentSpec, fail too.

      With agent types, every key resolves here, against those types: a simple-name key must name exactly one of them, and a key that names none fails, so a broken key leaves the earlier run in place. Without them, a key matches lazily, the first time a class is configured: any class that reaches this reader with that name takes the section, registered agent or not; a simple-name key selects one class per run, so a second class with that simple name fails where it is first configured, naming both; and a key that names no class is the host's to check, as is a simple-name key only a role class carries: a role class takes no section, so nothing reads that key.

      Parameters:
      run - the run file
      Throws:
      IllegalStateException - when the section does not parse, breaks the shape of an agent's file, or, with agent types, has a key that does not resolve as described; nothing has changed then
      ServiceConfigurationError - when the providers cannot be found; nothing has changed then
      RuntimeException - the first failure of a ConfigReset provider, the others suppressed in it, after every provider ran
      LinkageError - the same, when the first failure is a linkage error, such as a provider missing a class it needs
      Since:
      0.18.2
    • useBaseDirectory

      public static void useBaseDirectory(Path base, String source, String agents)
      Starts a new run as useBaseDirectory(RunFile) does with RunFile.of(base, source, agents), so its keys match lazily.
      Parameters:
      base - the directory, made absolute here; null for the working directory
      source - the run file, for messages
      agents - the Agents section as JSON text; null or blank for none
      Throws:
      IllegalStateException - when agents does not parse or breaks the shape of an agent's file; nothing has changed then
      ServiceConfigurationError - when the providers cannot be found; nothing has changed then
      RuntimeException - the first failure of a ConfigReset provider, the others suppressed in it, after every provider ran
      LinkageError - the same, when the first failure is a linkage error, such as a provider missing a class it needs
      Since:
      0.18.2
    • checkAgents

      public static void checkAgents(ClassConfig.RunFile run)
      Checks a run file's Agents section as useBaseDirectory(RunFile) would, without starting a run: no file is read, nothing a run holds changes and no ConfigReset provider runs. A host checks an edited section with it before saving. It throws on the first error, with the message the run would give. Knowledge sources and roles keys are checked where the run reads them: the source when it is loaded, a roles key when an agent binds the roles it plays.
      Parameters:
      run - the run file
      Throws:
      IllegalStateException - when the section does not parse, breaks the shape of an agent's file, or, with agent types, has a key that does not resolve
      Since:
      0.18.2
    • isPresent

      public boolean isPresent()
      Returns:
      true when the class names a configuration file
    • ofRun

      public static ClassConfig ofRun(Class<?> type)
      The run file Agents section the current run gives a class, matched as levels(java.lang.Class<?>, java.lang.Class<?>) matches it. Its level() is the type's level and roleNames() the roles it configures; a host that binds roles itself checks those names against the roles the agent plays, as bindRoles(java.lang.Class<?>, java.lang.String, java.util.Collection<java.lang.Class<?>>) does.
      Parameters:
      type - an agent class
      Returns:
      the section; absent when the run gives the class none
      Throws:
      IllegalStateException - when the section breaks the rules useBaseDirectory(RunFile) describes
      Since:
      0.18.2
    • source

      public String source()
      Returns:
      the file and the class, for messages; blank when absent
    • level

      public ClassConfig.Level level()
      Returns:
      the top level of the file: the agent's for an agent class, the role's own for a role class. A class that is both names its file in one of the two annotations; ownAgentLevel(java.lang.Class<?>) and ownRoleLevel(java.lang.Class<?>) tell which.
    • role

      public ClassConfig.Level role(String roleName)
      Parameters:
      roleName - a role's @RoleSpec name
      Returns:
      the roles.<roleName> level, or an empty level
    • roleNames

      public Set<String> roleNames()
      Returns:
      the role names under roles, in file order
    • requireKnownRoles

      public void requireKnownRoles(Collection<String> played)
      Fails when roles names a role the agent does not play.
      Parameters:
      played - the @RoleSpec names of the agent's roles
      Throws:
      IllegalStateException - naming the unknown roles
    • bindRoles

      public static ClassConfig.RoleBinding bindRoles(Class<?> agentClass, String agent, Collection<Class<?>> played)
      Binds the roles an agent plays to the roles sections of its file. Each role's name is its @RoleSpec name, as KnowledgeDeclarations.roleName(java.lang.Class<?>) derives it. Before anything is recorded, no two roles may share a name and every roles key must name a played role.

      Each role class takes one section per run, the process unless useBaseDirectory(java.nio.file.Path) starts runs. An agent whose file lacks roles.<name> for a role it plays, or that has no file, gives that role an empty section. So agents sharing a role class must all carry the same section, or all carry none; one with and one without fails whichever binds second, naming both. The binding is all or nothing: when one role fails, the roles this call already recorded are released. A role's run file roles section binds the same way, but only for the run: the next useBaseDirectory(java.nio.file.Path) releases it, while an agent file's section stays bound for the process when no host starts runs.

      Parameters:
      agentClass - the agent class whose file is read
      agent - the agent, for messages
      played - the classes of every role the agent plays
      Returns:
      the binding, to release when the agent fails to initialize
      Throws:
      IllegalStateException - when two roles share a name, a roles key of the agent's file or of its run file section names no played role, another agent class holds a different section for a role class, or the agent's file or run file section breaks the rules of(java.lang.Class<?>) and useBaseDirectory(RunFile) describe
    • bindRole

      public static ClassConfig.RoleBinding bindRole(Class<?> agentClass, Class<?> roleClass)
      Binds one role of an agent that cannot list the roles it plays, as bindRoles(java.lang.Class<?>, java.lang.String, java.util.Collection<java.lang.Class<?>>) does without checking the other roles keys.
      Parameters:
      agentClass - the agent class whose file is read
      roleClass - the role
      Returns:
      the binding
      Throws:
      IllegalStateException - when another agent class holds a different section for the role class, or the agent's file or run file section breaks the rules of(java.lang.Class<?>) and useBaseDirectory(RunFile) describe
    • bindOwnerless

      public static ClassConfig.RoleBinding bindOwnerless(Class<?> roleClass)
      Binds a role a host dispatches with no owner agent. The role takes an empty section, as from an agent with no file, so an agent class that gives it a section fails whichever binds second, and messages name this side "ownerless dispatch".
      Parameters:
      roleClass - the role
      Returns:
      the binding
      Throws:
      IllegalStateException - when an agent class holds a section for the role class
    • roleLevels

      public static List<ClassConfig.Level> roleLevels(Class<?> roleClass)
      The levels over a role's annotation, lowest first: the roles section a bound agent gave it, then the role's own file, then the roles section the run file gave it under that agent's type. Absent and empty levels are left out.
      Parameters:
      roleClass - a role class
      Returns:
      the levels, lowest first
    • declarationEpoch

      public static long declarationEpoch()
      A count that moves each time the first agent binds a role (bindRole(java.lang.Class<?>, java.lang.Class<?>), bindRoles(java.lang.Class<?>, java.lang.String, java.util.Collection<java.lang.Class<?>>)) or the last one releases it, when that agent's file or the run file gives the role a roles section, and each time useBaseDirectory(java.nio.file.Path) starts a run. It moves after the change it marks, so a levels(java.lang.Class<?>, java.lang.Class<?>) read that follows a read of the count sees every change the count has marked: a caller that keeps what it merged from the levels reads the count before merging and merges again once it moves. It moves for every role, so a change to one role also moves it for the others. Public only because the modules that read it are separate.
      Returns:
      the current count; it never decreases
      Since:
      0.18.7
    • levels

      public static List<ClassConfig.Level> levels(Class<?> agentClass, Class<?> roleClass)
      Every level that configures an agent playing a role, lowest first: the agent's own file, the roles section a bound agent gave the role, the role's own file, the agent type's section of the run file, and the roles section the run file gave the role. Absent and empty levels are left out. levels(null, role) equals roleLevels(role), and levels(agent, null) holds the agent's file and its run file section.
      Parameters:
      agentClass - an agent class, or null
      roleClass - a role class, or null
      Returns:
      the levels, lowest first
      Throws:
      IllegalStateException - when the agent's file or its run file section breaks the rules of(java.lang.Class<?>) and useBaseDirectory(RunFile) describe
      Since:
      0.18.2
    • ownRoleLevel

      public static Optional<ClassConfig.Level> ownRoleLevel(Class<?> roleClass)
      Parameters:
      roleClass - a role class
      Returns:
      the top level of the file its RoleSpec.config() names, or empty without one
    • ownAgentLevel

      public static Optional<ClassConfig.Level> ownAgentLevel(Class<?> agentClass)
      Parameters:
      agentClass - an agent class
      Returns:
      the top level of the file its AgentSpec.config() names, or empty without one; on a class that is also a role, a file its RoleSpec.config() names is the role's, not this
      Since:
      0.18.0