@niscorp/signal 0.1.4 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @niscorp/signal
2
2
 
3
+ ## 0.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 6776f2d: A reply missing only its closing brace comes back whole, not as a piece of itself.
8
+
9
+ The repair ladder tried `extract` before `close-truncated`. On `{"reasoning":"…","data":{"rows":[…]}` — complete but for the last `}` — `extract` found the `rows` array inside it, and any acceptance schema that admits arrays (Cortex's does) took that array as the answer. Cortex then rejected it as not an envelope, and the model was asked again, up to its output-retry limit. The ladder now closes a cut-off reply before it looks for JSON inside it, so the whole object arrives at `wire.rung: 'close-truncated'`.
10
+
11
+ Only a reply that opens with `{` or `[` and is left unclosed takes a different path. A complete reply, a fenced or prose-wrapped one, and tool-call arguments go through the ladder as before.
12
+
13
+ **What to change:** nothing.
14
+
15
+ ## 0.1.5
16
+
17
+ ### Patch Changes
18
+
19
+ - c952ab0: A request to a model gets the options its maker or provider documents for whatever the caller leaves unset: `modelDefaults`, exported beside `modelRegistry`. gpt-oss on Groq gets temperature 1 and top_p 1, OpenAI's sampling for it (at temperature 0 its reasoning repeats one sentence until the budget runs out), and maxTokens 16,384, because Groq ends a completion at 3,072 tokens unless asked for more and reasoning alone can pass that. The caller's options always win.
20
+
3
21
  ## 0.1.4
4
22
 
5
23
  ### Patch Changes
package/DOCS.md CHANGED
@@ -146,6 +146,13 @@ Set LLM options: `temperature`, `maxTokens`, `topP`, `stopSequences`, `seed`, `s
146
146
  signal.options({ temperature: 0 })
147
147
  ```
148
148
 
149
+ ### Model defaults
150
+ Some models need options their caller does not know to set. `modelDefaults` (beside `modelRegistry` in `src/registry.ts`, exported) holds them, and a request gets them for whatever the caller leaves unset: the caller's options always win. gpt-oss on Groq gets `temperature: 1` and `topP: 1`, OpenAI's sampling for it (at temperature 0 its reasoning repeats one sentence until the budget runs out), and `maxTokens: 16384`, because Groq ends a completion at 3,072 tokens unless asked for more, and reasoning alone can pass that.
151
+ ```typescript
152
+ createSignal('groq').model('openai/gpt-oss-120b') // temperature 1, top_p 1, max_completion_tokens 16384
153
+ createSignal('groq').model('openai/gpt-oss-120b').options({ maxTokens: 4000 }) // temperature 1, top_p 1, max_completion_tokens 4000
154
+ ```
155
+
149
156
  ### Capabilities — looked up, not configured
150
157
  What a client can do is the provider's registry row (what the ENDPOINT
151
158
  does) joined with the model's registry row (what the MODEL does, as
package/dist/index.cjs CHANGED
@@ -201,6 +201,11 @@ var UNMEASURED_MODEL = {
201
201
  };
202
202
  var modelKey = (provider, model) => `${provider}/${model}`;
203
203
  var modelEntry = (provider, model) => modelRegistry[modelKey(provider, model)];
204
+ var GPT_OSS_ON_GROQ = { temperature: 1, topP: 1, maxTokens: 16384 };
205
+ var modelDefaults = {
206
+ "groq/openai/gpt-oss-120b": GPT_OSS_ON_GROQ,
207
+ "groq/openai/gpt-oss-20b": GPT_OSS_ON_GROQ
208
+ };
204
209
  var resolveApiKey = (envKey, explicitKey) => explicitKey ?? (typeof process !== "undefined" ? process.env?.[envKey] : void 0);
