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

# Chat Completions

> POST /v1/chat/completions — OpenAI-compatible chat completions endpoint

An OpenAI-compatible chat completions endpoint. If you're already using the OpenAI SDK, simply change `base_url` and `api_key` for a seamless migration.

```
POST https://api.profy.cn/v1/chat/completions
```

## Authentication

Two authentication methods are supported:

* **API Key**: `Authorization: Bearer sk-pro-xxxx`
* **OAuth Bearer**: `Authorization: Bearer <access_token>`

## Request Body

<ParamField body="model" type="string" required>
  Model identifier. Use `GET /v1/models` to query available models.
</ParamField>

<ParamField body="messages" type="array" required>
  Array of conversation messages. Each message includes `role` and `content` fields.
</ParamField>

<ParamField body="stream" type="boolean" default="false">
  Whether to use SSE streaming response.
</ParamField>

<ParamField body="temperature" type="number">
  Sampling temperature, range 0-2. Higher values produce more random output. Some models do not support this parameter (see model capabilities).
</ParamField>

<ParamField body="max_tokens" type="integer">
  Maximum number of tokens to generate.
</ParamField>

<ParamField body="top_p" type="number">
  Nucleus sampling parameter.
</ParamField>

<ParamField body="frequency_penalty" type="number">
  Frequency penalty parameter, range -2.0 to 2.0.
</ParamField>

<ParamField body="presence_penalty" type="number">
  Presence penalty parameter, range -2.0 to 2.0.
</ParamField>

<ParamField body="stop" type="string | string[]">
  Stop sequences for generation.
</ParamField>

<ParamField body="tools" type="array">
  Function Calling tool definition array (same format as OpenAI).
</ParamField>

<ParamField body="tool_choice" type="string | object">
  Tool selection strategy: `"auto"`, `"none"`, or a specific tool.
</ParamField>

## Non-Streaming Request Examples

<CodeGroup>
  ```python Python theme={null}
  from profy import Profy

  async with Profy(api_key="sk-pro-your-key") as client:
      resp = await client.chat.completions.create(
          model="deepseek-v3",
          messages=[
              {"role": "system", "content": "You are a professional translation assistant"},
              {"role": "user", "content": "Translate this to English: 今天天气真好"},
          ],
          temperature=0.3,
      )
      print(resp.text)
  ```

  ```typescript TypeScript theme={null}
  import { Profy } from "@profy-ai/sdk";

  const client = new Profy({ apiKey: "sk-pro-your-key" });

  const resp = await client.chat.completions.create({
    model: "deepseek-v3",
    messages: [
      { role: "system", content: "You are a professional translation assistant" },
      { role: "user", content: "Translate this to English: 今天天气真好" },
    ],
    temperature: 0.3,
  });

  console.log(resp.text);
  ```

  ```bash curl theme={null}
  curl -X POST https://api.profy.cn/v1/chat/completions \
    -H "Authorization: Bearer sk-pro-your-key" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "deepseek-v3",
      "messages": [
        {"role": "system", "content": "You are a professional translation assistant"},
        {"role": "user", "content": "Translate this to English: 今天天气真好"}
      ],
      "temperature": 0.3
    }'
  ```
</CodeGroup>

## Non-Streaming Response

```json theme={null}
{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "model": "deepseek-v3",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The weather is really nice today."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 28,
    "completion_tokens": 8,
    "total_tokens": 36
  },
  "balance_remaining": 9500
}
```

<Note>
  The response includes an additional `balance_remaining` field showing the remaining credit balance after the call. This is one of the key differences from OpenAI.
</Note>

## Streaming Request Examples

