Skip to content

manifest

Class info

Classes

Name Children Inherits
AgentConfig
llmling_agent.models.agents
Configuration for a single agent in the system.
    • NodeConfig
    AgentWorkerConfig
    llmling_agent_config.workers
    Configuration for agent workers.
      • BaseWorkerConfig
      AgentsManifest
      llmling_agent.models.manifest
      Complete agent configuration manifest defining all available agents.
        BaseMCPServerConfig
        llmling_agent_config.mcp_server
        Base model for MCP server configuration.
        • StdioMCPServerConfig
        • SSEMCPServerConfig
        • StreamableHTTPMCPServerConfig
        BaseProviderConfig
        llmling_agent_config.providers
        Base configuration for agent providers.
        • PydanticAIProviderConfig
        • HumanProviderConfig
        • CallbackProviderConfig
        BaseWorkerConfig
        llmling_agent_config.workers
        Base configuration for workers.
        • TeamWorkerConfig
        • AgentWorkerConfig
        ConversionConfig
        llmling_agent_config.converters
        Global conversion configuration.
          Job
          llmling_agent_config.task
          A task is a piece of work that can be executed by an agent.
            MCPPoolServerConfig
            llmling_agent_config.pool_server
            Configuration for pool-based MCP server.
              ObservabilityConfig
              llmling_agent_config.observability
              Global observability configuration - supports single backend only.
                PromptLibraryConfig
                llmling_agent_config.system_prompts
                Complete prompt configuration.
                  ResourceRegistry
                  llmling_agent.resource_registry
                  Registry for filesystem resources.
                    SourceResourceConfig
                    llmling_agent_config.resources
                    Configuration for a single filesystem source.
                      • BaseResourceConfig
                      StorageConfig
                      llmling_agent_config.storage
                      Global storage configuration.
                        StructuredResponseConfig
                        llmling_agent_config.output_types
                        Base class for response definitions.
                          TeamConfig
                          llmling_agent_config.teams
                          Configuration for a team or chain of message nodes.
                            • NodeConfig
                            TeamWorkerConfig
                            llmling_agent_config.workers
                            Configuration for team workers.
                              • BaseWorkerConfig

                              🛈 DocStrings

                              Models for agent configuration.

                              AgentsManifest

                              Bases: Schema

                              Complete agent configuration manifest defining all available agents.

                              This is the root configuration that: - Defines available response types (both inline and imported) - Configures all agent instances and their settings - Sets up custom role definitions and capabilities - Manages environment configurations

                              A single manifest can define multiple agents that can work independently or collaborate through the orchestrator.

                              Source code in src/llmling_agent/models/manifest.py
                               49
                               50
                               51
                               52
                               53
                               54
                               55
                               56
                               57
                               58
                               59
                               60
                               61
                               62
                               63
                               64
                               65
                               66
                               67
                               68
                               69
                               70
                               71
                               72
                               73
                               74
                               75
                               76
                               77
                               78
                               79
                               80
                               81
                               82
                               83
                               84
                               85
                               86
                               87
                               88
                               89
                               90
                               91
                               92
                               93
                               94
                               95
                               96
                               97
                               98
                               99
                              100
                              101
                              102
                              103
                              104
                              105
                              106
                              107
                              108
                              109
                              110
                              111
                              112
                              113
                              114
                              115
                              116
                              117
                              118
                              119
                              120
                              121
                              122
                              123
                              124
                              125
                              126
                              127
                              128
                              129
                              130
                              131
                              132
                              133
                              134
                              135
                              136
                              137
                              138
                              139
                              140
                              141
                              142
                              143
                              144
                              145
                              146
                              147
                              148
                              149
                              150
                              151
                              152
                              153
                              154
                              155
                              156
                              157
                              158
                              159
                              160
                              161
                              162
                              163
                              164
                              165
                              166
                              167
                              168
                              169
                              170
                              171
                              172
                              173
                              174
                              175
                              176
                              177
                              178
                              179
                              180
                              181
                              182
                              183
                              184
                              185
                              186
                              187
                              188
                              189
                              190
                              191
                              192
                              193
                              194
                              195
                              196
                              197
                              198
                              199
                              200
                              201
                              202
                              203
                              204
                              205
                              206
                              207
                              208
                              209
                              210
                              211
                              212
                              213
                              214
                              215
                              216
                              217
                              218
                              219
                              220
                              221
                              222
                              223
                              224
                              225
                              226
                              227
                              228
                              229
                              230
                              231
                              232
                              233
                              234
                              235
                              236
                              237
                              238
                              239
                              240
                              241
                              242
                              243
                              244
                              245
                              246
                              247
                              248
                              249
                              250
                              251
                              252
                              253
                              254
                              255
                              256
                              257
                              258
                              259
                              260
                              261
                              262
                              263
                              264
                              265
                              266
                              267
                              268
                              269
                              270
                              271
                              272
                              273
                              274
                              275
                              276
                              277
                              278
                              279
                              280
                              281
                              282
                              283
                              284
                              285
                              286
                              287
                              288
                              289
                              290
                              291
                              292
                              293
                              294
                              295
                              296
                              297
                              298
                              299
                              300
                              301
                              302
                              303
                              304
                              305
                              306
                              307
                              308
                              309
                              310
                              311
                              312
                              313
                              314
                              315
                              316
                              317
                              318
                              319
                              320
                              321
                              322
                              323
                              324
                              325
                              326
                              327
                              328
                              329
                              330
                              331
                              332
                              333
                              334
                              335
                              336
                              337
                              338
                              339
                              340
                              341
                              342
                              343
                              344
                              345
                              346
                              347
                              348
                              349
                              350
                              351
                              352
                              353
                              354
                              355
                              356
                              357
                              358
                              359
                              360
                              361
                              362
                              363
                              364
                              365
                              366
                              367
                              368
                              369
                              370
                              371
                              372
                              373
                              374
                              375
                              376
                              377
                              378
                              379
                              380
                              381
                              382
                              383
                              384
                              385
                              386
                              387
                              388
                              389
                              390
                              391
                              392
                              393
                              394
                              395
                              396
                              397
                              398
                              399
                              400
                              401
                              402
                              403
                              404
                              405
                              406
                              407
                              408
                              409
                              410
                              411
                              412
                              413
                              414
                              415
                              416
                              417
                              418
                              419
                              420
                              421
                              422
                              423
                              424
                              425
                              426
                              427
                              428
                              429
                              430
                              431
                              432
                              433
                              434
                              435
                              436
                              437
                              438
                              439
                              440
                              441
                              442
                              443
                              444
                              445
                              446
                              447
                              448
                              449
                              450
                              451
                              452
                              453
                              454
                              455
                              456
                              457
                              458
                              459
                              460
                              461
                              462
                              463
                              464
                              465
                              466
                              467
                              468
                              469
                              470
                              471
                              472
                              473
                              474
                              475
                              476
                              477
                              478
                              479
                              480
                              481
                              class AgentsManifest(Schema):
                                  """Complete agent configuration manifest defining all available agents.
                              
                                  This is the root configuration that:
                                  - Defines available response types (both inline and imported)
                                  - Configures all agent instances and their settings
                                  - Sets up custom role definitions and capabilities
                                  - Manages environment configurations
                              
                                  A single manifest can define multiple agents that can work independently
                                  or collaborate through the orchestrator.
                                  """
                              
                                  INHERIT: str | list[str] | None = None
                                  """Inheritance references."""
                              
                                  resources: dict[str, ResourceConfig | str] = Field(default_factory=dict)
                                  """Resource configurations defining available filesystems.
                              
                                  Supports both full config and URI shorthand:
                                      resources:
                                        docs: "file://./docs"  # shorthand
                                        data:  # full config
                                          type: "source"
                                          uri: "s3://bucket/data"
                                          cached: true
                                  """
                              
                                  agents: dict[str, AgentConfig] = Field(default_factory=dict)
                                  """Mapping of agent IDs to their configurations"""
                              
                                  teams: dict[str, TeamConfig] = Field(default_factory=dict)
                                  """Mapping of team IDs to their configurations"""
                              
                                  storage: StorageConfig = Field(default_factory=StorageConfig)
                                  """Storage provider configuration."""
                              
                                  observability: ObservabilityConfig = Field(default_factory=ObservabilityConfig)
                                  """Observability provider configuration."""
                              
                                  conversion: ConversionConfig = Field(default_factory=ConversionConfig)
                                  """Document conversion configuration."""
                              
                                  responses: dict[str, StructuredResponseConfig] = Field(default_factory=dict)
                                  """Mapping of response names to their definitions"""
                              
                                  jobs: dict[str, Job] = Field(default_factory=dict)
                                  """Pre-defined jobs, ready to be used by nodes."""
                              
                                  mcp_servers: list[str | MCPServerConfig] = Field(default_factory=list)
                                  """List of MCP server configurations:
                              
                                  These MCP servers are used to provide tools and other resources to the nodes.
                                  """
                                  pool_server: MCPPoolServerConfig = Field(default_factory=MCPPoolServerConfig)
                                  """Pool server configuration.
                              
                                  This MCP server configuration is used for the pool MCP server,
                                  which exposes pool functionality to other applications / clients."""
                              
                                  prompts: PromptLibraryConfig = Field(default_factory=PromptLibraryConfig)
                              
                                  model_config = ConfigDict(use_attribute_docstrings=True, extra="forbid")
                              
                                  @model_validator(mode="before")
                                  @classmethod
                                  def normalize_workers(cls, data: dict[str, Any]) -> dict[str, Any]:
                                      """Convert string workers to appropriate WorkerConfig for all agents."""
                                      teams = data.get("teams", {})
                                      agents = data.get("agents", {})
                              
                                      # Process workers for all agents that have them
                                      for agent_name, agent_config in agents.items():
                                          if isinstance(agent_config, dict):
                                              workers = agent_config.get("workers", [])
                                          else:
                                              workers = agent_config.workers
                              
                                          if workers:
                                              normalized: list[BaseWorkerConfig] = []
                              
                                              for worker in workers:
                                                  match worker:
                                                      case str() as name if name in teams:
                                                          # Determine type based on presence in teams/agents
                                                          normalized.append(TeamWorkerConfig(name=name))
                                                      case str() as name if name in agents:
                                                          normalized.append(AgentWorkerConfig(name=name))
                                                      case str():  # Default to agent if type can't be determined
                                                          normalized.append(AgentWorkerConfig(name=name))
                              
                                                      case dict() as config:
                                                          # If type is explicitly specified, use it
                                                          if worker_type := config.get("type"):
                                                              match worker_type:
                                                                  case "team":
                                                                      normalized.append(TeamWorkerConfig(**config))
                                                                  case "agent":
                                                                      normalized.append(AgentWorkerConfig(**config))
                                                                  case _:
                                                                      msg = f"Invalid worker type: {worker_type}"
                                                                      raise ValueError(msg)
                                                          else:
                                                              # Determine type based on worker name
                                                              worker_name = config.get("name")
                                                              if not worker_name:
                                                                  msg = "Worker config missing name"
                                                                  raise ValueError(msg)
                              
                                                              if worker_name in teams:
                                                                  normalized.append(TeamWorkerConfig(**config))
                                                              else:
                                                                  normalized.append(AgentWorkerConfig(**config))
                              
                                                      case BaseWorkerConfig():  # Already normalized
                                                          normalized.append(worker)
                              
                                                      case _:
                                                          msg = f"Invalid worker configuration: {worker}"
                                                          raise ValueError(msg)
                              
                                              if isinstance(agent_config, dict):
                                                  agent_config["workers"] = normalized
                                              else:
                                                  # Need to create a new dict with updated workers
                                                  agent_dict = agent_config.model_dump()
                                                  agent_dict["workers"] = normalized
                                                  agents[agent_name] = agent_dict
                              
                                      return data
                              
                                  @cached_property
                                  def resource_registry(self) -> ResourceRegistry:
                                      """Get registry with all configured resources."""
                                      registry = ResourceRegistry()
                                      for name, config in self.resources.items():
                                          if isinstance(config, str):
                                              # Convert URI shorthand to SourceResourceConfig
                                              config = SourceResourceConfig(uri=config)
                                          registry.register_from_config(name, config)
                                      return registry
                              
                                  def clone_agent_config(
                                      self,
                                      name: str,
                                      new_name: str | None = None,
                                      *,
                                      template_context: dict[str, Any] | None = None,
                                      **overrides: Any,
                                  ) -> str:
                                      """Create a copy of an agent configuration.
                              
                                      Args:
                                          name: Name of agent to clone
                                          new_name: Optional new name (auto-generated if None)
                                          template_context: Variables for template rendering
                                          **overrides: Configuration overrides for the clone
                              
                                      Returns:
                                          Name of the new agent
                              
                                      Raises:
                                          KeyError: If original agent not found
                                          ValueError: If new name already exists or if overrides invalid
                                      """
                                      if name not in self.agents:
                                          msg = f"Agent {name} not found"
                                          raise KeyError(msg)
                              
                                      actual_name = new_name or f"{name}_copy_{len(self.agents)}"
                                      if actual_name in self.agents:
                                          msg = f"Agent {actual_name} already exists"
                                          raise ValueError(msg)
                              
                                      # Deep copy the configuration
                                      config = self.agents[name].model_copy(deep=True)
                              
                                      # Apply overrides
                                      for key, value in overrides.items():
                                          if not hasattr(config, key):
                                              msg = f"Invalid override: {key}"
                                              raise ValueError(msg)
                                          setattr(config, key, value)
                              
                                      # Handle template rendering if context provided
                                      if template_context and "name" in template_context and "name" not in overrides:
                                          config.name = template_context["name"]
                              
                                      # Note: system_prompts will be rendered during agent creation, not here
                                      # config.system_prompts remains as PromptConfig objects
                              
                                      self.agents[actual_name] = config
                                      return actual_name
                              
                                  @model_validator(mode="before")
                                  @classmethod
                                  def resolve_inheritance(cls, data: dict) -> dict:
                                      """Resolve agent inheritance chains."""
                                      nodes = data.get("agents", {})
                                      resolved: dict[str, dict] = {}
                                      seen: set[str] = set()
                              
                                      def resolve_node(name: str) -> dict:
                                          if name in resolved:
                                              return resolved[name]
                              
                                          if name in seen:
                                              msg = f"Circular inheritance detected: {name}"
                                              raise ValueError(msg)
                              
                                          seen.add(name)
                                          config = (
                                              nodes[name].model_copy()
                                              if hasattr(nodes[name], "model_copy")
                                              else nodes[name].copy()
                                          )
                                          inherit = (
                                              config.get("inherits") if isinstance(config, dict) else config.inherits
                                          )
                                          if inherit:
                                              if inherit not in nodes:
                                                  msg = f"Parent agent {inherit} not found"
                                                  raise ValueError(msg)
                              
                                              # Get resolved parent config
                                              parent = resolve_node(inherit)
                                              # Merge parent with child (child overrides parent)
                                              merged = parent.copy()
                                              merged.update(config)
                                              config = merged
                              
                                          seen.remove(name)
                                          resolved[name] = config
                                          return config
                              
                                      # Resolve all nodes
                                      for name in nodes:
                                          resolved[name] = resolve_node(name)
                              
                                      # Update nodes with resolved configs
                                      data["agents"] = resolved
                                      return data
                              
                                  @property
                                  def node_names(self) -> list[str]:
                                      """Get list of all agent and team names."""
                                      return list(self.agents.keys()) + list(self.teams.keys())
                              
                                  @property
                                  def nodes(self) -> dict[str, Any]:
                                      """Get all agent and team configurations."""
                                      return {**self.agents, **self.teams}
                              
                                  def get_mcp_servers(self) -> list[MCPServerConfig]:
                                      """Get processed MCP server configurations.
                              
                                      Converts string entries to appropriate MCP server configs based on heuristics:
                                      - URLs ending with "/sse" -> SSE server
                                      - URLs starting with http(s):// -> HTTP server
                                      - Everything else -> stdio command
                              
                                      Returns:
                                          List of MCPServerConfig instances
                              
                                      Raises:
                                          ValueError: If string entry is empty
                                      """
                                      return [
                                          BaseMCPServerConfig.from_string(cfg) if isinstance(cfg, str) else cfg
                                          for cfg in self.mcp_servers
                                      ]
                              
                                  @cached_property
                                  def prompt_manager(self) -> PromptManager:
                                      """Get prompt manager for this manifest."""
                                      from llmling_agent.prompts.manager import PromptManager
                              
                                      return PromptManager(self.prompts)
                              
                                  # @model_validator(mode="after")
                                  # def validate_response_types(self) -> AgentsManifest:
                                  #     """Ensure all agent output_types exist in responses or are inline."""
                                  #     for agent_id, agent in self.agents.items():
                                  #         if (
                                  #             isinstance(agent.output_type, str)
                                  #             and agent.output_type not in self.responses
                                  #         ):
                                  #             msg = f"'{agent.output_type=}' for '{agent_id=}' not found in responses"
                                  #             raise ValueError(msg)
                                  #     return self
                              
                                  def get_agent[TAgentDeps](
                                      self, name: str, deps: TAgentDeps | None = None
                                  ) -> Agent[TAgentDeps, Any]:
                                      # TODO: Make this method async to support async function prompts
                                      from llmling import RuntimeConfig
                              
                                      from llmling_agent import Agent, AgentContext
                              
                                      config = self.agents[name]
                                      cfg = config.get_config()
                                      runtime = RuntimeConfig.from_config(cfg)  # Create runtime without async context
                                      # Create context with config path and capabilities
                                      context = AgentContext[TAgentDeps](
                                          node_name=name,
                                          data=deps,
                                          definition=self,
                                          config=config,
                                          runtime=runtime,
                                          # pool=self,
                                          # confirmation_callback=confirmation_callback,
                                      )
                              
                                      # Resolve system prompts with new PromptConfig types
                                      from llmling_agent_config.system_prompts import (
                                          FilePromptConfig,
                                          FunctionPromptConfig,
                                          LibraryPromptConfig,
                                          StaticPromptConfig,
                                      )
                              
                                      sys_prompts: list[str] = []
                                      for prompt in config.system_prompts:
                                          match prompt:
                                              case (str() as sys_prompt) | StaticPromptConfig(content=sys_prompt):
                                                  sys_prompts.append(sys_prompt)
                                              case FilePromptConfig(path=path, variables=variables):
                                                  template_path = Path(path)  # Load template from file
                                                  if not template_path.is_absolute() and config.config_file_path:
                                                      template_path = Path(config.config_file_path).parent / path
                              
                                                  template_content = template_path.read_text("utf-8")
                                                  if variables:  # Apply variables if any
                                                      from jinja2 import Template
                              
                                                      template = Template(template_content)
                                                      content = template.render(**variables)
                                                  else:
                                                      content = template_content
                                                  sys_prompts.append(content)
                                              case LibraryPromptConfig(reference=reference):
                                                  try:  # Load from library
                                                      content = self.prompt_manager.get.sync(reference)
                                                      sys_prompts.append(content)
                                                  except Exception as e:
                                                      msg = (
                                                          f"Failed to load library prompt {reference!r} "
                                                          f"for agent {name}"
                                                      )
                                                      logger.exception(msg)
                                                      raise ValueError(msg) from e
                                              case FunctionPromptConfig(function=function, arguments=arguments):
                                                  content = function(**arguments)  # Call function to get prompt content
                                                  sys_prompts.append(content)
                                      # Create agent with runtime and context
                                      return Agent(
                                          runtime=runtime,
                                          context=context,
                                          provider=config.get_provider(),
                                          system_prompt=sys_prompts,
                                          name=name,
                                          description=config.description,
                                          retries=config.retries,
                                          session=config.get_session_config(),
                                          output_retries=config.output_retries,
                                          end_strategy=config.end_strategy,
                                          debug=config.debug,
                                          output_type=self.get_output_type(name) or str,
                                          # name=config.name or name,
                                      )
                              
                                  def get_used_providers(self) -> set[str]:
                                      """Get all providers configured in this manifest."""
                                      providers = set[str]()
                              
                                      for agent_config in self.agents.values():
                                          match agent_config.provider:
                                              case ("pydantic_ai" as typ) | BaseProviderConfig(type=typ):
                                                  providers.add(typ)
                                      return providers
                              
                                  @classmethod
                                  def from_file(cls, path: JoinablePathLike) -> Self:
                                      """Load agent configuration from YAML file.
                              
                                      Args:
                                          path: Path to the configuration file
                              
                                      Returns:
                                          Loaded agent definition
                              
                                      Raises:
                                          ValueError: If loading fails
                                      """
                                      import yamling
                              
                                      try:
                                          data = yamling.load_yaml_file(path, resolve_inherit=True)
                                          agent_def = cls.model_validate(data)
                                          # Update all agents with the config file path and ensure names
                                          agents = {
                                              name: config.model_copy(update={"config_file_path": str(path)})
                                              for name, config in agent_def.agents.items()
                                          }
                                          return agent_def.model_copy(update={"agents": agents})
                                      except Exception as exc:
                                          msg = f"Failed to load agent config from {path}"
                                          raise ValueError(msg) from exc
                              
                                  @cached_property
                                  def pool(self) -> AgentPool:
                                      """Create an agent pool from this manifest.
                              
                                      Returns:
                                          Configured agent pool
                                      """
                                      from llmling_agent import AgentPool
                              
                                      return AgentPool(manifest=self)
                              
                                  def get_output_type(self, agent_name: str) -> type[Any] | None:
                                      """Get the resolved result type for an agent.
                              
                                      Returns None if no result type is configured.
                                      """
                                      agent_config = self.agents[agent_name]
                                      if not agent_config.output_type:
                                          return None
                                      logger.debug("Building response model for %r", agent_config.output_type)
                                      if isinstance(agent_config.output_type, str):
                                          response_def = self.responses[agent_config.output_type]
                                          return response_def.response_schema.get_schema()  # type: ignore
                                      return agent_config.output_type.response_schema.get_schema()  # type: ignore
                              

                              INHERIT class-attribute instance-attribute

                              INHERIT: str | list[str] | None = None
                              

                              Inheritance references.

                              agents class-attribute instance-attribute

                              agents: dict[str, AgentConfig] = Field(default_factory=dict)
                              

                              Mapping of agent IDs to their configurations

                              conversion class-attribute instance-attribute

                              conversion: ConversionConfig = Field(default_factory=ConversionConfig)
                              

                              Document conversion configuration.

                              jobs class-attribute instance-attribute

                              jobs: dict[str, Job] = Field(default_factory=dict)
                              

                              Pre-defined jobs, ready to be used by nodes.

                              mcp_servers class-attribute instance-attribute

                              mcp_servers: list[str | MCPServerConfig] = Field(default_factory=list)
                              

                              List of MCP server configurations:

                              These MCP servers are used to provide tools and other resources to the nodes.

                              node_names property

                              node_names: list[str]
                              

                              Get list of all agent and team names.

                              nodes property

                              nodes: dict[str, Any]
                              

                              Get all agent and team configurations.

                              observability class-attribute instance-attribute

                              observability: ObservabilityConfig = Field(default_factory=ObservabilityConfig)
                              

                              Observability provider configuration.

                              pool cached property

                              pool: AgentPool
                              

                              Create an agent pool from this manifest.

                              Returns:

                              Type Description
                              AgentPool

                              Configured agent pool

                              pool_server class-attribute instance-attribute

                              pool_server: MCPPoolServerConfig = Field(default_factory=MCPPoolServerConfig)
                              

                              Pool server configuration.

                              This MCP server configuration is used for the pool MCP server, which exposes pool functionality to other applications / clients.

                              prompt_manager cached property

                              prompt_manager: PromptManager
                              

                              Get prompt manager for this manifest.

                              resource_registry cached property

                              resource_registry: ResourceRegistry
                              

                              Get registry with all configured resources.

                              resources class-attribute instance-attribute

                              resources: dict[str, ResourceConfig | str] = Field(default_factory=dict)
                              

                              Resource configurations defining available filesystems.

                              Supports both full config and URI shorthand

                              resources: docs: "file://./docs" # shorthand data: # full config type: "source" uri: "s3://bucket/data" cached: true

                              responses class-attribute instance-attribute

                              responses: dict[str, StructuredResponseConfig] = Field(default_factory=dict)
                              

                              Mapping of response names to their definitions

                              storage class-attribute instance-attribute

                              storage: StorageConfig = Field(default_factory=StorageConfig)
                              

                              Storage provider configuration.

                              teams class-attribute instance-attribute

                              teams: dict[str, TeamConfig] = Field(default_factory=dict)
                              

                              Mapping of team IDs to their configurations

                              clone_agent_config

                              clone_agent_config(
                                  name: str,
                                  new_name: str | None = None,
                                  *,
                                  template_context: dict[str, Any] | None = None,
                                  **overrides: Any,
                              ) -> str
                              

                              Create a copy of an agent configuration.

                              Parameters:

                              Name Type Description Default
                              name str

                              Name of agent to clone

                              required
                              new_name str | None

                              Optional new name (auto-generated if None)

                              None
                              template_context dict[str, Any] | None

                              Variables for template rendering

                              None
                              **overrides Any

                              Configuration overrides for the clone

                              {}

                              Returns:

                              Type Description
                              str

                              Name of the new agent

                              Raises:

                              Type Description
                              KeyError

                              If original agent not found

                              ValueError

                              If new name already exists or if overrides invalid

                              Source code in src/llmling_agent/models/manifest.py
                              191
                              192
                              193
                              194
                              195
                              196
                              197
                              198
                              199
                              200
                              201
                              202
                              203
                              204
                              205
                              206
                              207
                              208
                              209
                              210
                              211
                              212
                              213
                              214
                              215
                              216
                              217
                              218
                              219
                              220
                              221
                              222
                              223
                              224
                              225
                              226
                              227
                              228
                              229
                              230
                              231
                              232
                              233
                              234
                              235
                              236
                              237
                              238
                              239
                              240
                              241
                              def clone_agent_config(
                                  self,
                                  name: str,
                                  new_name: str | None = None,
                                  *,
                                  template_context: dict[str, Any] | None = None,
                                  **overrides: Any,
                              ) -> str:
                                  """Create a copy of an agent configuration.
                              
                                  Args:
                                      name: Name of agent to clone
                                      new_name: Optional new name (auto-generated if None)
                                      template_context: Variables for template rendering
                                      **overrides: Configuration overrides for the clone
                              
                                  Returns:
                                      Name of the new agent
                              
                                  Raises:
                                      KeyError: If original agent not found
                                      ValueError: If new name already exists or if overrides invalid
                                  """
                                  if name not in self.agents:
                                      msg = f"Agent {name} not found"
                                      raise KeyError(msg)
                              
                                  actual_name = new_name or f"{name}_copy_{len(self.agents)}"
                                  if actual_name in self.agents:
                                      msg = f"Agent {actual_name} already exists"
                                      raise ValueError(msg)
                              
                                  # Deep copy the configuration
                                  config = self.agents[name].model_copy(deep=True)
                              
                                  # Apply overrides
                                  for key, value in overrides.items():
                                      if not hasattr(config, key):
                                          msg = f"Invalid override: {key}"
                                          raise ValueError(msg)
                                      setattr(config, key, value)
                              
                                  # Handle template rendering if context provided
                                  if template_context and "name" in template_context and "name" not in overrides:
                                      config.name = template_context["name"]
                              
                                  # Note: system_prompts will be rendered during agent creation, not here
                                  # config.system_prompts remains as PromptConfig objects
                              
                                  self.agents[actual_name] = config
                                  return actual_name
                              

                              from_file classmethod

                              from_file(path: JoinablePathLike) -> Self
                              

                              Load agent configuration from YAML file.

                              Parameters:

                              Name Type Description Default
                              path JoinablePathLike

                              Path to the configuration file

                              required

                              Returns:

                              Type Description
                              Self

                              Loaded agent definition

                              Raises:

                              Type Description
                              ValueError

                              If loading fails

                              Source code in src/llmling_agent/models/manifest.py
                              430
                              431
                              432
                              433
                              434
                              435
                              436
                              437
                              438
                              439
                              440
                              441
                              442
                              443
                              444
                              445
                              446
                              447
                              448
                              449
                              450
                              451
                              452
                              453
                              454
                              455
                              456
                              @classmethod
                              def from_file(cls, path: JoinablePathLike) -> Self:
                                  """Load agent configuration from YAML file.
                              
                                  Args:
                                      path: Path to the configuration file
                              
                                  Returns:
                                      Loaded agent definition
                              
                                  Raises:
                                      ValueError: If loading fails
                                  """
                                  import yamling
                              
                                  try:
                                      data = yamling.load_yaml_file(path, resolve_inherit=True)
                                      agent_def = cls.model_validate(data)
                                      # Update all agents with the config file path and ensure names
                                      agents = {
                                          name: config.model_copy(update={"config_file_path": str(path)})
                                          for name, config in agent_def.agents.items()
                                      }
                                      return agent_def.model_copy(update={"agents": agents})
                                  except Exception as exc:
                                      msg = f"Failed to load agent config from {path}"
                                      raise ValueError(msg) from exc
                              

                              get_mcp_servers

                              get_mcp_servers() -> list[MCPServerConfig]
                              

                              Get processed MCP server configurations.

                              Converts string entries to appropriate MCP server configs based on heuristics: - URLs ending with "/sse" -> SSE server - URLs starting with http(s):// -> HTTP server - Everything else -> stdio command

                              Returns:

                              Type Description
                              list[MCPServerConfig]

                              List of MCPServerConfig instances

                              Raises:

                              Type Description
                              ValueError

                              If string entry is empty

                              Source code in src/llmling_agent/models/manifest.py
                              302
                              303
                              304
                              305
                              306
                              307
                              308
                              309
                              310
                              311
                              312
                              313
                              314
                              315
                              316
                              317
                              318
                              319
                              def get_mcp_servers(self) -> list[MCPServerConfig]:
                                  """Get processed MCP server configurations.
                              
                                  Converts string entries to appropriate MCP server configs based on heuristics:
                                  - URLs ending with "/sse" -> SSE server
                                  - URLs starting with http(s):// -> HTTP server
                                  - Everything else -> stdio command
                              
                                  Returns:
                                      List of MCPServerConfig instances
                              
                                  Raises:
                                      ValueError: If string entry is empty
                                  """
                                  return [
                                      BaseMCPServerConfig.from_string(cfg) if isinstance(cfg, str) else cfg
                                      for cfg in self.mcp_servers
                                  ]
                              

                              get_output_type

                              get_output_type(agent_name: str) -> type[Any] | None
                              

                              Get the resolved result type for an agent.

                              Returns None if no result type is configured.

                              Source code in src/llmling_agent/models/manifest.py
                              469
                              470
                              471
                              472
                              473
                              474
                              475
                              476
                              477
                              478
                              479
                              480
                              481
                              def get_output_type(self, agent_name: str) -> type[Any] | None:
                                  """Get the resolved result type for an agent.
                              
                                  Returns None if no result type is configured.
                                  """
                                  agent_config = self.agents[agent_name]
                                  if not agent_config.output_type:
                                      return None
                                  logger.debug("Building response model for %r", agent_config.output_type)
                                  if isinstance(agent_config.output_type, str):
                                      response_def = self.responses[agent_config.output_type]
                                      return response_def.response_schema.get_schema()  # type: ignore
                                  return agent_config.output_type.response_schema.get_schema()  # type: ignore
                              

                              get_used_providers

                              get_used_providers() -> set[str]
                              

                              Get all providers configured in this manifest.

                              Source code in src/llmling_agent/models/manifest.py
                              420
                              421
                              422
                              423
                              424
                              425
                              426
                              427
                              428
                              def get_used_providers(self) -> set[str]:
                                  """Get all providers configured in this manifest."""
                                  providers = set[str]()
                              
                                  for agent_config in self.agents.values():
                                      match agent_config.provider:
                                          case ("pydantic_ai" as typ) | BaseProviderConfig(type=typ):
                                              providers.add(typ)
                                  return providers
                              

                              normalize_workers classmethod

                              normalize_workers(data: dict[str, Any]) -> dict[str, Any]
                              

                              Convert string workers to appropriate WorkerConfig for all agents.

                              Source code in src/llmling_agent/models/manifest.py
                              113
                              114
                              115
                              116
                              117
                              118
                              119
                              120
                              121
                              122
                              123
                              124
                              125
                              126
                              127
                              128
                              129
                              130
                              131
                              132
                              133
                              134
                              135
                              136
                              137
                              138
                              139
                              140
                              141
                              142
                              143
                              144
                              145
                              146
                              147
                              148
                              149
                              150
                              151
                              152
                              153
                              154
                              155
                              156
                              157
                              158
                              159
                              160
                              161
                              162
                              163
                              164
                              165
                              166
                              167
                              168
                              169
                              170
                              171
                              172
                              173
                              174
                              175
                              176
                              177
                              178
                              @model_validator(mode="before")
                              @classmethod
                              def normalize_workers(cls, data: dict[str, Any]) -> dict[str, Any]:
                                  """Convert string workers to appropriate WorkerConfig for all agents."""
                                  teams = data.get("teams", {})
                                  agents = data.get("agents", {})
                              
                                  # Process workers for all agents that have them
                                  for agent_name, agent_config in agents.items():
                                      if isinstance(agent_config, dict):
                                          workers = agent_config.get("workers", [])
                                      else:
                                          workers = agent_config.workers
                              
                                      if workers:
                                          normalized: list[BaseWorkerConfig] = []
                              
                                          for worker in workers:
                                              match worker:
                                                  case str() as name if name in teams:
                                                      # Determine type based on presence in teams/agents
                                                      normalized.append(TeamWorkerConfig(name=name))
                                                  case str() as name if name in agents:
                                                      normalized.append(AgentWorkerConfig(name=name))
                                                  case str():  # Default to agent if type can't be determined
                                                      normalized.append(AgentWorkerConfig(name=name))
                              
                                                  case dict() as config:
                                                      # If type is explicitly specified, use it
                                                      if worker_type := config.get("type"):
                                                          match worker_type:
                                                              case "team":
                                                                  normalized.append(TeamWorkerConfig(**config))
                                                              case "agent":
                                                                  normalized.append(AgentWorkerConfig(**config))
                                                              case _:
                                                                  msg = f"Invalid worker type: {worker_type}"
                                                                  raise ValueError(msg)
                                                      else:
                                                          # Determine type based on worker name
                                                          worker_name = config.get("name")
                                                          if not worker_name:
                                                              msg = "Worker config missing name"
                                                              raise ValueError(msg)
                              
                                                          if worker_name in teams:
                                                              normalized.append(TeamWorkerConfig(**config))
                                                          else:
                                                              normalized.append(AgentWorkerConfig(**config))
                              
                                                  case BaseWorkerConfig():  # Already normalized
                                                      normalized.append(worker)
                              
                                                  case _:
                                                      msg = f"Invalid worker configuration: {worker}"
                                                      raise ValueError(msg)
                              
                                          if isinstance(agent_config, dict):
                                              agent_config["workers"] = normalized
                                          else:
                                              # Need to create a new dict with updated workers
                                              agent_dict = agent_config.model_dump()
                                              agent_dict["workers"] = normalized
                                              agents[agent_name] = agent_dict
                              
                                  return data
                              

                              resolve_inheritance classmethod

                              resolve_inheritance(data: dict) -> dict
                              

                              Resolve agent inheritance chains.

                              Source code in src/llmling_agent/models/manifest.py
                              243
                              244
                              245
                              246
                              247
                              248
                              249
                              250
                              251
                              252
                              253
                              254
                              255
                              256
                              257
                              258
                              259
                              260
                              261
                              262
                              263
                              264
                              265
                              266
                              267
                              268
                              269
                              270
                              271
                              272
                              273
                              274
                              275
                              276
                              277
                              278
                              279
                              280
                              281
                              282
                              283
                              284
                              285
                              286
                              287
                              288
                              289
                              290
                              @model_validator(mode="before")
                              @classmethod
                              def resolve_inheritance(cls, data: dict) -> dict:
                                  """Resolve agent inheritance chains."""
                                  nodes = data.get("agents", {})
                                  resolved: dict[str, dict] = {}
                                  seen: set[str] = set()
                              
                                  def resolve_node(name: str) -> dict:
                                      if name in resolved:
                                          return resolved[name]
                              
                                      if name in seen:
                                          msg = f"Circular inheritance detected: {name}"
                                          raise ValueError(msg)
                              
                                      seen.add(name)
                                      config = (
                                          nodes[name].model_copy()
                                          if hasattr(nodes[name], "model_copy")
                                          else nodes[name].copy()
                                      )
                                      inherit = (
                                          config.get("inherits") if isinstance(config, dict) else config.inherits
                                      )
                                      if inherit:
                                          if inherit not in nodes:
                                              msg = f"Parent agent {inherit} not found"
                                              raise ValueError(msg)
                              
                                          # Get resolved parent config
                                          parent = resolve_node(inherit)
                                          # Merge parent with child (child overrides parent)
                                          merged = parent.copy()
                                          merged.update(config)
                                          config = merged
                              
                                      seen.remove(name)
                                      resolved[name] = config
                                      return config
                              
                                  # Resolve all nodes
                                  for name in nodes:
                                      resolved[name] = resolve_node(name)
                              
                                  # Update nodes with resolved configs
                                  data["agents"] = resolved
                                  return data