---
title: "ToolCallingService"
manual: "TYPO3 LLM Extension"
version: "0.35"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:api-tool-calling-service@0.35"
source: "Api/ToolCallingService.rst"
modified: "2026-09-16T22:09:16+00:00"
---

# ToolCallingService

-   **class ToolCallingService**

    -   *Fully qualified name:* `\Netresearch\NrLlm\Service\Feature\ToolCallingService`

    Tool-calling chat completion. Depend on
    `ToolCallingServiceInterface` rather than this class (or the
    service manager) — the interface exposes exactly the tool-calling
    capability, so consumer test doubles stay two methods small
    ([ADR-051](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-051@0.35)).

    When no `beUserUid` is set on the options, the active backend user
    is resolved and populated automatically so per-user budget
    enforcement applies.

    -   **chatWithTools(array $messages, array $tools, ?ToolOptions $options = null) : CompletionResponse**

        Chat completion with tool calling. The provider is resolved from
        the options (or the extension's default); the configuration is a
        model-less transient one.

        -   *param array $messages:* list of `ChatMessage` (or legacy `{role, content}` arrays)
        -   *param array $tools:* list of `ToolSpec` (or legacy OpenAI-wire arrays)
        -   *param ?ToolOptions $options:* Tool choice, provider/model pin, budget fields

        *Returns:* CompletionResponse (`toolCalls` carries requested calls)

    -   **chatWithToolsForConfiguration(array $messages, array $tools, LlmConfiguration $configuration, ?ToolOptions $options = null) : CompletionResponse**

        Chat completion with tool calling against a specific LLM
        configuration — the adapter is resolved from the configuration's
        model (vault key, model, pricing), so budget and usage middleware
        record real cost. Prefer this entry point when a database-backed
        configuration exists.

        -   *param array $messages:* list of `ChatMessage` (or legacy `{role, content}` arrays)
        -   *param array $tools:* list of `ToolSpec` (or legacy OpenAI-wire arrays)
        -   *param LlmConfiguration $configuration:* The configuration to run on
        -   *param ?ToolOptions $options:* Tool choice and budget fields

        *Returns:* CompletionResponse (`toolCalls` carries requested calls)

## Usage

```php
use Netresearch\NrLlm\Domain\ValueObject\ChatMessage;
use Netresearch\NrLlm\Domain\ValueObject\ToolSpec;
use Netresearch\NrLlm\Service\Feature\ToolCallingServiceInterface;
use Netresearch\NrLlm\Service\Option\ToolOptions;

final readonly class WeatherAgent
{
    public function __construct(
        private ToolCallingServiceInterface $toolCalling,
    ) {}

    public function ask(string $question): string
    {
        $response = $this->toolCalling->chatWithTools(
            [ChatMessage::user($question)],
            [ToolSpec::function(
                'get_weather',
                'Get the current weather for a location.',
                [
                    'type' => 'object',
                    'properties' => ['location' => ['type' => 'string']],
                    'required' => ['location'],
                ],
            )],
            ToolOptions::auto(),
        );

        // Dispatch $response->toolCalls and continue the conversation —
        // see :ref:`tool-calling` for the full loop.
        return $response->content;
    }
}
```