<CodeGroup>
  ```python Python (streaming) theme={null}
  from profy import Profy

  async with Profy(api_key="sk-pro-your-key") as client:
      stream = await client.chat.completions.create(
          model="deepseek-v3",
          messages=[{"role": "user", "content": "Tell me a joke"}],
          stream=True,
      )
      async for chunk in stream:
          if chunk.delta:
              print(chunk.delta, end="", flush=True)
  ```

  ```typescript TypeScript (streaming) theme={null}
  import { Profy } from "@profy-ai/sdk";

  const client = new Profy({ apiKey: "sk-pro-your-key" });

  const stream = await client.chat.completions.create({
    model: "deepseek-v3",
    messages: [{ role: "user", content: "Tell me a joke" }],
    stream: true,
  });

  for await (const chunk of stream) {
    if (chunk.delta) {
      process.stdout.write(chunk.delta);
    }
  }
  ```

  ```bash curl (streaming) theme={null}
  curl -N -X POST https://api.profy.cn/v1/chat/completions \
    -H "Authorization: Bearer sk-pro-your-key" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "deepseek-v3",
      "messages": [{"role": "user", "content": "Tell me a joke"}],
      "stream": true
    }'
  ```
</CodeGroup>

## As an OpenAI Drop-In Replacement

If you're using OpenAI's official SDK, simply change `base_url` and `api_key`:

<CodeGroup>
  ```python Python (OpenAI SDK) theme={null}
  from openai import OpenAI

  client = OpenAI(
      api_key="sk-pro-your-key",
      base_url="https://api.profy.cn/v1",
  )

  response = client.chat.completions.create(
      model="deepseek-v3",
      messages=[{"role": "user", "content": "Hello"}],
  )
  print(response.choices[0].message.content)
  ```

  ```typescript TypeScript (OpenAI SDK) theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: "sk-pro-your-key",
    baseURL: "https://api.profy.cn/v1",
  });

  const response = await client.chat.completions.create({
    model: "deepseek-v3",
    messages: [{ role: "user", content: "Hello" }],
  });
  console.log(response.choices[0].message.content);
  ```
</CodeGroup>

## Call a Profy Expert (Expert-as-Model)

In addition to calling raw LLM models, you can invoke any published Profy Expert (AI Agent) using the `profy-<identifier>` model name format. Behind the scenes, the Expert runs a full Agent stack: persona, tool calls, memory, and skills — but you interact with it just like any other model.

### Model Naming Convention

| Format               | Meaning                             | Example                   |
| -------------------- | ----------------------------------- | ------------------------- |
| `profy-<identifier>` | Invoke the specified Profy Expert   | `profy-financial-advisor` |
| Other model names    | Direct LLM call (existing behavior) | `deepseek-v3`             |

Use `GET /v1/models` to discover all available Expert models (`type: "expert"`).

### Request Examples (Non-Streaming)

Default behavior (`stream` omitted or `false`): the Agent completes all reasoning and tool calls, then returns a single JSON response.

<CodeGroup>
  ```python Python (OpenAI SDK) theme={null}
  from openai import OpenAI

  client = OpenAI(
      api_key="sk-pro-your-key",
      base_url="https://api.profy.cn/v1",
  )

  response = client.chat.completions.create(
      model="profy-financial-advisor",
      messages=[
          {"role": "user", "content": "Analyze Tesla's latest earnings report"}
      ],
  )

  print(response.choices[0].message.content)
  ```

  ```typescript TypeScript (OpenAI SDK) theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: "sk-pro-your-key",
    baseURL: "https://api.profy.cn/v1",
  });

  const response = await client.chat.completions.create({
    model: "profy-financial-advisor",
    messages: [
      { role: "user", content: "Analyze Tesla's latest earnings report" },
    ],
  });

  console.log(response.choices[0].message.content);
  ```

  ```bash curl theme={null}
  curl -X POST https://api.profy.cn/v1/chat/completions \
    -H "Authorization: Bearer sk-pro-your-key" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "profy-financial-advisor",
      "messages": [
        {"role": "user", "content": "Analyze Tesla'\''s latest earnings report"}
      ]
    }'
  ```
</CodeGroup>

### Request Examples (Streaming)

Set `stream: true` to receive incremental content in real time — useful for displaying text as it's generated.

