nuabase 2.3.7 → 2.3.11

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 ADDED
@@ -0,0 +1,26 @@
1
+ # nuabase
2
+
3
+ ## 2.3.11
4
+
5
+ ### Patch Changes
6
+
7
+ - Ship the changelog in the npm package
8
+
9
+ ## 2.3.10
10
+
11
+ ### Patch Changes
12
+
13
+ - 0303881: The "fast" model alias now uses `openai/gpt-oss-120b` on Groq instead of the preview `qwen/qwen3.8-27b`. On Groq models that support strict structured outputs, casts now enforce the output schema natively instead of describing it in the prompt.
14
+
15
+ ## 2.3.9
16
+
17
+ ### Patch Changes
18
+
19
+ - Format the release script with Prettier
20
+
21
+ ## 2.3.8
22
+
23
+ ### Patch Changes
24
+
25
+ - Publish the packed tarball with npm, so 2FA is approved in the browser
26
+ Release nuabase through changesets and the release train
@@ -87,4 +87,91 @@ function requireLogger() {
87
87
  return logger;
88
88
  }
89
89
 
90
+ var strictJsonSchema = {};
91
+
92
+ var hasRequiredStrictJsonSchema;
93
+ function requireStrictJsonSchema() {
94
+ if (hasRequiredStrictJsonSchema) return strictJsonSchema;
95
+ hasRequiredStrictJsonSchema = 1;
96
+ Object.defineProperty(strictJsonSchema, "__esModule", { value: true });
97
+ strictJsonSchema.toStrictSchema = toStrictSchema;
98
+ const STRICT_UNSUPPORTED_KEYWORDS = /* @__PURE__ */ new Set([
99
+ "patternProperties",
100
+ "propertyNames",
101
+ "unevaluatedProperties",
102
+ "dependentRequired",
103
+ "dependentSchemas",
104
+ "if",
105
+ "then",
106
+ "else",
107
+ "not",
108
+ "allOf",
109
+ "oneOf"
110
+ ]);
111
+ function toStrictSchema(schema) {
112
+ return strictNode(schema);
113
+ }
114
+ function strictNode(node) {
115
+ if (typeof node !== "object" || node === null || Array.isArray(node)) {
116
+ return null;
117
+ }
118
+ const schema = node;
119
+ for (const key of Object.keys(schema)) {
120
+ if (STRICT_UNSUPPORTED_KEYWORDS.has(key))
121
+ return null;
122
+ }
123
+ const result = { ...schema };
124
+ delete result.$schema;
125
+ const isObject = schema.type === "object" || Array.isArray(schema.type) && schema.type.includes("object") || schema.properties !== void 0;
126
+ if (isObject) {
127
+ const properties = schema.properties ?? {};
128
+ if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false) {
129
+ return null;
130
+ }
131
+ const required = new Set(schema.required ?? []);
132
+ const shapedProperties = {};
133
+ for (const [name, child] of Object.entries(properties)) {
134
+ if (!required.has(name))
135
+ return null;
136
+ const shaped = strictNode(child);
137
+ if (!shaped)
138
+ return null;
139
+ shapedProperties[name] = shaped;
140
+ }
141
+ result.properties = shapedProperties;
142
+ result.required = Object.keys(shapedProperties);
143
+ result.additionalProperties = false;
144
+ }
145
+ if (schema.items !== void 0) {
146
+ const shaped = strictNode(schema.items);
147
+ if (!shaped)
148
+ return null;
149
+ result.items = shaped;
150
+ }
151
+ if (schema.prefixItems !== void 0)
152
+ return null;
153
+ if (schema.anyOf !== void 0) {
154
+ const variants = schema.anyOf.map(strictNode);
155
+ if (variants.some((v) => v === null))
156
+ return null;
157
+ result.anyOf = variants;
158
+ }
159
+ for (const defsKey of ["$defs", "definitions"]) {
160
+ if (schema[defsKey] !== void 0) {
161
+ const defs = {};
162
+ for (const [name, child] of Object.entries(schema[defsKey])) {
163
+ const shaped = strictNode(child);
164
+ if (!shaped)
165
+ return null;
166
+ defs[name] = shaped;
167
+ }
168
+ result[defsKey] = defs;
169
+ }
170
+ }
171
+ return result;
172
+ }
173
+ return strictJsonSchema;
174
+ }
175
+
90
176
  exports.requireLogger = requireLogger;
177
+ exports.requireStrictJsonSchema = requireStrictJsonSchema;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var logger = require('./chunks/logger-Co5uZG0A.cjs');
3
+ var strictJsonSchema = require('./chunks/strict-json-schema-DUE7RTxG.cjs');
4
4
  var zod = require('zod');
5
5
 
