> ## Documentation Index
> Fetch the complete documentation index at: https://docs.engini.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# LLM providers (OpenAI & Anthropic)

> Turn Engini tools into vendor tool-calling schemas and run the agent loop - with zero vendor dependencies.

The SDK ships `Provider` adapters that convert Engini tool schemas into each vendor's tool-calling format, parse the model's tool calls, and format results back - without depending on the vendor's SDK.

## Pick a provider

<CodeGroup>
  ```python Python theme={null}
  from engini import Engini
  from engini.providers.anthropic import AnthropicProvider   # deep import in Python
  # from engini.providers.openai import OpenAIProvider       # OpenAI is the default

  client = Engini(provider=AnthropicProvider())
  ```

  ```typescript TypeScript theme={null}
  import { Engini, AnthropicProvider, OpenAIProvider } from "@engini/sdk"; // root exports in TS

  const client = new Engini({ provider: new AnthropicProvider() });
  ```
</CodeGroup>

## The agent loop (Anthropic)

A complete working loop, from the repo's Salesforce CRM agent example:

```python theme={null}
import anthropic
from engini import Engini
from engini.providers.anthropic import AnthropicProvider

client = Engini(provider=AnthropicProvider())
conn_id = next(c.connection_id for c in client.connections.list(application="salesforce"))
toolset = client.toolset(connections={"salesforce": conn_id})
tools = toolset.tools()                      # Anthropic-shaped tool schemas

llm = anthropic.Anthropic()
messages = [{"role": "user", "content": "How many open opportunities do we have?"}]
while True:
    reply = llm.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        system="You are a Salesforce CRM assistant. Use the tools to answer.",
        tools=tools,
        messages=messages,
    )
    if reply.stop_reason != "tool_use":
        break
    messages.append({"role": "assistant", "content": reply.content})
    results = toolset.handle_tool_calls(reply)          # executes every tool call
    # Anthropic requires all tool results in ONE user turn:
    messages.append({
        "role": "user",
        "content": [b for m in results for b in m["content"]],
    })
```

`handle_tool_calls` parses the vendor response, executes each call through the toolset's connections, and returns vendor-formatted result messages. Per-call tool failures come back as structured error results (the batch continues); systemic errors (auth, network, server) raise.

## The agent loop (OpenAI)

OpenAI is the default provider, so no configuration is needed:

<CodeGroup>
  ```python Python theme={null}
  toolset = client.toolset(tools=["salesforce_getrecords"], connections={"salesforce": "Prod"})
  openai_tools = client.provider.wrap_tools(toolset.tools(format="canonical"))

  # pass openai_tools to chat.completions.create(tools=...), then:
  results = toolset.handle_tool_calls(llm_response)   # returns role:"tool" messages
  ```

  ```typescript TypeScript theme={null}
  const toolset = client.toolset({ tools: ["salesforce_getrecords"], connections: { salesforce: "Prod" } });
  const openaiTools = client.provider.wrapTools(await toolset.tools({ format: "canonical" }));

  // pass openaiTools to chat.completions.create({ tools }), then:
  const results = await toolset.handleToolCalls(llmResponse);  // role:"tool" messages
  ```
</CodeGroup>

## Low-level: no Toolset

If you want full control, the provider primitives compose with plain `tools.execute`:

```python theme={null}
provider = client.provider
tools = provider.wrap_tools(client.tools.get(applications=["google-drive"]))

# ... send tools to the LLM, get a reply ...
blocks = []
for call in provider.parse_tool_calls(reply):
    result = client.tools.execute(call.name, call.arguments, connection_id=conn_id)
    blocks += provider.format_tool_result(call, output=result.output)["content"]
messages.append({"role": "user", "content": blocks})
```

The `Provider` contract is four methods - `wrap_tool(s)`, `parse_tool_calls`, `format_tool_result` - so adding another vendor is a small class, not a rewrite.
