# How DS2API Converts Claude Messages Format to DeepSeek Web Context

> DS2API converts Claude messages to DeepSeek web context by resolving aliases, injecting system prompts, and forwarding parameters. Learn how this API bridges the gap between models. Read more.

- Repository: [CJACK./ds2api](https://github.com/CJackHwang/ds2api)
- Tags: internals
- Published: 2026-04-26

---

**DS2API converts Claude messages to DeepSeek web context by resolving model aliases, reshaping message arrays to inject system prompts, and forwarding compatible parameters like temperature and stop sequences.**

The open-source project **CJackHwang/ds2api** acts as a compatibility shim that translates Anthropic Claude-style API requests into DeepSeek-compatible web payloads. This conversion process bridges the structural differences between the two AI providers, allowing clients to send standard Claude requests while the proxy handles the protocol translation. Understanding this transformation is essential for developers integrating Claude-based tools with DeepSeek's backend infrastructure.

## The Three-Stage Conversion Pipeline

The conversion logic in [`internal/claudeconv/convert.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/claudeconv/convert.go) executes through three tightly coupled stages that map the incoming Claude payload to DeepSeek's expected schema.

### Model Resolution and Alias Mapping

First, DS2API resolves the Claude model name to its DeepSeek equivalent using the global alias configuration. In [`internal/claudeconv/convert.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/claudeconv/convert.go) (lines 16-19), the `ConvertClaudeToDeepSeek` function extracts the model field and attempts resolution via `config.ResolveModel`. If the alias lookup fails or returns an empty string, the system falls back to `deepseek-v4-flash` as a hard-coded default.

```go
// internal/claudeconv/convert.go
dsModel, ok := config.ResolveModel(aliasProvider, model)
if !ok || strings.TrimSpace(dsModel) == "" {
    dsModel = "deepseek-v4-flash"            // hard-coded fallback
}

```

### Message Reshaping and System Injection

The second stage handles the structural differences in message formatting. Claude permits a top-level `"system"` string separate from the messages array, while DeepSeek expects system instructions as a standard message object with `role: "system"`. The converter checks for this field at lines 21-26 and prepends a system message to the array before appending the original Claude messages unchanged.

```go
// internal/claudeconv/convert.go
convertedMessages := make([]any, 0, len(messages)+1)
if system, ok := claudeReq["system"].(string); ok && system != "" {
    convertedMessages = append(
        convertedMessages,
        map[string]any{"role": "system", "content": system},
    )
}
convertedMessages = append(convertedMessages, messages...)

```

### Parameter Forwarding and Field Translation

Finally, the converter maps supported top-level parameters from the Claude request to DeepSeek-compatible fields. According to lines 27-35 in [`convert.go`](https://github.com/CJackHwang/ds2api/blob/main/convert.go), DS2API forwards `temperature`, `top_p`, and `stream` verbatim. It specifically translates Claude's `stop_sequences` parameter to DeepSeek's `stop` field to satisfy the downstream API schema.

```go
// internal/claudeconv/convert.go
for _, k := range []string{"temperature", "top_p", "stream"} {
    if v, ok := claudeReq[k]; ok {
        out[k] = v
    }
}
if stopSeq, ok := claudeReq["stop_sequences"]; ok {
    out["stop"] = stopSeq
}

```

## Entry Points and API Integration

The HTTP API layer initiates conversion through `normalizeClaudeRequest` in [`internal/httpapi/claude/standard_request.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/claude/standard_request.go) (line 33). This handler receives the incoming Claude payload and invokes the conversion chain.

```go
// internal/httpapi/claude/standard_request.go
dsPayload := convertClaudeToDeepSeek(payload, store) // line 33

```

For broader codebase accessibility, a thin wrapper in [`internal/util/messages.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/util/messages.go) exposes the conversion with a built-in default model constant. The `ConvertClaudeToDeepSeek` wrapper injects `ClaudeDefaultModel` (defined as `"claude-sonnet-4-6"`) as the fallback when the incoming request lacks a model specification.

```go
// internal/util/messages.go
const ClaudeDefaultModel = "claude-sonnet-4-6"

func ConvertClaudeToDeepSeek(claudeReq map[string]any, store *config.Store) map[string]any {
    return claudeconv.ConvertClaudeToDeepSeek(claudeReq, store, ClaudeDefaultModel)
}

```

## Practical Conversion Example

When processing a typical Claude request containing a system prompt and user message, DS2API transforms the payload as follows:

```go
// Simulated Claude request (as received by DS2API)
claudeReq := map[string]any{
    "model": "claude-sonnet-4-6",
    "system": "You are a helpful assistant.",
    "messages": []any{
        map[string]any{"role": "user", "content": "Tell me a joke."},
    },
    "temperature": 0.7,
    "stream": true,
}

// Conversion (the same call chain used inside the server)
deepseekPayload := ConvertClaudeToDeepSeek(claudeReq, store)

// Resulting DeepSeek-compatible payload
/*
{
    "model": "deepseek-v4-flash",               // resolved alias or fallback
    "messages": [
        {"role":"system","content":"You are a helpful assistant."},
        {"role":"user","content":"Tell me a joke."}
    ],
    "temperature": 0.7,
    "stream": true
}
*/

```

## Summary

- **Model Resolution**: DS2API maps Claude model names to DeepSeek equivalents via `config.ResolveModel`, defaulting to `deepseek-v4-flash` when aliases are unavailable.
- **System Message Injection**: The converter transforms Claude's top-level `system` field into a DeepSeek message object with `role: "system"`, prepending it to the messages array.
- **Parameter Translation**: Supported fields like `temperature` and `stream` pass through unchanged, while `stop_sequences` becomes `stop` to match DeepSeek's schema.
- **File Locations**: Core logic resides in [`internal/claudeconv/convert.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/claudeconv/convert.go), HTTP handling in [`internal/httpapi/claude/standard_request.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/claude/standard_request.go), and the public wrapper in [`internal/util/messages.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/util/messages.go).

## Frequently Asked Questions

### What happens if the Claude request doesn't specify a model?

DS2API applies the built-in default `"claude-sonnet-4-6"` defined in [`internal/util/messages.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/util/messages.go), then attempts to resolve this to a DeepSeek model alias. If resolution fails, it falls back to `deepseek-v4-flash` according to the logic in [`internal/claudeconv/convert.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/claudeconv/convert.go).

### Does DS2API modify the original Claude messages array?

No, the converter preserves the original message objects unchanged. It only prepends a system message when the Claude request contains a top-level `system` string, creating a new array that includes both the injected system message and the original messages.

### Which parameters does DS2API forward to DeepSeek?

The converter explicitly forwards `temperature`, `top_p`, `stream`, and `stop_sequences` (renamed to `stop`). Other Claude-specific parameters are not included in the DeepSeek payload, as implemented in the parameter forwarding loop at lines 27-35 of [`internal/claudeconv/convert.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/claudeconv/convert.go).

### Where does the actual conversion get triggered in the HTTP API?

The entry point is `normalizeClaudeRequest` in [`internal/httpapi/claude/standard_request.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/claude/standard_request.go) at line 33, which calls `convertClaudeToDeepSeek` to transform the incoming request before forwarding it to DeepSeek's web endpoint.