# LangChain

> Use LangChain ChatOpenAI with MirAPI: point base_url at the OpenAI-compatible endpoint and switch models by name.

MirAPI exposes an OpenAI-compatible endpoint, so LangChain's `ChatOpenAI` class works with it directly — no dedicated integration package required. Point `base_url` at MirAPI and call any chat model from the catalogue, such as `deepseek-chat`.

## What you need

| Element | Value |
|---|---|
| Base URL | `https://api.mirapi.ai/v1` |
| API key | `sk-...` (set as `MIRAPI_API_KEY`) |
| Model | `deepseek-chat` |

## Install

```bash
pip install langchain-openai
```

```bash
npm install @langchain/openai
```

## Configure

Python:

```python
import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    base_url="https://api.mirapi.ai/v1",
    api_key=os.environ["MIRAPI_API_KEY"],
    model="deepseek-chat",
)
```

TypeScript:

```ts
import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "deepseek-chat",
  apiKey: process.env.MIRAPI_API_KEY,
  configuration: {
    baseURL: "https://api.mirapi.ai/v1",
  },
});
```

## Verify with a message

```python
response = llm.invoke("Reply with exactly: connected to MirAPI")
print(response.content)
```

The output should contain `connected to MirAPI`.

## Stream responses

```python
for chunk in llm.stream("Count from 1 to 5."):
    print(chunk.content, end="")
```

```ts
const stream = await llm.stream("Count from 1 to 5.");
for await (const chunk of stream) {
  process.stdout.write(chunk.content as string);
}
```

Streaming uses standard SSE and ends with `data: [DONE]`, which LangChain handles for you.

## Tool calling

```python
from langchain_core.tools import tool

@tool
def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b

llm_with_tools = llm.bind_tools([add])
response = llm_with_tools.invoke("What is 3 + 5?")
print(response.tool_calls)
```

`bind_tools` maps to the OpenAI `tools`/`tool_choice` format. The model must support tool calling — check its capability flag in the model catalogue.

## Structured output

```python
from pydantic import BaseModel

class Recipe(BaseModel):
    name: str
    ingredients: list[str]

structured = llm.with_structured_output(Recipe)
recipe = structured.invoke("Suggest a simple pasta recipe")
print(recipe)
```

Structured output maps to `response_format` (`json_schema` / JSON mode). The model must support JSON output.

## Parameters

| Parameter | Purpose |
|---|---|
| `model` | Model id, verbatim from `GET /v1/models` |
| `temperature` | Sampling temperature |
| `max_tokens` | Cap on output tokens |
| `streaming` | Enable SSE streaming |
| `reasoning_effort` | Reasoning intensity (reasoning-capable models) |
| `response_format` | Structured output (JSON mode / JSON Schema) |
| `tools` / `tool_choice` | Tool calling |

Reasoning tokens are billed as output. Prompt caching is automatic for OpenAI-compatible models, and cache reads are cheaper than regular input.

## Errors and troubleshooting

| Status | Meaning | Action |
|---|---|---|
| 401 | Missing, invalid, or unknown key | Fix the API key |
| 403 | Key valid but rejected (empty balance, model whitelist, quota, IP allowlist) | Resolve the cause, then retry |
| 413 | Payload too large | Reduce the request size |
| 429 | Rate limited — no `Retry-After` header | Jittered exponential backoff: 1s → 2s → 4s, cap ~30s |
| 500 | Gateway or upstream failure | Idempotent requests are safe to retry |

Errors use the OpenAI envelope `{"error":{"message","type","param","code"}}`, with `type` set to `new_api_error` (gateway) or `upstream_error` (upstream). Each message ends with a request ID — include it in support tickets. The gateway does not retry for you.

## Billing

Chat usage is billed per token in three tiers: input, output, and cache read. Cache reads are cheaper than regular input, and reasoning tokens are billed as output. MirAPI uses a prepaid USD balance with no subscription and no monthly fee, and the balance never expires. When the balance runs out, requests return 403; top up and the same key works again immediately.

## Troubleshooting

- **Keep `/v1` in `base_url`** — LangChain appends `/chat/completions` to the base URL.
- **Use model names verbatim** from `GET /v1/models`; the `vendor/` prefix is for console browsing only and is not part of the API name.
- **403 with a valid key** usually means the model is not whitelisted or the balance is exhausted.
- **429 has no `Retry-After` header** — implement backoff yourself.

## Related links

- [Quickstart](/docs/quickstart)
- [OpenAI compatibility](/docs/openai-compat)
- [Models & pricing](/docs/models)
- [Tool-calling agent](/docs/tutorials/tool-calling-agent)
- [Structured outputs](/docs/guides/structured-outputs)
- [LlamaIndex](/docs/integrations/llamaindex)