@glassly/llm-providers 0.3.5-dev.823 → 0.3.5-dev.826

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/llm-providers",
3
- "version": "0.3.5-dev.823",
3
+ "version": "0.3.5-dev.826",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "exports": {
@@ -10,7 +10,7 @@
10
10
  "test": "bun test"
11
11
  },
12
12
  "dependencies": {
13
- "@glassly/cloud-protocol": "0.3.5-dev.823"
13
+ "@glassly/cloud-protocol": "0.3.5-dev.826"
14
14
  },
15
15
  "files": [
16
16
  "src",
package/src/index.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  export {createAnthropicProvider} from "./anthropic";
9
9
  export {createGeminiProvider} from "./gemini";
10
- export {createOpenAiProvider} from "./openai";
10
+ export {createDeepSeekProvider, createOpenAiProvider} from "./openai";
11
11
  export type {
12
12
  LlmProviderCompleteArgs,
13
13
  LlmProviderImpl,
package/src/openai.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  /**
2
- * @fileoverview OpenAI Chat Completions provider.
2
+ * @fileoverview Chat Completions providers: OpenAI, and DeepSeek which serves
3
+ * the same wire format at its own endpoint. One translator, a dialect table
4
+ * for what differs (URL, default model, token-limit field name).
3
5
  *
4
6
  * Raw `fetch` like the other providers — this code runs both server-side and
5
7
  * on the phone (React Native), so no SDK and no Node APIs.
@@ -23,11 +25,31 @@
23
25
  * it is the contract-correct degradation — the model answers from knowledge
24
26
  * and the result carries no webSearchCount.
25
27
  */
26
- import type { LlmStopReason, LlmToolCall } from "@glassly/cloud-protocol/llm";
28
+ import type { LlmProvider, LlmStopReason, LlmToolCall } from "@glassly/cloud-protocol/llm";
27
29
  import type { LlmProviderCompleteArgs, LlmProviderImpl, LlmProviderOptions } from "./provider";
28
30
 
29
- const OPENAI_URL = "https://api.openai.com/v1/chat/completions";
30
- const FALLBACK_MODEL = "gpt-5.1";
31
+ interface ChatCompletionsDialect {
32
+ name: LlmProvider;
33
+ url: string;
34
+ fallbackModel: string;
35
+ /** OpenAI retired `max_tokens` on current models; DeepSeek still takes it. */
36
+ maxTokensField: "max_completion_tokens" | "max_tokens";
37
+ }
38
+
39
+ const OPENAI: ChatCompletionsDialect = {
40
+ name: "openai",
41
+ url: "https://api.openai.com/v1/chat/completions",
42
+ fallbackModel: "gpt-5.1",
43
+ maxTokensField: "max_completion_tokens",
44
+ };
45
+
46
+ const DEEPSEEK: ChatCompletionsDialect = {
47
+ name: "deepseek",
48
+ url: "https://api.deepseek.com/chat/completions",
49
+ fallbackModel: "deepseek-chat",
50
+ maxTokensField: "max_tokens",
51
+ };
52
+
31
53
  const DEFAULT_MAX_TOKENS = 2048;
32
54
 
33
55
  interface OpenAiToolCall {
@@ -92,12 +114,12 @@ function toOpenAiMessages(args: LlmProviderCompleteArgs): unknown[] {
92
114
  return out;
93
115
  }
94
116
 
95
- export function createOpenAiProvider(opts: LlmProviderOptions = {}): LlmProviderImpl {
96
- const defaultModel = opts.defaultModel?.trim() || FALLBACK_MODEL;
117
+ function createChatCompletionsProvider(dialect: ChatCompletionsDialect, opts: LlmProviderOptions): LlmProviderImpl {
118
+ const defaultModel = opts.defaultModel?.trim() || dialect.fallbackModel;
97
119
  const logger = opts.logger;
98
120
 
99
121
  return {
100
- name: "openai",
122
+ name: dialect.name,
101
123
  defaultModel,
102
124
 
103
125
  async complete(args) {
@@ -105,7 +127,7 @@ export function createOpenAiProvider(opts: LlmProviderOptions = {}): LlmProvider
105
127
 
106
128
  const body: Record<string, unknown> = {
107
129
  model,
108
- max_completion_tokens: args.maxTokens ?? DEFAULT_MAX_TOKENS,
130
+ [dialect.maxTokensField]: args.maxTokens ?? DEFAULT_MAX_TOKENS,
109
131
  messages: toOpenAiMessages(args),
110
132
  };
111
133
  // `temperature` is deliberately dropped, like the Anthropic provider:
@@ -120,7 +142,7 @@ export function createOpenAiProvider(opts: LlmProviderOptions = {}): LlmProvider
120
142
  }));
121
143
  }
122
144
 
123
- const res = await fetch(OPENAI_URL, {
145
+ const res = await fetch(dialect.url, {
124
146
  method: "POST",
125
147
  headers: {
126
148
  "Content-Type": "application/json",
@@ -131,8 +153,8 @@ export function createOpenAiProvider(opts: LlmProviderOptions = {}): LlmProvider
131
153
 
132
154
  if (!res.ok) {
133
155
  const detail = await res.text().catch(() => "");
134
- logger?.error({ status: res.status, model }, "openai completion failed");
135
- throw new Error(`openai ${res.status}: ${detail.slice(0, 300)}`);
156
+ logger?.error({ status: res.status, model }, `${dialect.name} completion failed`);
157
+ throw new Error(`${dialect.name} ${res.status}: ${detail.slice(0, 300)}`);
136
158
  }
137
159
 
138
160
  const data = (await res.json()) as OpenAiResponse;
@@ -161,9 +183,18 @@ export function createOpenAiProvider(opts: LlmProviderOptions = {}): LlmProvider
161
183
  inputTokens: data.usage?.prompt_tokens ?? 0,
162
184
  outputTokens: data.usage?.completion_tokens ?? 0,
163
185
  },
164
- provider: "openai" as const,
186
+ provider: dialect.name,
165
187
  model,
166
188
  };
167
189
  },
168
190
  };
169
191
  }
192
+
193
+ export function createOpenAiProvider(opts: LlmProviderOptions = {}): LlmProviderImpl {
194
+ return createChatCompletionsProvider(OPENAI, opts);
195
+ }
196
+
197
+ /** DeepSeek: OpenAI-compatible Chat Completions. `deepseek-chat` / `deepseek-reasoner`. */
198
+ export function createDeepSeekProvider(opts: LlmProviderOptions = {}): LlmProviderImpl {
199
+ return createChatCompletionsProvider(DEEPSEEK, opts);
200
+ }