205
210
  var chatProviderEntry = (id) => {
206
211
  const entry = providerRegistry[id];
@@ -1171,6 +1176,11 @@ function* ladder(text, strategies) {
1171
1176
  if (trimmed.length === 0) return;
1172
1177
  const direct = tryParse2(trimmed);
1173
1178
  if (direct.ok) yield { value: direct.value, rung: "parse" };
1179
+ const closed = closeTruncated(trimmed);
1180
+ if (closed !== void 0) {
1181
+ const parsed = tryParse2(closed);
1182
+ if (parsed.ok) yield { value: parsed.value, rung: "close-truncated" };
1183
+ }
1174
1184
  const extracted = extractJson(trimmed);
1175
1185
  if (extracted.ok) yield { value: extracted.value, rung: "extract" };
1176
1186
  for (const repaired of repairEscapeDamage(trimmed)) {
@@ -1183,11 +1193,6 @@ function* ladder(text, strategies) {
1183
1193
  if (parsed.ok) yield { value: parsed.value, rung: strategy.id };
1184
1194
  }
1185
1195
  }
1186
- const closed = closeTruncated(trimmed);
1187
- if (closed !== void 0) {
1188
- const parsed = tryParse2(closed);
1189
- if (parsed.ok) yield { value: parsed.value, rung: "close-truncated" };
1190
- }
1191
1196
  }
1192
1197
  var firstAccepted = (candidates, accept) => {
1193
1198
  let nearMiss;
@@ -1311,6 +1316,10 @@ var modelRowOf = (config) => {
1311
1316
  const model = config.model ?? entry.defaultModel;
1312
1317
  return { provider: config.provider, model, row: modelEntry(config.provider, model) };
1313
1318
  };
1319
+ var defaultsOf = (config) => {
1320
+ const resolved = modelRowOf(config);
1321
+ return resolved === void 0 ? void 0 : modelDefaults[modelKey(resolved.provider, resolved.model)];
1322
+ };
1314
1323
  var resolveCapabilities = (config) => {
1315
1324
  if (typeof config.provider === "string") {
1316
1325
  const entry = providerRegistry[config.provider];
@@ -1376,6 +1385,7 @@ var createSignalFromConfig = (config) => {
1376
1385
  let cachedAdapterKey;
1377
1386
  const wireStrategies = resolveWireStrategies(wireIdsOf(config));
1378
1387
  assertEffort(config, config.options?.reasoningEffort);
1388
+ const defaults = defaultsOf(config);
1379
1389
  const getAdapter = async () => {
1380
1390
  const resolved = resolveProvider(config);
1381
1391
  const key = `${resolved.adapterType}:${resolved.baseUrl}:${resolved.apiKey}`;
@@ -1412,8 +1422,8 @@ var createSignalFromConfig = (config) => {
1412
1422
  ...providerTools && { tools: providerTools },
1413
1423
  ...request.toolChoice !== void 0 && { toolChoice: request.toolChoice },
1414
1424
  ...request.responseFormat !== void 0 && { responseFormat: request.responseFormat },
1415
- ...(config.options !== void 0 || request.options !== void 0) && {
1416
- options: { ...config.options, ...request.options }
1425
+ ...(defaults !== void 0 || config.options !== void 0 || request.options !== void 0) && {
1426
+ options: { ...defaults, ...config.options, ...request.options }
1417
1427
  }
1418
1428
  };
1419
1429
  assertEffort(config, providerRequest.options?.reasoningEffort);
@@ -1467,7 +1477,7 @@ var createSignalFromConfig = (config) => {
1467
1477
  const adapter = await getChatAdapter(verb);
1468
1478
  const resolved = resolveProvider(config);
1469
1479
  const declared = new Set((request.tools ?? []).map((tool) => tool.name));
1470
- const merged = config.options !== void 0 || request.options !== void 0 ? { ...request, options: { ...config.options, ...request.options } } : request;
1480
+ const merged = defaults !== void 0 || config.options !== void 0 || request.options !== void 0 ? { ...request, options: { ...defaults, ...config.options, ...request.options } } : request;
1471
1481
  assertEffort(config, merged.options?.reasoningEffort);
1472
1482
  try {
1473
1483
  for await (const event of executeStepStream({
@@ -1788,6 +1798,7 @@ exports.deepDecodeJsonish = deepDecodeJsonish;
1788
1798
  exports.defineTool = defineTool;
1789
1799
  exports.extractJson = extractJson;
1790
1800
  exports.isTruncatedJson = isTruncatedJson;
1801
+ exports.modelDefaults = modelDefaults;
1791
1802
  exports.modelEntry = modelEntry;
1792
1803
  exports.modelKey = modelKey;
1793
1804
  exports.modelRegistry = modelRegistry;