@tanstack/ai-muse 0.0.0-stage → 0.0.1

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.
@@ -0,0 +1,329 @@
1
+ import type { Modality, ModelReasoning } from '@tanstack/ai'
2
+ import type { ModelReasoningCapability } from '@tanstack/ai/adapter-internals'
3
+ import type { MuseTextProviderOptions } from './text/text-provider-options'
4
+
5
+ // Model data below is from Meta's official Model API docs (dev.meta.ai):
6
+ // "Pricing and rate limits", "Choose a model", and the per-model pages,
7
+ // checked 2026-11-11. Prices are USD per 1M tokens, Standard mode unless the
8
+ // model id says `-contributor`. Every reasoning model on the API has a
9
+ // 1,048,576-token context window. Muse Glimmer is an open-weight,
10
+ // self-hosted model — it is NOT served by the Model API and is deliberately
11
+ // absent here.
12
+
13
+ // Reasoning shapes: the levels a model has for `chat({ reasoning })`, and the
14
+ // provider value to send for each. Each model points at one of them.
15
+ const EFFORT_MINIMAL_TO_MAX = {
16
+ levels: {
17
+ minimal: 'minimal',
18
+ low: 'low',
19
+ medium: 'medium',
20
+ high: 'high',
21
+ xhigh: 'xhigh',
22
+ max: 'max',
23
+ },
24
+ budget: false,
25
+ } as const
26
+ const EFFORT_MINIMAL_TO_XHIGH = {
27
+ levels: {
28
+ minimal: 'minimal',
29
+ low: 'low',
30
+ medium: 'medium',
31
+ high: 'high',
32
+ xhigh: 'xhigh',
33
+ },
34
+ budget: false,
35
+ } as const
36
+
37
+ /**
38
+ * Internal metadata structure describing a Muse model's capabilities and
39
+ * pricing.
40
+ */
41
+ interface ModelMeta<TProviderOptions = unknown> {
42
+ name: string
43
+ context_window?: number
44
+ max_completion_tokens?: number
45
+ pricing: {
46
+ input?: { normal: number; cached?: number }
47
+ output?: { normal: number }
48
+ }
49
+ supports: {
50
+ input: Array<'text' | 'image'>
51
+ output: Array<'text'>
52
+ endpoints: Array<'chat'>
53
+ features: Array<
54
+ | 'streaming'
55
+ | 'tools'
56
+ | 'json_object'
57
+ | 'json_schema'
58
+ | 'reasoning'
59
+ | 'vision'
60
+ >
61
+ tools?: ReadonlyArray<never>
62
+ }
63
+ /** Type-level description of which provider options this model supports. */
64
+ providerOptions?: TProviderOptions
65
+ /** The model's data for `chat({ reasoning })`. None: it does not reason. */
66
+ reasoning?: ModelReasoning
67
+ }
68
+
69
+ const FEATURES = [
70
+ 'streaming',
71
+ 'tools',
72
+ 'json_object',
73
+ 'json_schema',
74
+ 'reasoning',
75
+ 'vision',
76
+ ] as const
77
+
78
+ // Ember pricing is the ≤200K-input-token tier; requests over 200K input
79
+ // tokens bill at 3x ($0.30 / $0.012 cached / $1.50) for the whole request.
80
+ const EMBER_1_0 = {
81
+ name: 'muse-ember-1.0',
82
+ context_window: 1_048_576,
83
+ max_completion_tokens: 524_288,
84
+ pricing: {
85
+ input: { normal: 0.1, cached: 0.004 },
86
+ output: { normal: 0.5 },
87
+ },
88
+ supports: {
89
+ input: ['text', 'image'],
90
+ output: ['text'],
91
+ endpoints: ['chat'],
92
+ features: [...FEATURES],
93
+ tools: [] as const,
94
+ },
95
+ reasoning: EFFORT_MINIMAL_TO_MAX,
96
+ } as const satisfies ModelMeta<MuseTextProviderOptions>
97
+
98
+ const WONDER_1_0 = {
99
+ name: 'muse-wonder-1.0',
100
+ context_window: 1_048_576,
101
+ max_completion_tokens: 131_072,
102
+ pricing: {
103
+ input: { normal: 4, cached: 0.2 },
104
+ output: { normal: 20 },
105
+ },
106
+ supports: {
107
+ input: ['text', 'image'],
108
+ output: ['text'],
109
+ endpoints: ['chat'],
110
+ features: [...FEATURES],
111
+ tools: [] as const,
112
+ },
113
+ reasoning: EFFORT_MINIMAL_TO_MAX,
114
+ } as const satisfies ModelMeta<MuseTextProviderOptions>
115
+
116
+ const SPARK_1_3 = {
117
+ name: 'muse-spark-1.3',
118
+ context_window: 1_048_576,
119
+ max_completion_tokens: 131_072,
120
+ pricing: {
121
+ input: { normal: 1.25, cached: 0.15 },
122
+ output: { normal: 4.25 },
123
+ },
124
+ supports: {
125
+ input: ['text', 'image'],
126
+ output: ['text'],
127
+ endpoints: ['chat'],
128
+ features: [...FEATURES],
129
+ tools: [] as const,
130
+ },
131
+ // Spark 1.3 is the only Spark that accepts `max` effort.
132
+ reasoning: EFFORT_MINIMAL_TO_MAX,
133
+ } as const satisfies ModelMeta<MuseTextProviderOptions>
134
+
135
+ const SPARK_1_3_CONTRIBUTOR = {
136
+ name: 'muse-spark-1.3-contributor',
137
+ context_window: 1_048_576,
138
+ max_completion_tokens: 131_072,
139
+ pricing: {
140
+ input: { normal: 0.1, cached: 0.002 },
141
+ output: { normal: 0.2 },
142
+ },
143
+ supports: {
144
+ input: ['text', 'image'],
145
+ output: ['text'],
146
+ endpoints: ['chat'],
147
+ features: [...FEATURES],
148
+ tools: [] as const,
149
+ },
150
+ // `max` is not available on the Contributor variant.
151
+ reasoning: EFFORT_MINIMAL_TO_XHIGH,
152
+ } as const satisfies ModelMeta<MuseTextProviderOptions>
153
+
154
+ const SPARK_1_2 = {
155
+ name: 'muse-spark-1.2',
156
+ context_window: 1_048_576,
157
+ max_completion_tokens: 131_072,
158
+ pricing: {
159
+ input: { normal: 1.25, cached: 0.15 },
160
+ output: { normal: 4.25 },
161
+ },
162
+ supports: {
163
+ input: ['text', 'image'],
164
+ output: ['text'],
165
+ endpoints: ['chat'],
166
+ features: [...FEATURES],
167
+ tools: [] as const,
168
+ },
169
+ reasoning: EFFORT_MINIMAL_TO_XHIGH,
170
+ } as const satisfies ModelMeta<MuseTextProviderOptions>
171
+
172
+ const SPARK_1_2_CONTRIBUTOR = {
173
+ name: 'muse-spark-1.2-contributor',
174
+ context_window: 1_048_576,
175
+ max_completion_tokens: 131_072,
176
+ pricing: {
177
+ input: { normal: 0.1, cached: 0.002 },
178
+ output: { normal: 0.2 },
179
+ },
180
+ supports: {
181
+ input: ['text', 'image'],
182
+ output: ['text'],
183
+ endpoints: ['chat'],
184
+ features: [...FEATURES],
185
+ tools: [] as const,
186
+ },
187
+ reasoning: EFFORT_MINIMAL_TO_XHIGH,
188
+ } as const satisfies ModelMeta<MuseTextProviderOptions>
189
+
190
+ const SPARK_1_1 = {
191
+ name: 'muse-spark-1.1',
192
+ context_window: 1_048_576,
193
+ max_completion_tokens: 32_768,
194
+ pricing: {
195
+ input: { normal: 1.25, cached: 0.15 },
196
+ output: { normal: 4.25 },
197
+ },
198
+ supports: {
199
+ input: ['text', 'image'],
200
+ output: ['text'],
201
+ endpoints: ['chat'],
202
+ features: [...FEATURES],
203
+ tools: [] as const,
204
+ },
205
+ reasoning: EFFORT_MINIMAL_TO_XHIGH,
206
+ } as const satisfies ModelMeta<MuseTextProviderOptions>
207
+
208
+ /**
209
+ * All supported Muse chat model identifiers.
210
+ */
211
+ export const MUSE_CHAT_MODELS = [
212
+ EMBER_1_0.name,
213
+ WONDER_1_0.name,
214
+ SPARK_1_3.name,
215
+ SPARK_1_3_CONTRIBUTOR.name,
216
+ SPARK_1_2.name,
217
+ SPARK_1_2_CONTRIBUTOR.name,
218
+ SPARK_1_1.name,
219
+ ] as const
220
+
221
+ /**
222
+ * Union type of all supported Muse chat model names.
223
+ */
224
+ export type MuseChatModels = (typeof MUSE_CHAT_MODELS)[number]
225
+
226
+ /**
227
+ * Type-only map from Muse chat model name to its supported input modalities.
228
+ */
229
+ export type MuseModelInputModalitiesByName = {
230
+ [EMBER_1_0.name]: typeof EMBER_1_0.supports.input
231
+ [WONDER_1_0.name]: typeof WONDER_1_0.supports.input
232
+ [SPARK_1_3.name]: typeof SPARK_1_3.supports.input
233
+ [SPARK_1_3_CONTRIBUTOR.name]: typeof SPARK_1_3_CONTRIBUTOR.supports.input
234
+ [SPARK_1_2.name]: typeof SPARK_1_2.supports.input
235
+ [SPARK_1_2_CONTRIBUTOR.name]: typeof SPARK_1_2_CONTRIBUTOR.supports.input
236
+ [SPARK_1_1.name]: typeof SPARK_1_1.supports.input
237
+ }
238
+
239
+ /**
240
+ * Runtime map from chat model name to its supported input modalities, for the
241
+ * text adapter's `inputModalities`. `satisfies` keeps it equal to
242
+ * {@link MuseModelInputModalitiesByName}. An unknown name gives `undefined`.
243
+ */
244
+ export const MUSE_MODEL_INPUT_MODALITIES: Readonly<
245
+ Record<string, ReadonlyArray<Modality>>
246
+ > = {
247
+ [EMBER_1_0.name]: EMBER_1_0.supports.input,
248
+ [WONDER_1_0.name]: WONDER_1_0.supports.input,
249
+ [SPARK_1_3.name]: SPARK_1_3.supports.input,
250
+ [SPARK_1_3_CONTRIBUTOR.name]: SPARK_1_3_CONTRIBUTOR.supports.input,
251
+ [SPARK_1_2.name]: SPARK_1_2.supports.input,
252
+ [SPARK_1_2_CONTRIBUTOR.name]: SPARK_1_2_CONTRIBUTOR.supports.input,
253
+ [SPARK_1_1.name]: SPARK_1_1.supports.input,
254
+ } satisfies MuseModelInputModalitiesByName
255
+
256
+ /**
257
+ * Type-only map from Muse chat model name to its provider options type.
258
+ */
259
+ export type MuseChatModelProviderOptionsByName = {
260
+ [K in (typeof MUSE_CHAT_MODELS)[number]]: MuseTextProviderOptions
261
+ }
262
+
263
+ /**
264
+ * Type-only map from Muse chat model name to its supported provider tools.
265
+ * Muse exposes no provider-specific tool factories, so every model gets an
266
+ * empty tuple. This makes passing another provider's ProviderTool to a Muse
267
+ * adapter a compile-time type error.
268
+ */
269
+ export type MuseChatModelToolCapabilitiesByName = {
270
+ [EMBER_1_0.name]: typeof EMBER_1_0.supports.tools
271
+ [WONDER_1_0.name]: typeof WONDER_1_0.supports.tools
272
+ [SPARK_1_3.name]: typeof SPARK_1_3.supports.tools
273
+ [SPARK_1_3_CONTRIBUTOR.name]: typeof SPARK_1_3_CONTRIBUTOR.supports.tools
274
+ [SPARK_1_2.name]: typeof SPARK_1_2.supports.tools
275
+ [SPARK_1_2_CONTRIBUTOR.name]: typeof SPARK_1_2_CONTRIBUTOR.supports.tools
276
+ [SPARK_1_1.name]: typeof SPARK_1_1.supports.tools
277
+ }
278
+
279
+ /**
280
+ * Resolves the provider options type for a specific Muse model.
281
+ */
282
+ export type ResolveProviderOptions<TModel extends string> =
283
+ TModel extends keyof MuseChatModelProviderOptionsByName
284
+ ? MuseChatModelProviderOptionsByName[TModel]
285
+ : MuseTextProviderOptions
286
+
287
+ /**
288
+ * Resolve input modalities for a specific model. If the model has explicit
289
+ * modalities in the map, use those; otherwise use text only.
290
+ */
291
+ export type ResolveInputModalities<TModel extends string> =
292
+ TModel extends keyof MuseModelInputModalitiesByName
293
+ ? MuseModelInputModalitiesByName[TModel]
294
+ : readonly ['text']
295
+
296
+ // Reasoning
297
+ //
298
+ // Each model's `reasoning` field holds its data for `chat({ reasoning })`: the
299
+ // levels it has, with the provider value for each, and whether it takes a
300
+ // thinking token budget.
301
+
302
+ const MUSE_REASONING = {
303
+ [EMBER_1_0.name]: EMBER_1_0.reasoning,
304
+ [WONDER_1_0.name]: WONDER_1_0.reasoning,
305
+ [SPARK_1_3.name]: SPARK_1_3.reasoning,
306
+ [SPARK_1_3_CONTRIBUTOR.name]: SPARK_1_3_CONTRIBUTOR.reasoning,
307
+ [SPARK_1_2.name]: SPARK_1_2.reasoning,
308
+ [SPARK_1_2_CONTRIBUTOR.name]: SPARK_1_2_CONTRIBUTOR.reasoning,
309
+ [SPARK_1_1.name]: SPARK_1_1.reasoning,
310
+ }
311
+
312
+ /**
313
+ * Each chat model's reasoning levels, and whether it takes a token budget, for
314
+ * `chat({ reasoning })`. Derived from the `reasoning` field. A model that is
315
+ * not here does not reason.
316
+ */
317
+ export type MuseModelReasoningByName = {
318
+ [K in keyof typeof MUSE_REASONING]: ModelReasoningCapability<
319
+ (typeof MUSE_REASONING)[K]
320
+ >
321
+ }
322
+
323
+ /**
324
+ * Runtime map from chat model name to its reasoning data, for the text
325
+ * adapter. An unknown name gives `undefined`: the adapter sends no reasoning
326
+ * field.
327
+ */
328
+ export const MUSE_MODEL_REASONING: Readonly<Record<string, ModelReasoning>> =
329
+ MUSE_REASONING
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Muse-specific provider options for text/chat models.
3
+ *
4
+ * Muse exposes an OpenAI-compatible Chat Completions endpoint, so these are
5
+ * the standard Chat Completions parameters. They are the values a caller can
6
+ * pass through `chat({ modelOptions })`.
7
+ */
8
+ export interface MuseTextProviderOptions {
9
+ /**
10
+ * Sampling temperature between 0 and 2. Higher values make the output more
11
+ * random; lower values make it more focused and deterministic.
12
+ */
13
+ temperature?: number | null
14
+
15
+ /**
16
+ * Nucleus sampling. The model considers the tokens with `top_p` probability
17
+ * mass. 0.1 means only the tokens in the top 10% are considered.
18
+ */
19
+ top_p?: number | null
20
+
21
+ /**
22
+ * The maximum number of tokens to generate in the chat completion. The total
23
+ * of input tokens and generated tokens is limited by the model's context
24
+ * window.
25
+ */
26
+ max_completion_tokens?: number | null
27
+
28
+ /**
29
+ * Number between -2.0 and 2.0. Positive values penalize new tokens by their
30
+ * frequency so far, so the model is less likely to repeat the same line.
31
+ */
32
+ frequency_penalty?: number | null
33
+
34
+ /**
35
+ * Number between -2.0 and 2.0. Positive values penalize new tokens that have
36
+ * already appeared, so the model is more likely to talk about new topics.
37
+ */
38
+ presence_penalty?: number | null
39
+
40
+ /**
41
+ * If set, the system makes a best effort to sample deterministically, so
42
+ * repeated requests with the same `seed` and parameters return the same
43
+ * result.
44
+ */
45
+ seed?: number | null
46
+
47
+ /**
48
+ * Up to 4 sequences where the API stops generating further tokens. The
49
+ * returned text does not contain the stop sequence.
50
+ */
51
+ stop?: string | null | Array<string>
52
+
53
+ /** Whether to enable parallel function calling during tool use. */
54
+ parallel_tool_calls?: boolean | null
55
+
56
+ /**
57
+ * A unique identifier for your end-user, which can help monitor and detect
58
+ * abuse.
59
+ */
60
+ user?: string | null
61
+ }
62
+
63
+ /**
64
+ * External provider options (what users pass in).
65
+ */
66
+ export type ExternalTextProviderOptions = MuseTextProviderOptions
@@ -0,0 +1,49 @@
1
+ import { getApiKeyFromEnv } from '@tanstack/ai-utils'
2
+ import type { createOpenAIClient } from '@tanstack/openai-base'
3
+
4
+ /**
5
+ * The options `createOpenAIClient` accepts. Derived from the base factory so
6
+ * the config type matches the exact `openai` copy the base resolves, rather
7
+ * than a second copy pulled in by a direct `openai` dependency.
8
+ */
9
+ type OpenAIClientOptions = Parameters<typeof createOpenAIClient>[0]
10
+
11
+ export interface MuseClientConfig extends Omit<OpenAIClientOptions, 'apiKey'> {
12
+ apiKey: string
13
+ }
14
+
15
+ /**
16
+ * Gets the Muse API key from environment variables.
17
+ *
18
+ * `MUSE_API_KEY` is the provider-scoped name, matching the repo's other
19
+ * adapters (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, …). Meta's own docs call
20
+ * the same key `MODEL_API_KEY`, so that name is accepted as a fallback.
21
+ *
22
+ * @throws Error if neither variable is found
23
+ */
24
+ export function getMuseApiKeyFromEnv(): string {
25
+ try {
26
+ return getApiKeyFromEnv('MUSE_API_KEY')
27
+ } catch {
28
+ try {
29
+ return getApiKeyFromEnv('MODEL_API_KEY')
30
+ } catch {
31
+ throw new Error(
32
+ 'MUSE_API_KEY is required. Please set it in your environment variables or use the factory function with an explicit API key.',
33
+ )
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Returns a Muse client config with the Meta Model API base URL applied
40
+ * when not already set. Meta serves Muse through an OpenAI-compatible
41
+ * endpoint at api.meta.ai, so the adapter drives it with the OpenAI SDK
42
+ * pointed at this base URL.
43
+ */
44
+ export function withMuseDefaults(config: MuseClientConfig): MuseClientConfig {
45
+ return {
46
+ ...config,
47
+ baseURL: config.baseURL || 'https://api.meta.ai/v1',
48
+ }
49
+ }