6
6
  var dist = {};
@@ -9841,7 +9841,7 @@ function requireProviderConfig() {
9841
9841
  };
9842
9842
  };
9843
9843
  exports$1.normalizeGeminiUsage = normalizeGeminiUsage;
9844
- const buildOpenAiStyleRequest = (baseUrl) => ({ prompt, model, maxTokens, assistantPrefillPrompt }, apiKey) => {
9844
+ const buildOpenAiStyleRequest = (baseUrl) => ({ prompt, model, maxTokens, assistantPrefillPrompt, responseSchema }, apiKey) => {
9845
9845
  const messages = [];
9846
9846
  messages.push({ role: "user", content: prompt });
9847
9847
  if (assistantPrefillPrompt) {
@@ -9854,7 +9854,13 @@ function requireProviderConfig() {
9854
9854
  max_completion_tokens: maxTokens,
9855
9855
  top_p: 1,
9856
9856
  stream: false,
9857
- stop: null
9857
+ stop: null,
9858
+ ...responseSchema && {
9859
+ response_format: {
9860
+ type: "json_schema",
9861
+ json_schema: { name: "response", strict: true, schema: responseSchema }
9862
+ }
9863
+ }
9858
9864
  };
9859
9865
  const headers = {
9860
9866
  "Content-Type": "application/json",
@@ -9981,6 +9987,12 @@ function requireProviderConfig() {
9981
9987
  providerId: "groq",
9982
9988
  apiOperation: "chat/completions",
9983
9989
  errorLabel: "Groq",
9990
+ // https://console.groq.com/docs/structured-outputs
9991
+ strictSchemaModels: /* @__PURE__ */ new Set([
9992
+ "openai/gpt-oss-120b",
9993
+ "openai/gpt-oss-20b",
9994
+ "qwen/qwen3.8-27b"
9995
+ ]),
9984
9996
  buildRequest: buildOpenAiStyleRequest("https://api.groq.com/openai/v1/chat/completions"),
9985
9997
  parseResponse: parseOpenAiStyleResponse("Groq")
9986
9998
  },
@@ -10028,7 +10040,7 @@ function requireModelInfo() {
10028
10040
  exports$1.MODEL_ALIASES = {
10029
10041
  fast: [
10030
10042
  { provider: "cerebras", model: "qwen-3.8-27b" },
10031
- { provider: "groq", model: "qwen/qwen3.8-27b" },
10043
+ { provider: "groq", model: "openai/gpt-oss-120b" },
10032
10044
  { provider: "openrouter", model: "z-ai/glm-5.2" }
10033
10045
  ],
10034
10046
  gemini: [
@@ -10128,7 +10140,7 @@ function requireProviderCall() {
10128
10140
  hasRequiredProviderCall = 1;
10129
10141
  Object.defineProperty(providerCall, "__esModule", { value: true });
10130
10142
  providerCall.callProvider = callProvider;
10131
- const logger_1 = logger.requireLogger();
10143
+ const logger_1 = strictJsonSchema.requireLogger();
10132
10144
  const provider_config_1 = requireProviderConfig();
10133
10145
  async function callProvider(call, logger) {
10134
10146
  const { target, apiOperation, request } = call;
@@ -10181,6 +10193,7 @@ function requireHttpEngine() {
10181
10193
  hasRequiredHttpEngine = 1;
10182
10194
  Object.defineProperty(httpEngine, "__esModule", { value: true });
10183
10195
  httpEngine.HttpEngine = void 0;
10196
+ const strict_json_schema_1 = strictJsonSchema.requireStrictJsonSchema();
10184
10197
  const provider_call_1 = requireProviderCall();
10185
10198
  const provider_config_1 = requireProviderConfig();
10186
10199
  const FATAL_HTTP_STATUSES = /* @__PURE__ */ new Set([401, 403, 404]);
@@ -10189,35 +10202,55 @@ function requireHttpEngine() {
10189
10202
  this.target = target;
10190
10203
  this.logger = logger;
10191
10204
  }
10192
- /** Provider APIs are given the schema in the prompt; none is enforced natively yet. */
10193
- nativeSchema() {
10194
- return null;
10205
+ /** Enforced in strict mode on models whose provider supports it; other models get the schema in the prompt. */
10206
+ nativeSchema(envelopeSchema) {
10207
+ const { provider, model } = this.target;
10208
+ if (!provider_config_1.providerConfigs[provider].strictSchemaModels?.has(model))
10209
+ return null;
10210
+ return (0, strict_json_schema_1.toStrictSchema)(envelopeSchema);
10195
10211
  }
10196
10212
  async attempt(request) {
10197
10213
  const { provider, model, apiKey } = this.target;
10198
10214
  const config = provider_config_1.providerConfigs[provider];
10215
+ const responseSchema = request.enforcement.kind === "native" ? request.enforcement.schema : void 0;
10199
10216
  const call = await (0, provider_call_1.callProvider)({
10200
10217
  target: this.target,
10201
10218
  apiOperation: config.apiOperation,
10202
10219
  maxTokens: request.maxTokens,
10203
- request: config.buildRequest({ prompt: request.prompt, model, maxTokens: request.maxTokens }, apiKey),
10220
+ request: config.buildRequest({ prompt: request.prompt, model, maxTokens: request.maxTokens, responseSchema }, apiKey),
10204
10221
  parseResponse: config.parseResponse,
10205
10222
  responseLength: (reply) => reply.text.length,
10206
10223
  usage: (reply) => reply.usage
10207
10224
  }, this.logger);
10208
10225
  if (call.kind === "failed") {
10209
- return { kind: "failed", failure: failureForStatus(call.status, call.message) };
10226
+ return { kind: "failed", failure: failureForStatus(call.status, call.message, responseSchema !== void 0) };
10227
+ }
10228
+ const answer = responseSchema ? readEnvelope(call.reply.text) : { kind: "text", text: call.reply.text };
10229
+ if (!answer) {
10230
+ return {
10231
+ kind: "failed",
10232
+ failure: { kind: "transient", message: `Structured output is not valid JSON: ${call.reply.text}` }
10233
+ };
10210
10234
  }
10211
10235
  return {
10212
10236
  kind: "answered",
10213
- answer: { kind: "text", text: call.reply.text },
10237
+ answer,
10214
10238
  usage: call.reply.usage,
10215
10239
  origin: { engine: "http", provider, model }
10216
10240
  };
10217
10241
  }
10218
10242
  }
10219
10243
  httpEngine.HttpEngine = HttpEngine;
10220
- function failureForStatus(status, message) {
10244
+ function readEnvelope(text) {
10245
+ try {
10246
+ return { kind: "structured", envelope: JSON.parse(text) };
10247
+ } catch {
10248
+ return null;
10249
+ }
10250
+ }
10251
+ function failureForStatus(status, message, sentSchema) {
10252
+ if (sentSchema && status === 400)
10253
+ return { kind: "schema-rejected", message };
10221
10254
  const fatal = status !== void 0 && FATAL_HTTP_STATUSES.has(status);
10222
10255
  return { kind: fatal ? "fatal" : "transient", message };
10223
10256
  }
@@ -10231,7 +10264,7 @@ function requireProviderEngines() {
10231
10264
  Object.defineProperty(providerEngines, "__esModule", { value: true });
10232
10265
  providerEngines.ProviderEngines = void 0;
10233
10266
  providerEngines.providerEngines = providerEngines$1;
10234
- const logger_1 = logger.requireLogger();
10267
+ const logger_1 = strictJsonSchema.requireLogger();
10235
10268
  const nua_errors_1 = requireNuaErrors();
10236
10269
  const model_info_1 = requireModelInfo();
10237
10270
  const http_engine_1 = requireHttpEngine();
@@ -11178,7 +11211,7 @@ function requireDist() {
11178
11211
  Object.defineProperty(exports$1, "__esModule", { value: true });
11179
11212
  exports$1.runAgentLoop = exports$1.runAgent = exports$1.parseListSchema = exports$1.listSchema = exports$1.stableStringify = exports$1.validateJsonSchema = exports$1.providerEngines = exports$1.providerConfigs = exports$1.normalizedUsageZero = exports$1.LLM_PROVIDER_IDS = void 0;
11180
11213
  __exportStar(requireNuaLlmClient(), exports$1);
11181
- __exportStar(logger.requireLogger(), exports$1);
11214
+ __exportStar(strictJsonSchema.requireLogger(), exports$1);
11182
11215
  __exportStar(requireTypes(), exports$1);
11183
11216
  __exportStar(requireNuaErrors(), exports$1);
11184
11217
  var provider_config_1 = requireProviderConfig();
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var logger = require('./chunks/logger-Co5uZG0A.cjs');
3
+ var strictJsonSchema = require('./chunks/strict-json-schema-DUE7RTxG.cjs');
4
4
  var node_module = require('node:module');
5
5
 
6
6
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -168,99 +168,13 @@ function requireClaudeCode() {
168
168
 
169
169
  var codex = {};
170
170
 
171
- var strictJsonSchema = {};
172
-
173
- var hasRequiredStrictJsonSchema;
174
- function requireStrictJsonSchema() {
175
- if (hasRequiredStrictJsonSchema) return strictJsonSchema;
176
- hasRequiredStrictJsonSchema = 1;
177
- Object.defineProperty(strictJsonSchema, "__esModule", { value: true });
178
- strictJsonSchema.toStrictSchema = toStrictSchema;
179
- const STRICT_UNSUPPORTED_KEYWORDS = /* @__PURE__ */ new Set([
180
- "patternProperties",
181
- "propertyNames",
182
- "unevaluatedProperties",
183
- "dependentRequired",
184
- "dependentSchemas",
185
- "if",
186
- "then",
187
- "else",
188
- "not",
189
- "allOf",
190
- "oneOf"
191
- ]);
192
- function toStrictSchema(schema) {
193
- return strictNode(schema);
194
- }
195
- function strictNode(node) {
196
- if (typeof node !== "object" || node === null || Array.isArray(node)) {
197
- return null;
198
- }
199
- const schema = node;
200
- for (const key of Object.keys(schema)) {
201
- if (STRICT_UNSUPPORTED_KEYWORDS.has(key))
202
- return null;
203
- }
204
- const result = { ...schema };
205
- delete result.$schema;
206
- const isObject = schema.type === "object" || Array.isArray(schema.type) && schema.type.includes("object") || schema.properties !== void 0;
207
- if (isObject) {
208
- const properties = schema.properties ?? {};
209
- if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false) {
210
- return null;
211
- }
212
- const required = new Set(schema.required ?? []);
213
- const shapedProperties = {};
214
- for (const [name, child] of Object.entries(properties)) {
215
- if (!required.has(name))
216
- return null;
217
- const shaped = strictNode(child);
218
- if (!shaped)
219
- return null;
220
- shapedProperties[name] = shaped;
221
- }
222
- result.properties = shapedProperties;
223
- result.required = Object.keys(shapedProperties);
224
- result.additionalProperties = false;
225
- }
226
- if (schema.items !== void 0) {
227
- const shaped = strictNode(schema.items);
228
- if (!shaped)
229
- return null;
230
- result.items = shaped;
231
- }
232
- if (schema.prefixItems !== void 0)
233
- return null;
234
- if (schema.anyOf !== void 0) {
235
- const variants = schema.anyOf.map(strictNode);
236
- if (variants.some((v) => v === null))
237
- return null;
238
- result.anyOf = variants;
239
- }
240
- for (const defsKey of ["$defs", "definitions"]) {
241
- if (schema[defsKey] !== void 0) {
242
- const defs = {};
243
- for (const [name, child] of Object.entries(schema[defsKey])) {
244
- const shaped = strictNode(child);
245
- if (!shaped)
246
- return null;
247
- defs[name] = shaped;
248
- }
249
- result[defsKey] = defs;
250
- }
251
- }
252
- return result;
253
- }
254
- return strictJsonSchema;
255
- }
256
-
257
171
  var hasRequiredCodex;
258
172
  function requireCodex() {
259
173
  if (hasRequiredCodex) return codex;
260
174
  hasRequiredCodex = 1;
261
175
  Object.defineProperty(codex, "__esModule", { value: true });
262
176
  codex.codexAdapter = void 0;
263
- const strict_json_schema_1 = requireStrictJsonSchema();
177
+ const strict_json_schema_1 = strictJsonSchema.requireStrictJsonSchema();
264
178
  const failure_classification_1 = requireFailureClassification();
265
179
  const SCHEMA_FILE = "output-schema.json";
266
180
  const LAST_MESSAGE_FILE = "last-message.txt";
@@ -781,7 +695,7 @@ function requireLocalAgentEngine() {
781
695
  hasRequiredLocalAgentEngine = 1;
782
696
  Object.defineProperty(localAgentEngine, "__esModule", { value: true });
783
697
  localAgentEngine.LocalAgentEngine = void 0;
784
- const logger_1 = logger.requireLogger();
698
+ const logger_1 = strictJsonSchema.requireLogger();
785
699
  const process_runner_1 = requireProcessRunner();
786
700
  class LocalAgentEngine {
787
701
  constructor(runner, model) {
@@ -940,7 +854,7 @@ function requireCreateLocalAgent() {
940
854
  Object.defineProperty(createLocalAgent, "__esModule", { value: true });
941
855
  createLocalAgent.localAgent = localAgent;
942
856
  createLocalAgent.findLocalAgent = findLocalAgent;
943
- const logger_1 = logger.requireLogger();
857
+ const logger_1 = strictJsonSchema.requireLogger();
944
858
  const adapters_1 = requireAdapters();
945
859
  const agent_id_1 = requireAgentId();
946
860
  const detect_1 = requireDetect();
@@ -85,4 +85,90 @@ function requireLogger() {
85
85
  return logger;
86
86
  }
87
87
 
88
- export { requireLogger as r };
88
+ var strictJsonSchema = {};
89
+
90
+ var hasRequiredStrictJsonSchema;
91
+ function requireStrictJsonSchema() {
92
+ if (hasRequiredStrictJsonSchema) return strictJsonSchema;
93
+ hasRequiredStrictJsonSchema = 1;
94
+ Object.defineProperty(strictJsonSchema, "__esModule", { value: true });
95
+ strictJsonSchema.toStrictSchema = toStrictSchema;
96
+ const STRICT_UNSUPPORTED_KEYWORDS = /* @__PURE__ */ new Set([
97
+ "patternProperties",
98
+ "propertyNames",
99
+ "unevaluatedProperties",
100
+ "dependentRequired",
101
+ "dependentSchemas",
102
+ "if",
103
+ "then",
104
+ "else",
105
+ "not",
106
+ "allOf",
107
+ "oneOf"
108
+ ]);
109
+ function toStrictSchema(schema) {
110
+ return strictNode(schema);
111
+ }
112
+ function strictNode(node) {
113
+ if (typeof node !== "object" || node === null || Array.isArray(node)) {
114
+ return null;
115
+ }
116
+ const schema = node;
117
+ for (const key of Object.keys(schema)) {
118
+ if (STRICT_UNSUPPORTED_KEYWORDS.has(key))
119
+ return null;
120
+ }
121
+ const result = { ...schema };
122
+ delete result.$schema;
123
+ const isObject = schema.type === "object" || Array.isArray(schema.type) && schema.type.includes("object") || schema.properties !== void 0;
124
+ if (isObject) {
125
+ const properties = schema.properties ?? {};
126
+ if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false) {
127
+ return null;
128
+ }
129
+ const required = new Set(schema.required ?? []);
130
+ const shapedProperties = {};
131
+ for (const [name, child] of Object.entries(properties)) {
132
+ if (!required.has(name))
133
+ return null;
134
+ const shaped = strictNode(child);
135
+ if (!shaped)
136
+ return null;
137
+ shapedProperties[name] = shaped;
138
+ }
139
+ result.properties = shapedProperties;
140
+ result.required = Object.keys(shapedProperties);
141
+ result.additionalProperties = false;
142
+ }
143
+ if (schema.items !== void 0) {
144
+ const shaped = strictNode(schema.items);
145
+ if (!shaped)
146
+ return null;
147
+ result.items = shaped;
148
+ }
149
+ if (schema.prefixItems !== void 0)
150
+ return null;
151
+ if (schema.anyOf !== void 0) {
152
+ const variants = schema.anyOf.map(strictNode);
153
+ if (variants.some((v) => v === null))
154
+ return null;
155
+ result.anyOf = variants;
156
+ }
157
+ for (const defsKey of ["$defs", "definitions"]) {
158
+ if (schema[defsKey] !== void 0) {
159
+ const defs = {};
160
+ for (const [name, child] of Object.entries(schema[defsKey])) {
161
+ const shaped = strictNode(child);
162
+ if (!shaped)
163
+ return null;
164
+ defs[name] = shaped;
165
+ }
166
+ result[defsKey] = defs;
167
+ }
168
+ }
169
+ return result;
170
+ }
171
+ return strictJsonSchema;
172
+ }
173
+
174
+ export { requireStrictJsonSchema as a, requireLogger as r };
@@ -1,4 +1,4 @@
1
- import { r as requireLogger } from './chunks/logger-DnkHzkX_.mjs';
1
+ import { r as requireLogger, a as requireStrictJsonSchema } from './chunks/strict-json-schema-DI9qiH5h.mjs';
2
2
  import { z } from 'zod';
3
3
  export { z } from 'zod';
4
4
 
@@ -9840,7 +9840,7 @@ function requireProviderConfig() {
9840
9840
  };
9841
9841
  };
9842
9842
  exports$1.normalizeGeminiUsage = normalizeGeminiUsage;
9843
- const buildOpenAiStyleRequest = (baseUrl) => ({ prompt, model, maxTokens, assistantPrefillPrompt }, apiKey) => {
9843
+ const buildOpenAiStyleRequest = (baseUrl) => ({ prompt, model, maxTokens, assistantPrefillPrompt, responseSchema }, apiKey) => {
9844
9844
  const messages = [];
9845
9845
  messages.push({ role: "user", content: prompt });
9846
9846
  if (assistantPrefillPrompt) {
@@ -9853,7 +9853,13 @@ function requireProviderConfig() {
9853
9853
  max_completion_tokens: maxTokens,
9854
9854
  top_p: 1,
9855
9855
  stream: false,
9856
- stop: null
9856
+ stop: null,
9857
+ ...responseSchema && {
9858
+ response_format: {
9859
+ type: "json_schema",
9860
+ json_schema: { name: "response", strict: true, schema: responseSchema }
9861
+ }
9862
+ }
9857
9863
  };
9858
9864
  const headers = {
9859
9865
  "Content-Type": "application/json",
@@ -9980,6 +9986,12 @@ function requireProviderConfig() {
9980
9986
  providerId: "groq",
9981
9987
  apiOperation: "chat/completions",
9982
9988
  errorLabel: "Groq",
9989
+ // https://console.groq.com/docs/structured-outputs
9990
+ strictSchemaModels: /* @__PURE__ */ new Set([
9991
+ "openai/gpt-oss-120b",
9992
+ "openai/gpt-oss-20b",
9993
+ "qwen/qwen3.8-27b"
9994
+ ]),
9983
9995
  buildRequest: buildOpenAiStyleRequest("https://api.groq.com/openai/v1/chat/completions"),
9984
9996
  parseResponse: parseOpenAiStyleResponse("Groq")
9985
9997
  },
@@ -10027,7 +10039,7 @@ function requireModelInfo() {
10027
10039
  exports$1.MODEL_ALIASES = {
10028
10040
  fast: [
10029
10041
  { provider: "cerebras", model: "qwen-3.8-27b" },
10030
- { provider: "groq", model: "qwen/qwen3.8-27b" },
10042
+ { provider: "groq", model: "openai/gpt-oss-120b" },
10031
10043
  { provider: "openrouter", model: "z-ai/glm-5.2" }
10032
10044
  ],
10033
10045
  gemini: [
@@ -10180,6 +10192,7 @@ function requireHttpEngine() {
10180
10192
  hasRequiredHttpEngine = 1;
10181
10193
  Object.defineProperty(httpEngine, "__esModule", { value: true });
10182
10194
  httpEngine.HttpEngine = void 0;
10195
+ const strict_json_schema_1 = requireStrictJsonSchema();
10183
10196
  const provider_call_1 = requireProviderCall();
10184
10197
  const provider_config_1 = requireProviderConfig();
10185
10198
  const FATAL_HTTP_STATUSES = /* @__PURE__ */ new Set([401, 403, 404]);
@@ -10188,35 +10201,55 @@ function requireHttpEngine() {
10188
10201
  this.target = target;
10189
10202
  this.logger = logger;
10190
10203
  }
10191
- /** Provider APIs are given the schema in the prompt; none is enforced natively yet. */
10192
- nativeSchema() {
10193
- return null;
10204
+ /** Enforced in strict mode on models whose provider supports it; other models get the schema in the prompt. */
10205
+ nativeSchema(envelopeSchema) {
10206
+ const { provider, model } = this.target;
10207
+ if (!provider_config_1.providerConfigs[provider].strictSchemaModels?.has(model))
10208
+ return null;
10209
+ return (0, strict_json_schema_1.toStrictSchema)(envelopeSchema);
10194
10210
  }
10195
10211
  async attempt(request) {
10196
10212
  const { provider, model, apiKey } = this.target;
10197
10213
  const config = provider_config_1.providerConfigs[provider];
10214
+ const responseSchema = request.enforcement.kind === "native" ? request.enforcement.schema : void 0;
10198
10215
  const call = await (0, provider_call_1.callProvider)({
10199
10216
  target: this.target,
10200
10217
  apiOperation: config.apiOperation,
10201
10218
  maxTokens: request.maxTokens,
10202
- request: config.buildRequest({ prompt: request.prompt, model, maxTokens: request.maxTokens }, apiKey),
10219
+ request: config.buildRequest({ prompt: request.prompt, model, maxTokens: request.maxTokens, responseSchema }, apiKey),
10203
10220
  parseResponse: config.parseResponse,
10204
10221
  responseLength: (reply) => reply.text.length,
10205
10222
  usage: (reply) => reply.usage
10206
10223
  }, this.logger);
10207
10224
  if (call.kind === "failed") {
10208
- return { kind: "failed", failure: failureForStatus(call.status, call.message) };
10225
+ return { kind: "failed", failure: failureForStatus(call.status, call.message, responseSchema !== void 0) };
10226
+ }
10227
+ const answer = responseSchema ? readEnvelope(call.reply.text) : { kind: "text", text: call.reply.text };
10228
+ if (!answer) {
10229
+ return {
10230
+ kind: "failed",
10231
+ failure: { kind: "transient", message: `Structured output is not valid JSON: ${call.reply.text}` }
10232
+ };
10209
10233
  }
10210
10234
  return {
10211
10235
  kind: "answered",
10212
- answer: { kind: "text", text: call.reply.text },
10236
+ answer,
10213
10237
  usage: call.reply.usage,
10214
10238
  origin: { engine: "http", provider, model }
10215
10239
  };
10216
10240
  }
10217
10241
  }
10218
10242
  httpEngine.HttpEngine = HttpEngine;
10219
- function failureForStatus(status, message) {
10243
+ function readEnvelope(text) {
10244
+ try {
10245
+ return { kind: "structured", envelope: JSON.parse(text) };
10246
+ } catch {
10247
+ return null;
10248
+ }
10249
+ }
10250
+ function failureForStatus(status, message, sentSchema) {
10251
+ if (sentSchema && status === 400)
10252
+ return { kind: "schema-rejected", message };
10220
10253
  const fatal = status !== void 0 && FATAL_HTTP_STATUSES.has(status);
10221
10254
  return { kind: fatal ? "fatal" : "transient", message };
10222
10255
  }
@@ -1,4 +1,4 @@
1
- import { r as requireLogger } from './chunks/logger-DnkHzkX_.mjs';
1
+ import { a as requireStrictJsonSchema, r as requireLogger } from './chunks/strict-json-schema-DI9qiH5h.mjs';
2
2
  import { createRequire } from 'node:module';
3
3
 
4
4
  var localAgent$1 = {};
@@ -165,92 +165,6 @@ function requireClaudeCode() {
165
165
 
166
166
  var codex = {};
167
167
 
168
- var strictJsonSchema = {};
169
-
170
- var hasRequiredStrictJsonSchema;
171
- function requireStrictJsonSchema() {
172
- if (hasRequiredStrictJsonSchema) return strictJsonSchema;
173
- hasRequiredStrictJsonSchema = 1;
174
- Object.defineProperty(strictJsonSchema, "__esModule", { value: true });
175
- strictJsonSchema.toStrictSchema = toStrictSchema;
176
- const STRICT_UNSUPPORTED_KEYWORDS = /* @__PURE__ */ new Set([
177
- "patternProperties",
178
- "propertyNames",
179
- "unevaluatedProperties",
180
- "dependentRequired",
181
- "dependentSchemas",
182
- "if",
183
- "then",
184
- "else",
185
- "not",
186
- "allOf",
187
- "oneOf"
188
- ]);
189
- function toStrictSchema(schema) {
190
- return strictNode(schema);
191
- }
192
- function strictNode(node) {
193
- if (typeof node !== "object" || node === null || Array.isArray(node)) {
194
- return null;
195
- }
196
- const schema = node;
197
- for (const key of Object.keys(schema)) {
198
- if (STRICT_UNSUPPORTED_KEYWORDS.has(key))
199
- return null;
200
- }
201
- const result = { ...schema };
202
- delete result.$schema;
203
- const isObject = schema.type === "object" || Array.isArray(schema.type) && schema.type.includes("object") || schema.properties !== void 0;
204
- if (isObject) {
205
- const properties = schema.properties ?? {};
206
- if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false) {
207
- return null;
208
- }
209
- const required = new Set(schema.required ?? []);
210
- const shapedProperties = {};
211
- for (const [name, child] of Object.entries(properties)) {
212
- if (!required.has(name))
213
- return null;
214
- const shaped = strictNode(child);
215
- if (!shaped)
216
- return null;
217
- shapedProperties[name] = shaped;
218
- }
219
- result.properties = shapedProperties;
220
- result.required = Object.keys(shapedProperties);
221
- result.additionalProperties = false;
222
- }
223
- if (schema.items !== void 0) {
224
- const shaped = strictNode(schema.items);
225
- if (!shaped)
226
- return null;
227
- result.items = shaped;
228
- }
229
- if (schema.prefixItems !== void 0)
230
- return null;
231
- if (schema.anyOf !== void 0) {
232
- const variants = schema.anyOf.map(strictNode);
233
- if (variants.some((v) => v === null))
234
- return null;
235
- result.anyOf = variants;
236
- }
237
- for (const defsKey of ["$defs", "definitions"]) {
238
- if (schema[defsKey] !== void 0) {
239
- const defs = {};
240
- for (const [name, child] of Object.entries(schema[defsKey])) {
241
- const shaped = strictNode(child);
242
- if (!shaped)
243
- return null;
244
- defs[name] = shaped;
245
- }
246
- result[defsKey] = defs;
247
- }
248
- }
249
- return result;
250
- }
251
- return strictJsonSchema;
252
- }
253
-
254
168
  var hasRequiredCodex;
255
169
  function requireCodex() {
256
170
  if (hasRequiredCodex) return codex;
@@ -1,4 +1,5 @@
1
1
  import { Logger } from "../../../lib/logger";
2
+ import { JsonSchema } from "../../../lib/schema-utils";
2
3
  import { AttemptOutcome, AttemptRequest, LlmEngine } from "../llm-engine";
3
4
  import { ProviderTarget } from "./provider-call";
4
5
  /** Answers cast attempts on one model of an LLM provider's HTTP API. */
@@ -6,7 +7,7 @@ export declare class HttpEngine implements LlmEngine {
6
7
  private readonly target;
7
8
  private readonly logger;
8
9
  constructor(target: ProviderTarget, logger: Logger);
9
- /** Provider APIs are given the schema in the prompt; none is enforced natively yet. */
10
- nativeSchema(): null;
10
+ /** Enforced in strict mode on models whose provider supports it; other models get the schema in the prompt. */
11
+ nativeSchema(envelopeSchema: JsonSchema): JsonSchema | null;
11
12
  attempt(request: AttemptRequest): Promise<AttemptOutcome>;
12
13
  }
@@ -1,3 +1,4 @@
1
+ import { JsonSchema } from "../../../lib/schema-utils";
1
2
  export type ProviderRequestBase = {
2
3
  url: string;
3
4
  method?: string;
@@ -15,6 +16,8 @@ export interface ProviderRequestOptions {
15
16
  model: string;
16
17
  maxTokens: number;
17
18
  assistantPrefillPrompt?: string;
19
+ /** A JSON schema the provider enforces on the reply, in strict mode. */
20
+ responseSchema?: JsonSchema;
18
21
  }
19
22
  export type NormalizedUsage = {
20
23
  promptTokens: number;
@@ -30,6 +33,8 @@ export interface ProviderConfig {
30
33
  providerId: LlmProviderId;
31
34
  apiOperation: string;
32
35
  errorLabel: string;
36
+ /** Models that can enforce a response JSON schema in strict mode. */
37
+ strictSchemaModels?: ReadonlySet<string>;
33
38
  buildRequest(options: ProviderRequestOptions, apiKey: string): ProviderRequestBase;
34
39
  parseResponse(response: Response): Promise<ProviderParsedResponse>;
35
40
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuabase",
3
3
  "description": "Nuabase TypeScript client to create typed functions from LLM prompts",
4
- "version": "2.3.7",
4
+ "version": "2.3.11",
5
5
  "type": "module",
6
6
  "author": "Jasim A Basheer <jasim@nuabase.com>",
7
7
  "engines": {
@@ -59,7 +59,6 @@
59
59
  "zod": "^4.1.5"
60
60
  },
61
61
  "devDependencies": {
62
- "ajv": "^8.17.1",
63
62
  "@eslint/js": "^9.35.0",
64
63
  "@ianvs/prettier-plugin-sort-imports": "^4.7.0",
65
64
  "@rollup/plugin-commonjs": "^28.0.6",
@@ -70,29 +69,23 @@
70
69
  "@types/jest": "^30.0.0",
71
70
  "@types/node": "^24.3.3",
72
71
  "@types/signale": "^1",
73
- "@types/yargs": "^17.0.33",
74
- "chalk": "^5.6.2",
72
+ "ajv": "^8.17.1",
75
73
  "esbuild": "^0.25.9",
76
74
  "eslint": "^9.35.0",
77
75
  "eventsource": "^4.0.0",
78
76
  "fs-extra": "^11.3.1",
79
77
  "jest": "^30.1.3",
80
78
  "knip": "^5.63.1",
81
- "new-github-release-url": "^2.0.0",
82
- "open": "^10.2.0",
83
79
  "prettier": "^3.6.2",
84
80
  "rimraf": "^6.0.1",
85
81
  "rollup": "^4.50.1",
86
82
  "rollup-plugin-esbuild": "^6.2.1",
87
83
  "rollup-plugin-node-externals": "^8.1.1",
88
84
  "signale": "^1.4.0",
89
- "simple-git": "^3.28.0",
90
85
  "ts-jest": "^29.4.1",
91
86
  "tsx": "^4.20.5",
92
87
  "typescript": "^5.9.2",
93
88
  "typescript-eslint": "^8.43.0",
94
- "version-next": "^1.0.2",
95
- "yargs": "^18.0.0",
96
89
  "zod": "^4.1.5",
97
90
  "zx": "^8.8.1",
98
91
  "nua-llm-core": "1.0.0"
@@ -108,7 +101,7 @@
108
101
  "lint": "eslint --cache .",
109
102
  "jest": "jest",
110
103
  "test": "pnpm run prettier:check && pnpm run typecheck && pnpm run lint && pnpm run jest && pnpm run build && pnpm run pack:test",
111
- "release": "pnpm test && tsx scripts/release",
104
+ "release": "tsx scripts/release",
112
105
  "knip": "knip"
113
106
  }
114
107
  }