dsh-cline-pass 0.1.0

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/lib/catalog.js ADDED
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Published model metadata, plus the merge that turns a caller's overrides into
3
+ * the exact descriptor the LLM seam validates.
4
+ *
5
+ * Context window and output cap are provider facts, not preferences: the
6
+ * harness sizes its prompt budget against `context.contextWindow`, so an
7
+ * understated window silently truncates history. Values come from the public
8
+ * catalog (models.dev), which agrees with the gateway's own `/v1/models`.
9
+ *
10
+ * The reasoning vocabulary is gateway-wide rather than per model: anything
11
+ * outside `none|minimal|low|medium|high|xhigh|max` is rejected with HTTP 400,
12
+ * so `off` is deliberately absent.
13
+ *
14
+ * @module dsh-cline-pass/catalog
15
+ */
16
+ /** Selectable reasoning efforts offered for a reasoning-capable model. */
17
+ export const REASONING_EFFORTS = [
18
+ { id: 'none', name: 'None', description: 'No extra thinking; fastest and cheapest.' },
19
+ { id: 'minimal', name: 'Minimal', description: 'A bare minimum of reasoning.' },
20
+ { id: 'low', name: 'Low', description: 'Prefer for routine or latency-sensitive work.' },
21
+ { id: 'medium', name: 'Medium', description: 'A balanced amount of reasoning.' },
22
+ { id: 'high', name: 'High', description: 'More thorough reasoning for multi-step work.' },
23
+ { id: 'xhigh', name: 'Extra high', description: 'For difficult work that rewards more thinking.' },
24
+ { id: 'max', name: 'Max', description: 'Reserve for the hardest quality-first tasks.' },
25
+ ]
26
+
27
+ /**
28
+ * Published metadata for the shipped Cline Pass models.
29
+ *
30
+ * `name` mirrors the provider's own display name where it publishes one, and
31
+ * falls back to the model id when the catalog has no separate label.
32
+ */
33
+ export const MODEL_CATALOG = {
34
+ 'cline-pass/deepseek-v4.1-flash': {
35
+ name: 'DeepSeek V4.1 Flash',
36
+ contextWindow: 1000000,
37
+ maxTokens: 384000,
38
+ reasoning: true,
39
+ input: ['text', 'image'],
40
+ },
41
+ 'cline-pass/deepseek-v4-flash': {
42
+ name: 'DeepSeek V4 Flash',
43
+ contextWindow: 1000000,
44
+ maxTokens: 384000,
45
+ reasoning: true,
46
+ input: ['text'],
47
+ },
48
+ 'cline-pass/deepseek-v4-pro': {
49
+ name: 'DeepSeek V4 Pro',
50
+ contextWindow: 1000000,
51
+ maxTokens: 384000,
52
+ reasoning: true,
53
+ input: ['text'],
54
+ },
55
+ 'cline-pass/glm-5.3': {
56
+ name: 'GLM-5.3',
57
+ contextWindow: 1000000,
58
+ maxTokens: 131072,
59
+ reasoning: true,
60
+ input: ['text'],
61
+ },
62
+ 'cline-pass/glm-5.3-flash': {
63
+ name: 'GLM-5.3 Flash',
64
+ contextWindow: 1000000,
65
+ maxTokens: 131072,
66
+ reasoning: true,
67
+ input: ['text', 'image'],
68
+ },
69
+ 'cline-pass/glm-5.2': {
70
+ name: 'GLM-5.2',
71
+ contextWindow: 1000000,
72
+ maxTokens: 131072,
73
+ reasoning: true,
74
+ input: ['text'],
75
+ },
76
+ 'cline-pass/kimi-k3': {
77
+ name: 'Kimi K3',
78
+ contextWindow: 1048576,
79
+ maxTokens: 131072,
80
+ reasoning: true,
81
+ input: ['text', 'image'],
82
+ },
83
+ 'cline-pass/kimi-k2.7-code': {
84
+ name: 'Kimi K2.7 Code',
85
+ contextWindow: 262144,
86
+ maxTokens: 262144,
87
+ reasoning: true,
88
+ input: ['text', 'image'],
89
+ },
90
+ 'cline-pass/kimi-k2.6': {
91
+ name: 'Kimi K2.6',
92
+ contextWindow: 262144,
93
+ maxTokens: 262144,
94
+ reasoning: true,
95
+ input: ['text', 'image'],
96
+ },
97
+ 'cline-pass/minimax-m3': {
98
+ name: 'MiniMax-M3',
99
+ contextWindow: 1048576,
100
+ maxTokens: 512000,
101
+ reasoning: true,
102
+ input: ['text', 'image'],
103
+ },
104
+ 'cline-pass/qwen3.8-max': {
105
+ name: 'Qwen3.8 Max',
106
+ contextWindow: 1000000,
107
+ maxTokens: 131072,
108
+ reasoning: true,
109
+ input: ['text', 'image'],
110
+ },
111
+ 'cline-pass/qwen3.7-max': {
112
+ name: 'Qwen3.7 Max',
113
+ contextWindow: 1000000,
114
+ maxTokens: 65536,
115
+ reasoning: true,
116
+ input: ['text'],
117
+ },
118
+ 'cline-pass/qwen3.7-plus': {
119
+ name: 'Qwen3.7 Plus',
120
+ contextWindow: 1000000,
121
+ maxTokens: 64000,
122
+ reasoning: true,
123
+ input: ['text', 'image'],
124
+ },
125
+ 'cline-pass/mimo-v2.5-pro': {
126
+ name: 'MiMo-V2.5-Pro',
127
+ contextWindow: 1048576,
128
+ maxTokens: 131072,
129
+ reasoning: true,
130
+ input: ['text'],
131
+ },
132
+ 'cline-pass/mimo-v2.5': {
133
+ name: 'MiMo-V2.5',
134
+ contextWindow: 1048576,
135
+ maxTokens: 131072,
136
+ reasoning: true,
137
+ input: ['text', 'image', 'audio'],
138
+ },
139
+ }
140
+
141
+ /** Published entry for one model id, or undefined when the catalog lacks it. */
142
+ export function catalogEntry(model) {
143
+ return MODEL_CATALOG[model]
144
+ }
145
+
146
+ /**
147
+ * Resolve one field as: configured value, then published value, then fallback.
148
+ * Configuration schemas default absent optional fields to `0`, `''` or `[]`,
149
+ * so an empty configured value must read as "not set" — otherwise the default
150
+ * would shadow the catalog and understate a real window to zero.
151
+ */
152
+ function pick(configured, published, fallback) {
153
+ const set = Array.isArray(configured)
154
+ ? configured.length > 0
155
+ : configured !== undefined && configured !== null && configured !== '' && configured !== 0
156
+ if (set) return configured
157
+ if (published !== undefined && published !== null) return published
158
+ return fallback
159
+ }
160
+
161
+ /**
162
+ * The seam's whole `ModelModality` vocabulary. A catalog entry may list more
163
+ * than this (models.dev reports `audio` for mimo-v2.5); the seam would reject
164
+ * the extra value, so the resolver clamps to what it declares.
165
+ */
166
+ const SEAM_MODALITIES = new Set(['text', 'image'])
167
+
168
+ /**
169
+ * Resolve the metadata seam fields for one model.
170
+ *
171
+ * Precedence per field: the configured override, then the published catalog,
172
+ * then the route-wide fallback.
173
+ *
174
+ * @returns a descriptor the seam's `normalizeModelInfo` accepts.
175
+ */
176
+ export function resolveModelMetadata(provider, model, override, fallback) {
177
+ const published = catalogEntry(model) ?? {}
178
+ const name = pick(override?.name, published.name, model)
179
+ const contextWindow = pick(override?.contextWindow, published.contextWindow, fallback.contextWindow)
180
+ const maxTokens = pick(override?.maxTokens, published.maxTokens, fallback.maxTokens)
181
+ const modality = pick(override?.input, published.input, undefined)
182
+ const reasoning = override?.reasoning ?? published.reasoning ?? fallback.reasoning
183
+ const offered = (modality ?? ['text']).filter((entry) => SEAM_MODALITIES.has(entry))
184
+ return {
185
+ provider,
186
+ id: model,
187
+ name,
188
+ inputModalities: offered.length > 0 ? offered : ['text'],
189
+ context: { contextWindow },
190
+ defaultMaxTokens: maxTokens,
191
+ ...(reasoning === true ? { reasoning: { efforts: REASONING_EFFORTS } } : {}),
192
+ }
193
+ }