@polyweave/deepseek (1.0.8)
Installation
@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/npm install @polyweave/deepseek@1.0.8"@polyweave/deepseek": "1.0.8"About this package
@polyweave/deepseek
DeepSeek adapter for the Polyweave platform. OpenAI-compatible chat completions interface supporting streaming and batch modes, tool calling, image inputs, cost estimation with budget gating, and progressive receipt emission.
Published as @polyweave/deepseek.
Synopsis
import { createDeepSeekAdapter } from '@polyweave/deepseek';
import { createFrameSink, FrameType } from '@polyweave/core';
const adapter = createDeepSeekAdapter({
apiKey: process.env.DEEPSEEK_API_KEY,
model: 'deepseek-v4-pro',
maxRetries: 2,
budget: 0.50
});
adapter.source.pipe(createFrameSink(frame => {
if (frame.type === 'TEXT') console.log(frame.content.text);
if (frame.type === 'TOOL_CALL') console.log('Tool:', frame.content.name, frame.content.arguments);
if (frame.type === 'RECEIPT') console.log('Cost:', frame.content.totalCost);
}));
// Send a chat message
adapter.sink.write({
type: 'TEXT',
streamId: 'chat-1',
content: { text: 'Explain quantum computing in one paragraph.' }
});
// Use cost estimation before sending
adapter.sink.write({ type: 'COST_CHECK', content: { maxBudget: 0.01 } });
adapter.sink.write({ type: 'TEXT', streamId: 'chat-1', content: { text: 'Hello' } });
// Receives COST frame with estimate, or ERROR if over budget
Install
npm install @polyweave/deepseek
Why
DeepSeek provides state-of-the-art reasoning models (V4 Pro, R1) at a fraction of the cost of comparable providers, with an OpenAI-compatible API. This adapter handles the full lifecycle: conversation history management, streaming with progressive cost receipts, tool call accumulation across streaming chunks, configurable retry with exponential backoff, cost estimation with budget gating, and automatic partial receipts on error — so you never lose track of tokens consumed before a failure.
API
createDeepSeekAdapter(config)
Creates a DeepSeek adapter as a Polyweave frame duplex { source, sink }.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
config.apiKey |
string |
No | process.env.DEEPSEEK_API_KEY || null |
DeepSeek API key. Can be set later via adapter.setReady(key) or PARAM { key: 'apiKey', value: '...' }. |
config.baseURL |
string |
No | "https://api.deepseek.com/v1" |
API base URL. Endpoint appended: /chat/completions. |
config.timeout |
number |
No | 120000 |
Request timeout in ms. |
config.maxRetries |
number |
No | 2 |
Maximum retry attempts on connection error or timeout. Backoff: 100ms × 2^n, capped at 10s. |
config.budget |
number |
No | 0 |
Hard spending limit in USD. Once cumulative spend exceeds this, TEXT/IMAGE/AUDIO frames are rejected with BUDGET_EXCEEDED. |
config._debug |
boolean |
No | false |
Enables verbose console logging of request/response details. |
config.name |
string |
No | null |
Human-readable name exposed on adapter.name. |
Returns: IFrameDuplex { source, sink, setReady, ready, apiKey, resetConversation, _conversationLength, _outputTokenCount, name }
Additional properties on the returned duplex:
| Property | Type | Description |
|---|---|---|
setReady(key) |
function |
Set the API key post-creation. Flushes buffered frames. |
ready |
boolean (getter) |
Whether the adapter has an API key. |
apiKey |
string (getter) |
Current API key or null. |
resetConversation() |
function |
Clears accumulated conversation history. |
_conversationLength() |
function |
Returns length of conversation array (debug). |
_outputTokenCount |
number |
Live count of output tokens during streaming (debug). |
name |
string | null |
Adapter name from config. |
Default accumulated parameters (set via PARAM frames):
| Key | Default | Description |
|---|---|---|
model |
"deepseek-v4-pro" |
Model identifier. |
max_tokens |
1024 |
Maximum output tokens. |
temperature |
undefined |
Sampling temperature (0–2). |
top_p |
undefined |
Nucleus sampling parameter. |
stop |
undefined |
Stop sequence(s). |
stream |
true |
Whether to use streaming. Set to false for batch. |
thinking |
undefined |
Thinking mode. Defaults to { type: 'disabled' } when not set. |
system |
undefined |
System prompt (prepended to every request as a system message). |
messages |
undefined |
Raw messages (can replace conversation history). |
tools |
undefined |
Tool definitions array. Enables tool calling. |
tool_choice |
undefined |
Tool choice constraint. Only sent when tools is defined. |
response_format |
undefined |
Response format specification. |
logprobs |
undefined |
Log probabilities setting. |
top_logprobs |
undefined |
Number of top logprobs to return. |
Input frames (sink receives):
| Frame type | Action |
|---|---|
TEXT |
Pushes user message to conversation history. Triggers chat request. Rejected if budget exceeded (emits ERROR + END). |
IMAGE |
Pushes image message to conversation history. Requires content.url, content.data (base64), or content.b64_json. content.text or content.prompt provides accompanying text. Triggers chat request. |
AUDIO |
Same as TEXT (added to conversation as user message). Triggers chat request. |
TOOL_RESULT |
Pushes tool result to conversation as { role: 'tool', tool_call_id, content }. Does NOT trigger a request — next TEXT frame will include it in history. |
TOOL_CALL |
Pushes tool call to conversation as assistant clause with tool_calls. Used when consumers replay tool call history. |
PARAM |
Accumulates configuration. key: 'apiKey' triggers setReady(). Other keys update params dict. |
START / END |
Ignored. |
COST_CHECK |
Enters cost estimation mode. Subsequent frames are buffered, estimated, and checked against content.maxBudget. Emits a COST frame with estimate or an ERROR if over budget. Exits cost-check mode after emitting. Supports ESTIMATED_OUTPUT frames in the buffer (skipped). |
TOPOLOGY_SCAN |
Responds with TOPOLOGY_REPORT. |
TOPOLOGY_REPORT / TOPOLOGY_COMPLETE |
Passed through to source. |
TEST_INJECT |
Injects frame arrays for testing. |
TEST_SNAPSHOT |
Returns adapter paused state. |
Output frames (source emits):
| Frame type | When | Content |
|---|---|---|
TEXT |
On each streaming chunk or batch response | content.text with metadata: model, usage, finishReason. |
TOOL_CALL |
On each tool call (accumulated across streaming chunks, emitted at finish) | content.name, content.arguments (parsed JSON object), id (tool call ID). |
RECEIPT |
On response end (batch) or every 50 output tokens (streaming progressive) + final end | inputTokens, outputTokens, inputCost, outputCost, totalCost. Progressive receipts: progressive: true, final: false. Final: progressive: false, final: true. Emitted even on error with accumulated partial tokens. |
COST |
Response to COST_CHECK |
Estimate: totalCost, bounds, accuracy, estimateSource, forwardEstimate, inputBounds, maxOutputTokens. |
ERROR |
On API error, HTTP error, parse failure, budget exceeded, or timeout after all retries | content.message, error code "ADAPTER_ERROR" or "BUDGET_EXCEEDED". |
END |
After each response (or error) | Metadata: model, usage, finishReason. |
TOPOLOGY_REPORT / TEST_SNAPSHOT |
Protocol responses. |
Error conditions:
| Condition | Error message | Code |
|---|---|---|
Budget exceeded (TEXT/IMAGE/AUDIO frame when totalSpent >= budgetTotal) |
"Budget exceeded" |
BUDGET_EXCEEDED |
| COST_CHECK estimate exceeds maxBudget | "Estimated cost $X exceeds budget $Y" |
BUDGET_EXCEEDED |
| HTTP error status (≥400) | API error message from response body, or "HTTP {statusCode}" |
ADAPTER_ERROR |
| Stream parse error | "Stream error: {message}" |
(from API error object) |
| Batch parse error | "Failed to parse response: {message}" |
ADAPTER_ERROR |
| Connection error after all retries | "{message} (after {attempts} attempts)" |
ADAPTER_ERROR |
| Request timeout after all retries | "Request timeout (after {attempts} attempts)" |
ADAPTER_ERROR |
| IMAGE frame without url/data/b64_json | "IMAGE frame requires url, data, or b64_json" |
ADAPTER_ERROR |
| General catch in sink.write | err.message |
ADAPTER_ERROR |
On error during streaming, a partial progressive receipt is emitted for _outputTokenCount accumulated tokens before the ERROR frame, ensuring accurate cost tracking even when requests fail mid-stream.
Ready-state buffering:
If no apiKey is provided at creation, frames are buffered until setReady() is called with a key. Buffered frames are replayed through the sink.
getModelCost(model) (exported from src/costs.js)
Returns the pricing object for a DeepSeek model, or null if unknown.
Supported models and pricing (per million tokens):
| Model | Input ($/MTok) | Output ($/MTok) |
|---|---|---|
deepseek-chat |
0.27 | 1.10 |
deepseek-v4-pro |
0.27 | 1.10 |
deepseek-v3 |
0.27 | 1.10 |
deepseek-reasoner |
0.55 | 2.19 |
deepseek-r1 |
0.55 | 2.19 |
deepseek-r1-0528 |
0.55 | 2.19 |
deepseek-r1-distill-qwen-1.5b |
0.14 | 0.14 |
deepseek-r1-distill-qwen-7b |
0.14 | 0.14 |
deepseek-r1-distill-qwen-14b |
0.14 | 0.14 |
deepseek-r1-distill-qwen-32b |
0.14 | 0.14 |
deepseek-r1-distill-llama-8b |
0.14 | 0.14 |
deepseek-r1-distill-llama-70b |
0.14 | 0.14 |
calculateTokenCost(model, inputTokens, outputTokens) (exported from src/costs.js)
Calculates cost from token counts using the pricing table above.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
model |
string |
Yes | — | Model identifier. |
inputTokens |
number |
No | 0 |
Prompt tokens. |
outputTokens |
number |
No | 0 |
Completion tokens. |
Returns: { inputCost: number, outputCost: number, totalCost: number } — all in USD. Returns { inputCost: 0, outputCost: 0, totalCost: 0 } if the model is unknown.
Tests
npm test
Runs node --test test/deepseek.test.js test/rigor.test.js. Covering: adapter creation with API key, PARAM accumulation, cost calculation for all models, cost monotonicity (inputTokens/outputTokens), streaming frame handling, budget gating, and progressive receipt emission.
Requirements
- Node.js >= 22.0.0
- ESM only
- Runtime dependencies:
@polyweave/costs - Peer dependencies:
@polyweave/core,@polyweave/errors,@polyweave/costs— must be installed alongside. - DeepSeek API key (set via
DEEPSEEK_API_KEYenv var, config option, or PARAM frame).
Caveats
- Conversation history accumulates in memory. Use
adapter.resetConversation()between logical sessions. There is no automatic history pruning. - Tool calls in streaming mode are accumulated across chunks — the function name and arguments arrive piecewise in
delta.tool_calls. They are emitted asTOOL_CALLframes only whenfinish_reasonis present in the stream. - The
thinkingparameter defaults to{ type: 'disabled' }when not explicitly set. To use DeepSeek's reasoning/thinking mode, set it via PARAM:{ key: 'thinking', value: { type: 'enabled' } }. stream_options.include_usageis always enabled when streaming, ensuring token usage is reported in stream chunks.- The budget tracker (
config.budget) is a simple cumulative counter across the adapter's lifetime. There is no per-request or per-session budget — it tracks total spend since creation. - Cost estimation mode (
COST_CHECK) uses@polyweave/costscreateCostEstimatorwith provider'deepseek'and default model'deepseek-v4-pro'.
Dependencies
Development Dependencies
| ID | Version |
|---|---|
| @rigor/core | * |
Peer Dependencies
| ID | Version |
|---|---|
| @polyweave/core | * |
| @polyweave/costs | * |
| @polyweave/errors | * |