<CodeGroup>
  ```python Python (OpenAI SDK, Streaming) theme={null}
  from openai import OpenAI

  client = OpenAI(
      api_key="sk-pro-your-key",
      base_url="https://api.profy.cn/v1",
  )

  response = client.chat.completions.create(
      model="profy-financial-advisor",
      messages=[
          {"role": "user", "content": "Analyze Tesla's latest earnings report"}
      ],
      stream=True,
  )

  for chunk in response:
      if chunk.choices[0].delta.content:
          print(chunk.choices[0].delta.content, end="")
  ```

  ```typescript TypeScript (OpenAI SDK, Streaming) theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: "sk-pro-your-key",
    baseURL: "https://api.profy.cn/v1",
  });

  const stream = await client.chat.completions.create({
    model: "profy-financial-advisor",
    messages: [
      { role: "user", content: "Analyze Tesla's latest earnings report" },
    ],
    stream: true,
  });

  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content;
    if (content) process.stdout.write(content);
  }
  ```

  ```bash curl (Streaming) theme={null}
  curl -N -X POST https://api.profy.cn/v1/chat/completions \
    -H "Authorization: Bearer sk-pro-your-key" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "profy-financial-advisor",
      "messages": [
        {"role": "user", "content": "Analyze Tesla'\''s latest earnings report"}
      ],
      "stream": true
    }'
  ```
</CodeGroup>

### Multi-Turn Conversations (Sessions)

A session is automatically created on the first call. The session ID is returned via the `X-Session-Id` response header. Pass the session ID in subsequent calls to continue the conversation:

* Request body `session_id` field (recommended; use `extra_body` with the OpenAI SDK)
* Request header `X-Session-Id`

```python theme={null}
from openai import OpenAI

client = OpenAI(
    api_key="sk-pro-your-key",
    base_url="https://api.profy.cn/v1",
)

# First turn
r1 = client.chat.completions.create(
    model="profy-financial-advisor",
    messages=[{"role": "user", "content": "My name is Alice. Help me create an investment plan."}],
    extra_body={}
)
session_id = r1._headers.get("x-session-id")

# Second turn — the Expert remembers your name and context
r2 = client.chat.completions.create(
    model="profy-financial-advisor",
    messages=[{"role": "user", "content": "Do you remember my name?"}],
    extra_body={"session_id": session_id}
)
```

### Expert-as-Model vs. Agents Run

| Feature                   | `/v1/chat/completions` + `profy-*`                | `/v1/agents/run`              |
| ------------------------- | ------------------------------------------------- | ----------------------------- |
| SDK Compatibility         | Works with OpenAI SDK directly                    | Requires Profy SDK            |
| Response Format           | Standard OpenAI Chat Completions                  | Profy custom SSE events       |
| Streaming / Non-Streaming | Both supported (`stream` defaults to `false`)     | Streaming only                |
| Tool Call Process         | Silent (handled internally by Agent)              | 16 event types fully exposed  |
| Best For                  | Drop-in replacement, LangChain, quick integration | Tool observability, custom UI |

<Tip>
  If you need to observe the Agent's full tool call process (e.g., displaying "Searching...", "Analyzing data..." intermediate states), use the `/v1/agents/run` endpoint with the Profy SDK.
</Tip>

## Differences from OpenAI

| Feature                    | Profy                                   | OpenAI             |
| -------------------------- | --------------------------------------- | ------------------ |
| Model names                | Profy platform model identifiers        | OpenAI model names |
| Additional response fields | `balance_remaining` (remaining credits) | None               |
| Function Calling           | Supported (select models)               | Fully supported    |
| Vision                     | Supported (select models)               | Supported          |
| Billing                    | Profy credits                           | USD                |

<Tip>
  Use `GET /v1/models` to query each model's `capabilities` to check whether the model supports temperature, tools, vision, and other features.
</Tip>

## Error Codes

| HTTP Status Code | Description                                         |
| ---------------- | --------------------------------------------------- |
| `400`            | Missing `model` or `messages`, or model unavailable |
| `401`            | Authentication failed                               |
| `502`            | Upstream model provider error                       |

## Next Steps

<CardGroup cols={2}>
  <Card title="Model List" icon="list" href="/en/developers/api/models">
    Query available models and their capabilities
  </Card>

  <Card title="Report Billing Events" icon="bolt" href="/en/developers/api/events">
    Report custom billing events
  </Card>
</CardGroup>
