Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
1 line
4.8 KiB
JSON
1 line
4.8 KiB
JSON
{"repo": "azrtydxb/go-ai-sdk", "files": {"docs/providers/deepseek.md": {"8271147fb82c7f1244c9b321c235a03af9b8fa27": {"t": 1786068469.6560695, "n": 4598, "h": "a0cb26251d88982b", "c": "# DeepSeek\n\nDeepSeek's API is OpenAI-chat-completions compatible; `providers/deepseek`\nis a thin preset over the shared `internal/openaicompat` base.\n\n```go\nimport \"github.com/azrtydxb/go-ai-sdk/providers/deepseek\"\n\n// APIKey defaults to os.Getenv(\"DEEPSEEK_API_KEY\"); BaseURL defaults to\n// \"https://api.deepseek.com/v1\". Auth is sent as \"Authorization: Bearer <key>\".\nmodel := deepseek.New(\n\tdeepseek.WithAPIKey(\"sk-...\"), // optional, overrides DEEPSEEK_API_KEY\n\tdeepseek.WithBaseURL(\"...\"), // optional, overrides the default base URL\n).Model(\"deepseek-chat\")\n```\n\n`deepseek.New` also accepts `deepseek.WithHTTPClient(*http.Client)` to\noverride the client used for requests.\n\n## Supported capabilities\n\n- **Text generation and streaming** — `deepseek.New().Model(id)`, used with\n `ai.GenerateText` / `ai.StreamText`.\n- **Tool calling** — standard `provider.Call.Tools` / `ToolChoice`, wired\n through the shared chat-completions request shape.\n- **Reasoning (`reasoning_content`)** — DeepSeek-R1-style models report\n their chain of thought via `reasoning_content`, surfaced as\n `provider.ReasoningPart` (non-streamed) or a run of\n `provider.ReasoningDelta`s (streamed). See\n [Reasoning](../core/reasoning.md#deepseek-reasoning_content).\n- **Structured JSON output** — supported, but constrained; see Quirks below.\n\nNot wired for this preset: embeddings, image generation, speech, or\ntranscription — `deepseek.Provider` only exposes `Model`, not\n`EmbeddingModel`.\n\n## Quirks and notes\n\n- **`response_format` is `json_object`-only.** DeepSeek's API rejects\n `json_schema` response formats. `providers/deepseek/deepseek.go` sets\n `openaicompat.Config.JSONObjectOnly: true` for exactly this reason:\n\n ```go\n // DeepSeek's response_format only accepts json_object, not\n // json_schema; it rejects requests that send the latter.\n JSONObjectOnly: true,\n ```\n\n With `JSONObjectOnly` set, `convertResponseFormat` in\n `internal/openaicompat/wire.go` always sends\n `{\"type\":\"json_object\"}` on the wire and drops any `Schema` you passed in\n `provider.ResponseFormat`, even when one is provided — schema conformance\n for DeepSeek is enforced by the `ai` core's decode step against the model's\n free-form JSON output, not by the wire request itself. Practically: ask\n for JSON in your prompt (DeepSeek's `json_object` mode requires the word\n \"json\" to appear somewhere in the messages) rather than relying on a\n schema being forwarded.\n\n- **`max_tokens`, not `max_completion_tokens`.** DeepSeek documents the\n older `max_tokens` field name and silently ignores OpenAI's current\n `max_completion_tokens`, so the preset sets\n `openaicompat.Config.MaxTokensParam: \"max_tokens\"`.\n\n- **`reasoning_content` is not replayed on later turns.** When a\n `provider.ReasoningPart` from a prior assistant turn is sent back as\n request history, `internal/openaicompat/wire.go`'s `convertMessages`\n drops it — DeepSeek-R1 and compatible APIs expect `reasoning_content` to\n be absent from prior turns in the request.\n\n## ProviderOptions\n\nRaw wire keys are merged verbatim into the chat-completions request body\n(see [Provider options](../core/provider-options.md)). `temperature` and\n`logprobs` below are the two keys directly exercised by\n`internal/openaicompat/provideroptions_test.go`\n(`TestChatProviderOptionsOverridesAndPassthrough`) — `temperature`\noverrides `provider.Call.Temperature`, and `logprobs` is a novel\npassthrough key with no dedicated `provider.Call` field:\n\n```go\nresult, err := ai.GenerateText(context.Background(), ai.GenerateTextOpts{\n\tModel: model,\n\tPrompt: \"Explain the CAP theorem in one sentence.\",\n\tProviderOptions: map[string]any{\n\t\t\"deepseek\": map[string]any{\n\t\t\t\"temperature\": 0.9,\n\t\t\t\"logprobs\": true,\n\t\t},\n\t},\n})\n```\n\nKeys not covered by a typed `provider.Call` field pass straight through;\nentries here take priority over anything the SDK built from typed fields.\n\n## Source of truth\n\n- [`providers/deepseek/deepseek.go`](../../providers/deepseek/deepseek.go)\n- [`internal/openaicompat/openaicompat.go`](../../internal/openaicompat/openaicompat.go)\n (`Config.JSONObjectOnly`, `Config.MaxTokensParam`)\n- [`internal/openaicompat/wire.go`](../../internal/openaicompat/wire.go)\n (`convertResponseFormat`, `ReasoningContent` handling in `convertMessages`\n and `convertResponse`)\n- [`internal/openaicompat/language_model.go`](../../internal/openaicompat/language_model.go)\n- [`internal/openaicompat/provideroptions_test.go`](../../internal/openaicompat/provideroptions_test.go)\n- [`docs/core/reasoning.md`](../core/reasoning.md#deepseek-reasoning_content)\n"}}}} |