Skip to content

tools

Class info

Classes

Name Children Inherits
ToolContext
llmling_agent.tools.base
Context for tool execution confirmation.
    ToolInfo
    llmling_agent.tools.base
    Information about a registered tool.
      ToolManager
      llmling_agent.tools.manager
      Manages tool registration, enabling/disabling and access.

        🛈 DocStrings

        Tool implementations and related classes / functions.

        ToolContext dataclass

        Context for tool execution confirmation.

        Source code in src/llmling_agent/tools/base.py
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        @dataclass(frozen=True)
        class ToolContext:
            """Context for tool execution confirmation."""
        
            name: str
            """Name of the tool being executed"""
        
            args: dict[str, Any]
            """Arguments being passed to the tool"""
        
            schema: py2openai.OpenAIFunctionTool
            """Complete OpenAI function schema"""
        
            runtime_ctx: AgentContext
            """Runtime context from agent"""
        
            @property
            def description(self) -> str | None:
                """Get tool description from schema."""
                return self.schema["function"].get("description")
        
            @property
            def parameters(self) -> ToolParameters:
                """Get parameter definitions from schema."""
                return self.schema["function"].get("parameters", {})  # type: ignore
        
            def __str__(self) -> str:
                """Format tool context for logging/display."""
                return (
                    f"Tool: {self.name}\n"
                    f"Arguments: {self.args}\n"
                    f"Description: {self.description or 'N/A'}"
                )
        

        args instance-attribute

        args: dict[str, Any]
        

        Arguments being passed to the tool

        description property

        description: str | None
        

        Get tool description from schema.

        name instance-attribute

        name: str
        

        Name of the tool being executed

        parameters property

        parameters: ToolParameters
        

        Get parameter definitions from schema.

        runtime_ctx instance-attribute

        runtime_ctx: AgentContext
        

        Runtime context from agent

        schema instance-attribute

        schema: OpenAIFunctionTool
        

        Complete OpenAI function schema

        __str__

        __str__() -> str
        

        Format tool context for logging/display.

        Source code in src/llmling_agent/tools/base.py
        56
        57
        58
        59
        60
        61
        62
        def __str__(self) -> str:
            """Format tool context for logging/display."""
            return (
                f"Tool: {self.name}\n"
                f"Arguments: {self.args}\n"
                f"Description: {self.description or 'N/A'}"
            )
        

        ToolInfo dataclass

        Information about a registered tool.

        Source code in src/llmling_agent/tools/base.py
         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
        @dataclass
        class ToolInfo:
            """Information about a registered tool."""
        
            callable: LLMCallableTool
            """The actual tool implementation"""
        
            enabled: bool = True
            """Whether the tool is currently enabled"""
        
            source: ToolSource = "runtime"
            """Where the tool came from."""
        
            priority: int = 100
            """Priority for tool execution (lower = higher priority)"""
        
            requires_confirmation: bool = False
            """Whether tool execution needs explicit confirmation"""
        
            requires_capability: str | None = None
            """Optional capability required to use this tool"""
        
            agent_name: str | None = None
            """The agent name as an identifier for agent-as-a-tool."""
        
            metadata: dict[str, str] = field(default_factory=dict)
            """Additional tool metadata"""
        
            cache_enabled: bool = False
            """Whether to enable caching for this tool."""
        
            @property
            def schema(self) -> py2openai.OpenAIFunctionTool:
                """Get the OpenAI function schema for the tool."""
                return self.callable.get_schema()
        
            @property
            def name(self) -> str:
                """Get tool name."""
                return self.callable.name
        
            @property
            def description(self) -> str | None:
                """Get tool description."""
                return self.callable.description
        
            def matches_filter(self, state: Literal["all", "enabled", "disabled"]) -> bool:
                """Check if tool matches state filter."""
                match state:
                    case "all":
                        return True
                    case "enabled":
                        return self.enabled
                    case "disabled":
                        return not self.enabled
        
            @property
            def parameters(self) -> list[ToolParameter]:
                """Get information about tool parameters."""
                schema = self.schema["function"]
                properties: dict[str, Property] = schema.get("properties", {})  # type: ignore
                required: list[str] = schema.get("required", [])  # type: ignore
        
                return [
                    ToolParameter(
                        name=name,
                        required=name in required,
                        type_info=details.get("type"),
                        description=details.get("description"),
                    )
                    for name, details in properties.items()
                ]
        
            def format_info(self, indent: str = "  ") -> str:
                """Format complete tool information."""
                lines = [f"{indent}{self.name}"]
                if self.description:
                    lines.append(f"{indent}  {self.description}")
                if self.parameters:
                    lines.append(f"{indent}  Parameters:")
                    lines.extend(f"{indent}    {param}" for param in self.parameters)
                if self.metadata:
                    lines.append(f"{indent}  Metadata:")
                    lines.extend(f"{indent}    {k}: {v}" for k, v in self.metadata.items())
                return "\n".join(lines)
        
            async def execute(self, *args: Any, **kwargs: Any) -> Any:
                """Execute tool, handling both sync and async cases."""
                fn = track_tool(self.name)(self.callable.callable)
                return await execute(fn, *args, **kwargs, use_thread=True)
        
            @classmethod
            def from_code(
                cls,
                code: str,
                name: str | None = None,
                description: str | None = None,
            ) -> Self:
                """Create a tool from a code string."""
                namespace: dict[str, Any] = {}
                exec(code, namespace)
                func = next((v for v in namespace.values() if callable(v)), None)
                if not func:
                    msg = "No callable found in provided code"
                    raise ValueError(msg)
                return cls.from_callable(
                    func, name_override=name, description_override=description
                )
        
            @classmethod
            def from_callable(
                cls,
                fn: Callable[..., Any] | str,
                *,
                name_override: str | None = None,
                description_override: str | None = None,
                schema_override: py2openai.OpenAIFunctionDefinition | None = None,
                **kwargs: Any,
            ) -> Self:
                tool = LLMCallableTool.from_callable(
                    fn,
                    name_override=name_override,
                    description_override=description_override,
                    schema_override=schema_override,
                )
                return cls(tool, **kwargs)
        
            @classmethod
            def from_crewai_tool(
                cls,
                tool: Any,
                *,
                name_override: str | None = None,
                description_override: str | None = None,
                schema_override: py2openai.OpenAIFunctionDefinition | None = None,
                **kwargs: Any,
            ) -> Self:
                """Allows importing crewai tools."""
                # vaidate_import("crewai_tools", "crewai")
                try:
                    from crewai.tools import BaseTool as CrewAiBaseTool
                except ImportError as e:
                    msg = "crewai package not found. Please install it with 'pip install crewai'"
                    raise ImportError(msg) from e
        
                if not isinstance(tool, CrewAiBaseTool):
                    msg = f"Expected CrewAI BaseTool, got {type(tool)}"
                    raise TypeError(msg)
        
                return cls.from_callable(
                    tool._run,
                    name_override=name_override or tool.__class__.__name__.removesuffix("Tool"),
                    description_override=description_override or tool.description,
                    schema_override=schema_override,
                    **kwargs,
                )
        
            @classmethod
            def from_langchain_tool(
                cls,
                tool: Any,
                *,
                name_override: str | None = None,
                description_override: str | None = None,
                schema_override: py2openai.OpenAIFunctionDefinition | None = None,
                **kwargs: Any,
            ) -> Self:
                """Create a tool from a LangChain tool."""
                # vaidate_import("langchain_core", "langchain")
                try:
                    from langchain_core.tools import BaseTool as LangChainBaseTool
                except ImportError as e:
                    msg = "langchain-core package not found."
                    raise ImportError(msg) from e
        
                if not isinstance(tool, LangChainBaseTool):
                    msg = f"Expected LangChain BaseTool, got {type(tool)}"
                    raise TypeError(msg)
        
                return cls.from_callable(
                    tool.invoke,
                    name_override=name_override or tool.name,
                    description_override=description_override or tool.description,
                    schema_override=schema_override,
                    **kwargs,
                )
        
            @classmethod
            def from_autogen_tool(
                cls,
                tool: Any,
                *,
                name_override: str | None = None,
                description_override: str | None = None,
                schema_override: py2openai.OpenAIFunctionDefinition | None = None,
                **kwargs: Any,
            ) -> Self:
                """Create a tool from a AutoGen tool."""
                # vaidate_import("autogen_core", "autogen")
                try:
                    from autogen_core import CancellationToken
                    from autogen_core.tools import BaseTool
                except ImportError as e:
                    msg = "autogent_core package not found."
                    raise ImportError(msg) from e
        
                if not isinstance(tool, BaseTool):
                    msg = f"Expected AutoGent BaseTool, got {type(tool)}"
                    raise TypeError(msg)
                token = CancellationToken()
        
                input_model = tool.__class__.__orig_bases__[0].__args__[0]  # type: ignore
        
                name = name_override or tool.name or tool.__class__.__name__.removesuffix("Tool")
                description = (
                    description_override
                    or tool.description
                    or inspect.getdoc(tool.__class__)
                    or ""
                )
        
                async def wrapper(**kwargs: Any) -> Any:
                    # Convert kwargs to the expected input model
                    model = input_model(**kwargs)
                    return await tool.run(model, cancellation_token=token)
        
                return cls.from_callable(
                    wrapper,  # type: ignore
                    name_override=name,
                    description_override=description,
                    schema_override=schema_override,
                    **kwargs,
                )
        

        agent_name class-attribute instance-attribute

        agent_name: str | None = None
        

        The agent name as an identifier for agent-as-a-tool.

        cache_enabled class-attribute instance-attribute

        cache_enabled: bool = False
        

        Whether to enable caching for this tool.

        callable instance-attribute

        callable: LLMCallableTool
        

        The actual tool implementation

        description property

        description: str | None
        

        Get tool description.

        enabled class-attribute instance-attribute

        enabled: bool = True
        

        Whether the tool is currently enabled

        metadata class-attribute instance-attribute

        metadata: dict[str, str] = field(default_factory=dict)
        

        Additional tool metadata

        name property

        name: str
        

        Get tool name.

        parameters property

        parameters: list[ToolParameter]
        

        Get information about tool parameters.

        priority class-attribute instance-attribute

        priority: int = 100
        

        Priority for tool execution (lower = higher priority)

        requires_capability class-attribute instance-attribute

        requires_capability: str | None = None
        

        Optional capability required to use this tool

        requires_confirmation class-attribute instance-attribute

        requires_confirmation: bool = False
        

        Whether tool execution needs explicit confirmation

        schema property

        schema: OpenAIFunctionTool
        

        Get the OpenAI function schema for the tool.

        source class-attribute instance-attribute

        source: ToolSource = 'runtime'
        

        Where the tool came from.

        execute async

        execute(*args: Any, **kwargs: Any) -> Any
        

        Execute tool, handling both sync and async cases.

        Source code in src/llmling_agent/tools/base.py
        151
        152
        153
        154
        async def execute(self, *args: Any, **kwargs: Any) -> Any:
            """Execute tool, handling both sync and async cases."""
            fn = track_tool(self.name)(self.callable.callable)
            return await execute(fn, *args, **kwargs, use_thread=True)
        

        format_info

        format_info(indent: str = '  ') -> str
        

        Format complete tool information.

        Source code in src/llmling_agent/tools/base.py
        138
        139
        140
        141
        142
        143
        144
        145
        146
        147
        148
        149
        def format_info(self, indent: str = "  ") -> str:
            """Format complete tool information."""
            lines = [f"{indent}{self.name}"]
            if self.description:
                lines.append(f"{indent}  {self.description}")
            if self.parameters:
                lines.append(f"{indent}  Parameters:")
                lines.extend(f"{indent}    {param}" for param in self.parameters)
            if self.metadata:
                lines.append(f"{indent}  Metadata:")
                lines.extend(f"{indent}    {k}: {v}" for k, v in self.metadata.items())
            return "\n".join(lines)
        

        from_autogen_tool classmethod

        from_autogen_tool(
            tool: Any,
            *,
            name_override: str | None = None,
            description_override: str | None = None,
            schema_override: OpenAIFunctionDefinition | None = None,
            **kwargs: Any,
        ) -> Self
        

        Create a tool from a AutoGen tool.

        Source code in src/llmling_agent/tools/base.py
        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
        @classmethod
        def from_autogen_tool(
            cls,
            tool: Any,
            *,
            name_override: str | None = None,
            description_override: str | None = None,
            schema_override: py2openai.OpenAIFunctionDefinition | None = None,
            **kwargs: Any,
        ) -> Self:
            """Create a tool from a AutoGen tool."""
            # vaidate_import("autogen_core", "autogen")
            try:
                from autogen_core import CancellationToken
                from autogen_core.tools import BaseTool
            except ImportError as e:
                msg = "autogent_core package not found."
                raise ImportError(msg) from e
        
            if not isinstance(tool, BaseTool):
                msg = f"Expected AutoGent BaseTool, got {type(tool)}"
                raise TypeError(msg)
            token = CancellationToken()
        
            input_model = tool.__class__.__orig_bases__[0].__args__[0]  # type: ignore
        
            name = name_override or tool.name or tool.__class__.__name__.removesuffix("Tool")
            description = (
                description_override
                or tool.description
                or inspect.getdoc(tool.__class__)
                or ""
            )
        
            async def wrapper(**kwargs: Any) -> Any:
                # Convert kwargs to the expected input model
                model = input_model(**kwargs)
                return await tool.run(model, cancellation_token=token)
        
            return cls.from_callable(
                wrapper,  # type: ignore
                name_override=name,
                description_override=description,
                schema_override=schema_override,
                **kwargs,
            )
        

        from_code classmethod

        from_code(code: str, name: str | None = None, description: str | None = None) -> Self
        

        Create a tool from a code string.

        Source code in src/llmling_agent/tools/base.py
        156
        157
        158
        159
        160
        161
        162
        163
        164
        165
        166
        167
        168
        169
        170
        171
        172
        @classmethod
        def from_code(
            cls,
            code: str,
            name: str | None = None,
            description: str | None = None,
        ) -> Self:
            """Create a tool from a code string."""
            namespace: dict[str, Any] = {}
            exec(code, namespace)
            func = next((v for v in namespace.values() if callable(v)), None)
            if not func:
                msg = "No callable found in provided code"
                raise ValueError(msg)
            return cls.from_callable(
                func, name_override=name, description_override=description
            )
        

        from_crewai_tool classmethod

        from_crewai_tool(
            tool: Any,
            *,
            name_override: str | None = None,
            description_override: str | None = None,
            schema_override: OpenAIFunctionDefinition | None = None,
            **kwargs: Any,
        ) -> Self
        

        Allows importing crewai tools.

        Source code in src/llmling_agent/tools/base.py
        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
        @classmethod
        def from_crewai_tool(
            cls,
            tool: Any,
            *,
            name_override: str | None = None,
            description_override: str | None = None,
            schema_override: py2openai.OpenAIFunctionDefinition | None = None,
            **kwargs: Any,
        ) -> Self:
            """Allows importing crewai tools."""
            # vaidate_import("crewai_tools", "crewai")
            try:
                from crewai.tools import BaseTool as CrewAiBaseTool
            except ImportError as e:
                msg = "crewai package not found. Please install it with 'pip install crewai'"
                raise ImportError(msg) from e
        
            if not isinstance(tool, CrewAiBaseTool):
                msg = f"Expected CrewAI BaseTool, got {type(tool)}"
                raise TypeError(msg)
        
            return cls.from_callable(
                tool._run,
                name_override=name_override or tool.__class__.__name__.removesuffix("Tool"),
                description_override=description_override or tool.description,
                schema_override=schema_override,
                **kwargs,
            )
        

        from_langchain_tool classmethod

        from_langchain_tool(
            tool: Any,
            *,
            name_override: str | None = None,
            description_override: str | None = None,
            schema_override: OpenAIFunctionDefinition | None = None,
            **kwargs: Any,
        ) -> Self
        

        Create a tool from a LangChain tool.

        Source code in src/llmling_agent/tools/base.py
        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
        @classmethod
        def from_langchain_tool(
            cls,
            tool: Any,
            *,
            name_override: str | None = None,
            description_override: str | None = None,
            schema_override: py2openai.OpenAIFunctionDefinition | None = None,
            **kwargs: Any,
        ) -> Self:
            """Create a tool from a LangChain tool."""
            # vaidate_import("langchain_core", "langchain")
            try:
                from langchain_core.tools import BaseTool as LangChainBaseTool
            except ImportError as e:
                msg = "langchain-core package not found."
                raise ImportError(msg) from e
        
            if not isinstance(tool, LangChainBaseTool):
                msg = f"Expected LangChain BaseTool, got {type(tool)}"
                raise TypeError(msg)
        
            return cls.from_callable(
                tool.invoke,
                name_override=name_override or tool.name,
                description_override=description_override or tool.description,
                schema_override=schema_override,
                **kwargs,
            )
        

        matches_filter

        matches_filter(state: Literal['all', 'enabled', 'disabled']) -> bool
        

        Check if tool matches state filter.

        Source code in src/llmling_agent/tools/base.py
        111
        112
        113
        114
        115
        116
        117
        118
        119
        def matches_filter(self, state: Literal["all", "enabled", "disabled"]) -> bool:
            """Check if tool matches state filter."""
            match state:
                case "all":
                    return True
                case "enabled":
                    return self.enabled
                case "disabled":
                    return not self.enabled
        

        ToolManager

        Bases: BaseRegistry[str, ToolInfo]

        Manages tool registration, enabling/disabling and access.

        Inherits from BaseRegistry providing: - Dict-like access: manager["tool_name"] -> ToolInfo - Async startup/shutdown: await manager.startup() - Event observation: manager.add_observer(observer) - Registration: manager.register("tool_name", tool) - Listing: manager.list_items() - State check: manager.is_empty, manager.has_item() - Async iteration: async for name, tool in manager: ...

        Source code in src/llmling_agent/tools/manager.py
         39
         40
         41
         42
         43
         44
         45
         46
         47
         48
         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
        class ToolManager(BaseRegistry[str, ToolInfo]):
            """Manages tool registration, enabling/disabling and access.
        
            Inherits from BaseRegistry providing:
            - Dict-like access: manager["tool_name"] -> ToolInfo
            - Async startup/shutdown: await manager.startup()
            - Event observation: manager.add_observer(observer)
            - Registration: manager.register("tool_name", tool)
            - Listing: manager.list_items()
            - State check: manager.is_empty, manager.has_item()
            - Async iteration: async for name, tool in manager: ...
            """
        
            @dataclass(frozen=True)
            class ToolStateReset:
                """Emitted when tool states are reset."""
        
                previous_tools: dict[str, bool]
                new_tools: dict[str, bool]
                timestamp: datetime = field(default_factory=datetime.now)
        
            tool_states_reset = Signal(ToolStateReset)
        
            def __init__(
                self,
                tools: Sequence[ToolInfo | ToolType | dict[str, Any]] | None = None,
            ):
                """Initialize tool manager.
        
                Args:
                    tools: Initial tools to register
                """
                super().__init__()
                self.providers: list[ResourceProvider] = []
        
                # Register initial tools
                for tool in tools or []:
                    t = self._validate_item(tool)
                    self.register(t.name, t)
        
            def __prompt__(self) -> str:
                enabled_tools = [t.name for t in self.values() if t.enabled]
                if not enabled_tools:
                    return "No tools available"
                return f"Available tools: {', '.join(enabled_tools)}"
        
            def add_provider(
                self,
                provider: ResourceProvider | ResourceCallable,
                owner: str | None = None,
            ):
                """Add a resource provider or tool callable.
        
                Args:
                    provider: Either a ResourceProvider instance or a callable
                             returning tools. Callables are automatically wrapped.
                    owner: Optional owner for the provider
                """
                from llmling_agent.resource_providers.base import ResourceProvider
        
                if not isinstance(provider, ResourceProvider):
                    # Wrap old-style callable in ResourceProvider
                    prov: ResourceProvider = CallableResourceProvider(
                        name=provider.__name__,
                        tool_callable=provider,
                    )
                else:
                    prov = provider
                if owner:
                    prov.owner = owner
                self.providers.append(prov)
        
            def remove_provider(
                self, provider: ResourceProvider | ResourceCallable | ProviderName
            ):
                """Remove a resource provider."""
                from llmling_agent.resource_providers.base import ResourceProvider
        
                match provider:
                    case ResourceProvider():
                        self.providers.remove(provider)
                    case Callable():
                        # Find and remove wrapped callable
                        for p in self.providers:
                            if (
                                isinstance(p, CallableResourceProvider)
                                and p.tool_callable == provider
                            ):
                                self.providers.remove(p)
                    case str():
                        for p in self.providers:
                            if p.name == provider:
                                self.providers.remove(p)
                    case _:
                        msg = f"Invalid provider type: {type(provider)}"
                        raise ValueError(msg)
        
            def reset_states(self):
                """Reset all tools to their default enabled states."""
                for info in self.values():
                    info.enabled = True
        
            @property
            def _error_class(self) -> type[ToolError]:
                """Error class for tool operations."""
                return ToolError
        
            def _validate_item(self, item: ToolInfo | ToolType | dict[str, Any]) -> ToolInfo:  # noqa: PLR0911
                """Validate and convert items before registration."""
                match item:
                    case ToolInfo():
                        return item
                    case LLMCallableTool():
                        return ToolInfo(callable=item)
                    case str():
                        if item.startswith("crewai_tools"):
                            obj = import_class(item)()
                            return ToolInfo.from_crewai_tool(obj)
                        if item.startswith("langchain"):
                            obj = import_class(item)()
                            return ToolInfo.from_langchain_tool(obj)
                        return ToolInfo.from_callable(item)
                    case Callable():
                        return ToolInfo.from_callable(item)
                    case {"callable": callable_item, **config} if callable(
                        callable_item
                    ) or isinstance(callable_item, LLMCallableTool):
                        # First convert callable to LLMCallableTool if needed
                        tool = (
                            callable_item
                            if isinstance(callable_item, LLMCallableTool)
                            else LLMCallableTool.from_callable(callable_item)
                        )
        
                        # Get valid fields from ToolInfo dataclass (excluding 'callable')
                        valid_keys = {f.name for f in fields(ToolInfo)} - {"callable"}
                        tool_config = {k: v for k, v in config.items() if k in valid_keys}
        
                        return ToolInfo(callable=tool, **tool_config)  # type: ignore
        
                    case _:
                        typ = type(item)
                        msg = f"Item must be ToolInfo, LLMCallableTool, or callable. Got {typ}"
                        raise ToolError(msg)
        
            def enable_tool(self, tool_name: str):
                """Enable a previously disabled tool."""
                if tool_name not in self:
                    msg = f"Tool not found: {tool_name}"
                    raise ToolError(msg)
                tool_info = self[tool_name]
                tool_info.enabled = True
                self.events.changed(tool_name, tool_info)
                logger.debug("Enabled tool: %s", tool_name)
        
            def disable_tool(self, tool_name: str):
                """Disable a tool."""
                if tool_name not in self:
                    msg = f"Tool not found: {tool_name}"
                    raise ToolError(msg)
                tool_info = self[tool_name]
                tool_info.enabled = False
                self.events.changed(tool_name, tool_info)
                logger.debug("Disabled tool: %s", tool_name)
        
            def is_tool_enabled(self, tool_name: str) -> bool:
                """Check if a tool is currently enabled."""
                return self[tool_name].enabled if tool_name in self else False
        
            async def list_tools(self) -> dict[str, bool]:
                """Get a mapping of all tools and their enabled status."""
                return {tool.name: tool.enabled for tool in await self.get_tools()}
        
            async def get_tools(
                self,
                state: ToolState = "all",
            ) -> list[ToolInfo]:
                """Get tool objects based on filters."""
                tools: list[ToolInfo] = []
        
                # Get tools from registry
                tools.extend(t for t in self.values() if t.matches_filter(state))
        
                # Get tools from providers
                for provider in self.providers:
                    try:
                        provider_tools = await provider.get_tools()
                        tools.extend(t for t in provider_tools if t.matches_filter(state))
                    except Exception:
                        logger.exception("Failed to get tools from provider: %r", provider)
                        continue
                # Sort by priority if any have non-default priority
                if any(t.priority != 100 for t in tools):  # noqa: PLR2004
                    tools.sort(key=lambda t: t.priority)
        
                return tools
        
            async def get_tool_names(self, state: ToolState = "all") -> set[str]:
                """Get tool names based on state."""
                return {t.name for t in await self.get_tools() if t.matches_filter(state)}
        
            def register_tool(
                self,
                tool: ToolType | ToolInfo,
                *,
                name_override: str | None = None,
                description_override: str | None = None,
                enabled: bool = True,
                source: ToolSource = "runtime",
                priority: int = 100,
                requires_confirmation: bool = False,
                requires_capability: str | None = None,
                metadata: dict[str, str] | None = None,
            ) -> ToolInfo:
                """Register a new tool with custom settings.
        
                Args:
                    tool: Tool to register (callable, LLMCallableTool, or import path)
                    enabled: Whether tool is initially enabled
                    name_override: Optional name override for the tool
                    description_override: Optional description override for the tool
                    source: Tool source (runtime/agent/builtin/dynamic)
                    priority: Execution priority (lower = higher priority)
                    requires_confirmation: Whether tool needs confirmation
                    requires_capability: Optional capability needed to use tool
                    metadata: Additional tool metadata
        
                Returns:
                    Created ToolInfo instance
                """
                # First convert to basic ToolInfo
                match tool:
                    case LLMCallableTool():
                        llm_tool = tool
                        llm_tool.name = name_override or llm_tool.name
                        llm_tool.description = description_override or llm_tool.description
                    case ToolInfo():
                        llm_tool = tool.callable
                        llm_tool.description = description_override or llm_tool.description
                        llm_tool.name = name_override or llm_tool.name
                    case _:
                        llm_tool = LLMCallableTool.from_callable(
                            tool,
                            name_override=name_override,
                            description_override=description_override,
                        )
        
                if llm_tool.description and len(llm_tool.description) > MAX_LEN_DESCRIPTION:
                    msg = f"Too long description for {tool}"
                    raise ToolError(msg)
                tool_info = ToolInfo(
                    llm_tool,
                    enabled=enabled,
                    source=source,
                    priority=priority,
                    requires_confirmation=requires_confirmation,
                    requires_capability=requires_capability,
                    metadata=metadata or {},
                )
                # Register the tool
                self.register(tool_info.name, tool_info)
                return tool_info
        
            def register_worker(
                self,
                worker: AnyAgent[Any, Any],
                *,
                name: str | None = None,
                reset_history_on_run: bool = True,
                pass_message_history: bool = False,
                share_context: bool = False,
                parent: AnyAgent[Any, Any] | None = None,
            ) -> ToolInfo:
                """Register an agent as a worker tool.
        
                Args:
                    worker: Agent to register as worker
                    name: Optional name override for the worker tool
                    reset_history_on_run: Whether to clear history before each run
                    pass_message_history: Whether to pass parent's message history
                    share_context: Whether to pass parent's context/deps
                    parent: Optional parent agent for history/context sharing
                """
                tool = worker.to_tool(
                    parent=parent,
                    name=name,
                    reset_history_on_run=reset_history_on_run,
                    pass_message_history=pass_message_history,
                    share_context=share_context,
                )
                msg = "Registering worker %s as tool %s"
                logger.debug(msg, worker.name, tool.name)
                return self.register_tool(tool, source="agent", metadata={"agent": worker.name})
        
            def reset(self):
                """Reset tool states."""
                old_tools = {i.name: i.enabled for i in self._items.values()}
                self.reset_states()
                new_tools = {i.name: i.enabled for i in self._items.values()}
        
                event = self.ToolStateReset(old_tools, new_tools)
                self.tool_states_reset.emit(event)
        
            @contextmanager
            def temporary_tools(
                self,
                tools: ToolType | ToolInfo | Sequence[ToolType | ToolInfo],
                *,
                exclusive: bool = False,
            ) -> Iterator[list[ToolInfo]]:
                """Temporarily register tools.
        
                Args:
                    tools: Tool(s) to register
                    exclusive: Whether to temporarily disable all other tools
        
                Yields:
                    List of registered tool infos
        
                Example:
                    ```python
                    with tool_manager.temporary_tools([tool1, tool2], exclusive=True) as tools:
                        # Only tool1 and tool2 are available
                        await agent.run(prompt)
                    # Original tool states are restored
                    ```
                """
                # Normalize inputs to lists
                tools_list: list[ToolType | ToolInfo] = (
                    [tools] if not isinstance(tools, Sequence) else list(tools)
                )
        
                # Store original tool states if exclusive
                original_states: dict[str, bool] = {}
                if exclusive:
                    original_states = {name: tool.enabled for name, tool in self.items()}
                    # Disable all existing tools
                    for t in self.values():
                        t.enabled = False
        
                # Register all tools
                registered_tools: list[ToolInfo] = []
                try:
                    for tool in tools_list:
                        tool_info = self.register_tool(tool)
                        registered_tools.append(tool_info)
                    yield registered_tools
        
                finally:
                    # Remove temporary tools
                    for tool_info in registered_tools:
                        del self[tool_info.name]
        
                    # Restore original tool states if exclusive
                    if exclusive:
                        for name_, was_enabled in original_states.items():
                            if t := self.get(name_):
                                t.enabled = was_enabled
        
            def tool(
                self,
                name: str | None = None,
                *,
                description: str | None = None,
                enabled: bool = True,
                source: ToolSource = "runtime",
                priority: int = 100,
                requires_confirmation: bool = False,
                requires_capability: str | None = None,
                metadata: dict[str, str] | None = None,
            ) -> Callable[[AnyCallable], AnyCallable]:
                """Decorator to register a function as a tool.
        
                Args:
                    name: Optional override for tool name (defaults to function name)
                    description: Optional description override
                    enabled: Whether tool is initially enabled
                    source: Tool source type
                    priority: Execution priority (lower = higher)
                    requires_confirmation: Whether tool needs confirmation
                    requires_capability: Optional required capability
                    metadata: Additional tool metadata
        
                Returns:
                    Decorator function that registers the tool
        
                Example:
                    @tool_manager.register(
                        name="search_docs",
                        description="Search documentation",
                        requires_confirmation=True
                    )
                    async def search(query: str) -> str:
                        '''Search the docs.'''
                        return "Results..."
                """
        
                def decorator(func: AnyCallable) -> AnyCallable:
                    self.register_tool(
                        func,
                        name_override=name,
                        description_override=description,
                        enabled=enabled,
                        source=source,
                        priority=priority,
                        requires_confirmation=requires_confirmation,
                        requires_capability=requires_capability,
                        metadata=metadata,
                    )
                    return func
        
                return decorator
        

        _error_class property

        _error_class: type[ToolError]
        

        Error class for tool operations.

        ToolStateReset dataclass

        Emitted when tool states are reset.

        Source code in src/llmling_agent/tools/manager.py
        52
        53
        54
        55
        56
        57
        58
        @dataclass(frozen=True)
        class ToolStateReset:
            """Emitted when tool states are reset."""
        
            previous_tools: dict[str, bool]
            new_tools: dict[str, bool]
            timestamp: datetime = field(default_factory=datetime.now)
        

        __init__

        __init__(tools: Sequence[ToolInfo | ToolType | dict[str, Any]] | None = None)
        

        Initialize tool manager.

        Parameters:

        Name Type Description Default
        tools Sequence[ToolInfo | ToolType | dict[str, Any]] | None

        Initial tools to register

        None
        Source code in src/llmling_agent/tools/manager.py
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        def __init__(
            self,
            tools: Sequence[ToolInfo | ToolType | dict[str, Any]] | None = None,
        ):
            """Initialize tool manager.
        
            Args:
                tools: Initial tools to register
            """
            super().__init__()
            self.providers: list[ResourceProvider] = []
        
            # Register initial tools
            for tool in tools or []:
                t = self._validate_item(tool)
                self.register(t.name, t)
        

        _validate_item

        _validate_item(item: ToolInfo | ToolType | dict[str, Any]) -> ToolInfo
        

        Validate and convert items before registration.

        Source code in src/llmling_agent/tools/manager.py
        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
        def _validate_item(self, item: ToolInfo | ToolType | dict[str, Any]) -> ToolInfo:  # noqa: PLR0911
            """Validate and convert items before registration."""
            match item:
                case ToolInfo():
                    return item
                case LLMCallableTool():
                    return ToolInfo(callable=item)
                case str():
                    if item.startswith("crewai_tools"):
                        obj = import_class(item)()
                        return ToolInfo.from_crewai_tool(obj)
                    if item.startswith("langchain"):
                        obj = import_class(item)()
                        return ToolInfo.from_langchain_tool(obj)
                    return ToolInfo.from_callable(item)
                case Callable():
                    return ToolInfo.from_callable(item)
                case {"callable": callable_item, **config} if callable(
                    callable_item
                ) or isinstance(callable_item, LLMCallableTool):
                    # First convert callable to LLMCallableTool if needed
                    tool = (
                        callable_item
                        if isinstance(callable_item, LLMCallableTool)
                        else LLMCallableTool.from_callable(callable_item)
                    )
        
                    # Get valid fields from ToolInfo dataclass (excluding 'callable')
                    valid_keys = {f.name for f in fields(ToolInfo)} - {"callable"}
                    tool_config = {k: v for k, v in config.items() if k in valid_keys}
        
                    return ToolInfo(callable=tool, **tool_config)  # type: ignore
        
                case _:
                    typ = type(item)
                    msg = f"Item must be ToolInfo, LLMCallableTool, or callable. Got {typ}"
                    raise ToolError(msg)
        

        add_provider

        add_provider(provider: ResourceProvider | ResourceCallable, owner: str | None = None)
        

        Add a resource provider or tool callable.

        Parameters:

        Name Type Description Default
        provider ResourceProvider | ResourceCallable

        Either a ResourceProvider instance or a callable returning tools. Callables are automatically wrapped.

        required
        owner str | None

        Optional owner for the provider

        None
        Source code in src/llmling_agent/tools/manager.py
         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
        def add_provider(
            self,
            provider: ResourceProvider | ResourceCallable,
            owner: str | None = None,
        ):
            """Add a resource provider or tool callable.
        
            Args:
                provider: Either a ResourceProvider instance or a callable
                         returning tools. Callables are automatically wrapped.
                owner: Optional owner for the provider
            """
            from llmling_agent.resource_providers.base import ResourceProvider
        
            if not isinstance(provider, ResourceProvider):
                # Wrap old-style callable in ResourceProvider
                prov: ResourceProvider = CallableResourceProvider(
                    name=provider.__name__,
                    tool_callable=provider,
                )
            else:
                prov = provider
            if owner:
                prov.owner = owner
            self.providers.append(prov)
        

        disable_tool

        disable_tool(tool_name: str)
        

        Disable a tool.

        Source code in src/llmling_agent/tools/manager.py
        194
        195
        196
        197
        198
        199
        200
        201
        202
        def disable_tool(self, tool_name: str):
            """Disable a tool."""
            if tool_name not in self:
                msg = f"Tool not found: {tool_name}"
                raise ToolError(msg)
            tool_info = self[tool_name]
            tool_info.enabled = False
            self.events.changed(tool_name, tool_info)
            logger.debug("Disabled tool: %s", tool_name)
        

        enable_tool

        enable_tool(tool_name: str)
        

        Enable a previously disabled tool.

        Source code in src/llmling_agent/tools/manager.py
        184
        185
        186
        187
        188
        189
        190
        191
        192
        def enable_tool(self, tool_name: str):
            """Enable a previously disabled tool."""
            if tool_name not in self:
                msg = f"Tool not found: {tool_name}"
                raise ToolError(msg)
            tool_info = self[tool_name]
            tool_info.enabled = True
            self.events.changed(tool_name, tool_info)
            logger.debug("Enabled tool: %s", tool_name)
        

        get_tool_names async

        get_tool_names(state: ToolState = 'all') -> set[str]
        

        Get tool names based on state.

        Source code in src/llmling_agent/tools/manager.py
        236
        237
        238
        async def get_tool_names(self, state: ToolState = "all") -> set[str]:
            """Get tool names based on state."""
            return {t.name for t in await self.get_tools() if t.matches_filter(state)}
        

        get_tools async

        get_tools(state: ToolState = 'all') -> list[ToolInfo]
        

        Get tool objects based on filters.

        Source code in src/llmling_agent/tools/manager.py
        212
        213
        214
        215
        216
        217
        218
        219
        220
        221
        222
        223
        224
        225
        226
        227
        228
        229
        230
        231
        232
        233
        234
        async def get_tools(
            self,
            state: ToolState = "all",
        ) -> list[ToolInfo]:
            """Get tool objects based on filters."""
            tools: list[ToolInfo] = []
        
            # Get tools from registry
            tools.extend(t for t in self.values() if t.matches_filter(state))
        
            # Get tools from providers
            for provider in self.providers:
                try:
                    provider_tools = await provider.get_tools()
                    tools.extend(t for t in provider_tools if t.matches_filter(state))
                except Exception:
                    logger.exception("Failed to get tools from provider: %r", provider)
                    continue
            # Sort by priority if any have non-default priority
            if any(t.priority != 100 for t in tools):  # noqa: PLR2004
                tools.sort(key=lambda t: t.priority)
        
            return tools
        

        is_tool_enabled

        is_tool_enabled(tool_name: str) -> bool
        

        Check if a tool is currently enabled.

        Source code in src/llmling_agent/tools/manager.py
        204
        205
        206
        def is_tool_enabled(self, tool_name: str) -> bool:
            """Check if a tool is currently enabled."""
            return self[tool_name].enabled if tool_name in self else False
        

        list_tools async

        list_tools() -> dict[str, bool]
        

        Get a mapping of all tools and their enabled status.

        Source code in src/llmling_agent/tools/manager.py
        208
        209
        210
        async def list_tools(self) -> dict[str, bool]:
            """Get a mapping of all tools and their enabled status."""
            return {tool.name: tool.enabled for tool in await self.get_tools()}
        

        register_tool

        register_tool(
            tool: ToolType | ToolInfo,
            *,
            name_override: str | None = None,
            description_override: str | None = None,
            enabled: bool = True,
            source: ToolSource = "runtime",
            priority: int = 100,
            requires_confirmation: bool = False,
            requires_capability: str | None = None,
            metadata: dict[str, str] | None = None,
        ) -> ToolInfo
        

        Register a new tool with custom settings.

        Parameters:

        Name Type Description Default
        tool ToolType | ToolInfo

        Tool to register (callable, LLMCallableTool, or import path)

        required
        enabled bool

        Whether tool is initially enabled

        True
        name_override str | None

        Optional name override for the tool

        None
        description_override str | None

        Optional description override for the tool

        None
        source ToolSource

        Tool source (runtime/agent/builtin/dynamic)

        'runtime'
        priority int

        Execution priority (lower = higher priority)

        100
        requires_confirmation bool

        Whether tool needs confirmation

        False
        requires_capability str | None

        Optional capability needed to use tool

        None
        metadata dict[str, str] | None

        Additional tool metadata

        None

        Returns:

        Type Description
        ToolInfo

        Created ToolInfo instance

        Source code in src/llmling_agent/tools/manager.py
        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
        def register_tool(
            self,
            tool: ToolType | ToolInfo,
            *,
            name_override: str | None = None,
            description_override: str | None = None,
            enabled: bool = True,
            source: ToolSource = "runtime",
            priority: int = 100,
            requires_confirmation: bool = False,
            requires_capability: str | None = None,
            metadata: dict[str, str] | None = None,
        ) -> ToolInfo:
            """Register a new tool with custom settings.
        
            Args:
                tool: Tool to register (callable, LLMCallableTool, or import path)
                enabled: Whether tool is initially enabled
                name_override: Optional name override for the tool
                description_override: Optional description override for the tool
                source: Tool source (runtime/agent/builtin/dynamic)
                priority: Execution priority (lower = higher priority)
                requires_confirmation: Whether tool needs confirmation
                requires_capability: Optional capability needed to use tool
                metadata: Additional tool metadata
        
            Returns:
                Created ToolInfo instance
            """
            # First convert to basic ToolInfo
            match tool:
                case LLMCallableTool():
                    llm_tool = tool
                    llm_tool.name = name_override or llm_tool.name
                    llm_tool.description = description_override or llm_tool.description
                case ToolInfo():
                    llm_tool = tool.callable
                    llm_tool.description = description_override or llm_tool.description
                    llm_tool.name = name_override or llm_tool.name
                case _:
                    llm_tool = LLMCallableTool.from_callable(
                        tool,
                        name_override=name_override,
                        description_override=description_override,
                    )
        
            if llm_tool.description and len(llm_tool.description) > MAX_LEN_DESCRIPTION:
                msg = f"Too long description for {tool}"
                raise ToolError(msg)
            tool_info = ToolInfo(
                llm_tool,
                enabled=enabled,
                source=source,
                priority=priority,
                requires_confirmation=requires_confirmation,
                requires_capability=requires_capability,
                metadata=metadata or {},
            )
            # Register the tool
            self.register(tool_info.name, tool_info)
            return tool_info
        

        register_worker

        register_worker(
            worker: AnyAgent[Any, Any],
            *,
            name: str | None = None,
            reset_history_on_run: bool = True,
            pass_message_history: bool = False,
            share_context: bool = False,
            parent: AnyAgent[Any, Any] | None = None,
        ) -> ToolInfo
        

        Register an agent as a worker tool.

        Parameters:

        Name Type Description Default
        worker AnyAgent[Any, Any]

        Agent to register as worker

        required
        name str | None

        Optional name override for the worker tool

        None
        reset_history_on_run bool

        Whether to clear history before each run

        True
        pass_message_history bool

        Whether to pass parent's message history

        False
        share_context bool

        Whether to pass parent's context/deps

        False
        parent AnyAgent[Any, Any] | None

        Optional parent agent for history/context sharing

        None
        Source code in src/llmling_agent/tools/manager.py
        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
        def register_worker(
            self,
            worker: AnyAgent[Any, Any],
            *,
            name: str | None = None,
            reset_history_on_run: bool = True,
            pass_message_history: bool = False,
            share_context: bool = False,
            parent: AnyAgent[Any, Any] | None = None,
        ) -> ToolInfo:
            """Register an agent as a worker tool.
        
            Args:
                worker: Agent to register as worker
                name: Optional name override for the worker tool
                reset_history_on_run: Whether to clear history before each run
                pass_message_history: Whether to pass parent's message history
                share_context: Whether to pass parent's context/deps
                parent: Optional parent agent for history/context sharing
            """
            tool = worker.to_tool(
                parent=parent,
                name=name,
                reset_history_on_run=reset_history_on_run,
                pass_message_history=pass_message_history,
                share_context=share_context,
            )
            msg = "Registering worker %s as tool %s"
            logger.debug(msg, worker.name, tool.name)
            return self.register_tool(tool, source="agent", metadata={"agent": worker.name})
        

        remove_provider

        remove_provider(provider: ResourceProvider | ResourceCallable | ProviderName)
        

        Remove a resource provider.

        Source code in src/llmling_agent/tools/manager.py
        111
        112
        113
        114
        115
        116
        117
        118
        119
        120
        121
        122
        123
        124
        125
        126
        127
        128
        129
        130
        131
        132
        133
        134
        def remove_provider(
            self, provider: ResourceProvider | ResourceCallable | ProviderName
        ):
            """Remove a resource provider."""
            from llmling_agent.resource_providers.base import ResourceProvider
        
            match provider:
                case ResourceProvider():
                    self.providers.remove(provider)
                case Callable():
                    # Find and remove wrapped callable
                    for p in self.providers:
                        if (
                            isinstance(p, CallableResourceProvider)
                            and p.tool_callable == provider
                        ):
                            self.providers.remove(p)
                case str():
                    for p in self.providers:
                        if p.name == provider:
                            self.providers.remove(p)
                case _:
                    msg = f"Invalid provider type: {type(provider)}"
                    raise ValueError(msg)
        

        reset

        reset()
        

        Reset tool states.

        Source code in src/llmling_agent/tools/manager.py
        333
        334
        335
        336
        337
        338
        339
        340
        def reset(self):
            """Reset tool states."""
            old_tools = {i.name: i.enabled for i in self._items.values()}
            self.reset_states()
            new_tools = {i.name: i.enabled for i in self._items.values()}
        
            event = self.ToolStateReset(old_tools, new_tools)
            self.tool_states_reset.emit(event)
        

        reset_states

        reset_states()
        

        Reset all tools to their default enabled states.

        Source code in src/llmling_agent/tools/manager.py
        136
        137
        138
        139
        def reset_states(self):
            """Reset all tools to their default enabled states."""
            for info in self.values():
                info.enabled = True
        

        temporary_tools

        temporary_tools(
            tools: ToolType | ToolInfo | Sequence[ToolType | ToolInfo], *, exclusive: bool = False
        ) -> Iterator[list[ToolInfo]]
        

        Temporarily register tools.

        Parameters:

        Name Type Description Default
        tools ToolType | ToolInfo | Sequence[ToolType | ToolInfo]

        Tool(s) to register

        required
        exclusive bool

        Whether to temporarily disable all other tools

        False

        Yields:

        Type Description
        list[ToolInfo]

        List of registered tool infos

        Example
        with tool_manager.temporary_tools([tool1, tool2], exclusive=True) as tools:
            # Only tool1 and tool2 are available
            await agent.run(prompt)
        # Original tool states are restored
        
        Source code in src/llmling_agent/tools/manager.py
        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
        @contextmanager
        def temporary_tools(
            self,
            tools: ToolType | ToolInfo | Sequence[ToolType | ToolInfo],
            *,
            exclusive: bool = False,
        ) -> Iterator[list[ToolInfo]]:
            """Temporarily register tools.
        
            Args:
                tools: Tool(s) to register
                exclusive: Whether to temporarily disable all other tools
        
            Yields:
                List of registered tool infos
        
            Example:
                ```python
                with tool_manager.temporary_tools([tool1, tool2], exclusive=True) as tools:
                    # Only tool1 and tool2 are available
                    await agent.run(prompt)
                # Original tool states are restored
                ```
            """
            # Normalize inputs to lists
            tools_list: list[ToolType | ToolInfo] = (
                [tools] if not isinstance(tools, Sequence) else list(tools)
            )
        
            # Store original tool states if exclusive
            original_states: dict[str, bool] = {}
            if exclusive:
                original_states = {name: tool.enabled for name, tool in self.items()}
                # Disable all existing tools
                for t in self.values():
                    t.enabled = False
        
            # Register all tools
            registered_tools: list[ToolInfo] = []
            try:
                for tool in tools_list:
                    tool_info = self.register_tool(tool)
                    registered_tools.append(tool_info)
                yield registered_tools
        
            finally:
                # Remove temporary tools
                for tool_info in registered_tools:
                    del self[tool_info.name]
        
                # Restore original tool states if exclusive
                if exclusive:
                    for name_, was_enabled in original_states.items():
                        if t := self.get(name_):
                            t.enabled = was_enabled
        

        tool

        tool(
            name: str | None = None,
            *,
            description: str | None = None,
            enabled: bool = True,
            source: ToolSource = "runtime",
            priority: int = 100,
            requires_confirmation: bool = False,
            requires_capability: str | None = None,
            metadata: dict[str, str] | None = None,
        ) -> Callable[[AnyCallable], AnyCallable]
        

        Decorator to register a function as a tool.

        Parameters:

        Name Type Description Default
        name str | None

        Optional override for tool name (defaults to function name)

        None
        description str | None

        Optional description override

        None
        enabled bool

        Whether tool is initially enabled

        True
        source ToolSource

        Tool source type

        'runtime'
        priority int

        Execution priority (lower = higher)

        100
        requires_confirmation bool

        Whether tool needs confirmation

        False
        requires_capability str | None

        Optional required capability

        None
        metadata dict[str, str] | None

        Additional tool metadata

        None

        Returns:

        Type Description
        Callable[[AnyCallable], AnyCallable]

        Decorator function that registers the tool

        Example

        @tool_manager.register( name="search_docs", description="Search documentation", requires_confirmation=True ) async def search(query: str) -> str: '''Search the docs.''' return "Results..."

        Source code in src/llmling_agent/tools/manager.py
        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
        def tool(
            self,
            name: str | None = None,
            *,
            description: str | None = None,
            enabled: bool = True,
            source: ToolSource = "runtime",
            priority: int = 100,
            requires_confirmation: bool = False,
            requires_capability: str | None = None,
            metadata: dict[str, str] | None = None,
        ) -> Callable[[AnyCallable], AnyCallable]:
            """Decorator to register a function as a tool.
        
            Args:
                name: Optional override for tool name (defaults to function name)
                description: Optional description override
                enabled: Whether tool is initially enabled
                source: Tool source type
                priority: Execution priority (lower = higher)
                requires_confirmation: Whether tool needs confirmation
                requires_capability: Optional required capability
                metadata: Additional tool metadata
        
            Returns:
                Decorator function that registers the tool
        
            Example:
                @tool_manager.register(
                    name="search_docs",
                    description="Search documentation",
                    requires_confirmation=True
                )
                async def search(query: str) -> str:
                    '''Search the docs.'''
                    return "Results..."
            """
        
            def decorator(func: AnyCallable) -> AnyCallable:
                self.register_tool(
                    func,
                    name_override=name,
                    description_override=description,
                    enabled=enabled,
                    source=source,
                    priority=priority,
                    requires_confirmation=requires_confirmation,
                    requires_capability=requires_capability,
                    metadata=metadata,
                )
                return func
        
            return decorator