Skip to content

Segregate tool-calling into a capability interface; promote ChatOptions/ToolChoice to value objects #24

Description

@MarcelloDuarte

Follow-up parked from the v0.13 tool-choice work. The feature currently ships toolChoice as an option on ProviderInterface::chat(), validated by the internal PapiAI\Core\ToolChoice normaliser (which throws on unenforceable values, never silently drops), with the options shape defined once as a @psalm-type ChatOptions that providers import.

That is deliberately the option-bag design. The richer, ISP-clean design is deferred to here:

1. Segregate tool-calling (ISP)

ProviderInterface is a fat interface (chat + stream + supportsTool() + supportsVision() + supportsStructuredOutput()). Introduce a segregated ToolCallingProviderInterface carrying the tool concerns (tools, tool-choice semantics), mirroring how Embedding/Image/TextToSpeech/Video are already separate capability interfaces. Consumers type against it for a static guarantee instead of the runtime supportsTool() probe; the Agent switches to instanceof.

2. Capability query for graceful consumers

Add a single capability query (e.g. supportsToolChoice(): bool) on the segregated interface so consumers can degrade gracefully by choice, without catching. Not added to the base contract now because a supports*() method there forces an all-providers-at-once change; it belongs on the segregated interface.

3. First-class value objects

Promote the options bag and tool-choice from a Psalm type / internal validator to public value objects: a ChatOptions object and a ToolChoice value object with public factories (ToolChoice::auto(), ::none(), ::required(), ::tool('name')), so callers build a validated, typed choice instead of a string|array.

Constraints

  • Breaking change to the base contract; touches every chat provider. Do it backward-compatibly (keep supports*() as deprecated shims) or as a clean v1.0 break.
  • Best sequenced with the planned v1.0 interface audit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions