@cartanova/qgrid-cli 2.5.4 → 2.5.5
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/bundle/dist/application/qgrid/tool-emulation-schema.js +9 -37
- package/bundle/dist/utils/providers/common/json-schema-keywords.js +77 -0
- package/bundle/dist/utils/providers/common/strictifier.js +42 -39
- package/bundle/src/application/qgrid/tool-emulation-schema.ts +29 -28
- package/bundle/src/utils/providers/common/json-schema-keywords.ts +72 -0
- package/bundle/src/utils/providers/common/strictifier.ts +46 -39
- package/bundle/web-dist/client/assets/{index-DiY5V2e3.js → index-CN_kMswh.js} +3 -3
- package/bundle/web-dist/client/assets/{logs-UZ4Cvdmc.js → logs-6nYj9OKL.js} +1 -1
- package/bundle/web-dist/client/assets/{monit-C5cJo-HD.js → monit-CaPH9Uwc.js} +1 -1
- package/bundle/web-dist/client/assets/{routes-DEKEmoQN.js → routes-Dh5HPi-b.js} +1 -1
- package/bundle/web-dist/client/assets/{show-DnX6b8KG.js → show-Be_LVd9d.js} +1 -1
- package/bundle/web-dist/client/assets/{tokens-Ct0FGD4b.js → tokens-CAi3zu_0.js} +1 -1
- package/bundle/web-dist/client/index.html +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +1 -1
- package/package.json +1 -1
- package/skills/qgrid/references/docs-workflow.md +9 -0
- package/skills/qgrid/references/tool-calling-and-multiturn.md +119 -7
|
@@ -91,13 +91,125 @@ The schema explicitly tells the model:
|
|
|
91
91
|
|
|
92
92
|
`qgrid.dispatcher.ts` strictifies this schema through `buildStrictOutputSchema` before it reaches provider dispatchers.
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
## Composing Tools With A User Output Schema
|
|
95
|
+
|
|
96
|
+
This section explains why `tool-emulation-schema.ts` exists at all. Read it
|
|
97
|
+
before changing schema composition; the mechanics look arbitrary without the
|
|
98
|
+
failure it was built to prevent.
|
|
99
|
+
|
|
100
|
+
### The problem
|
|
101
|
+
|
|
102
|
+
AI SDK hands the provider `tools` and `responseFormat` independently on every
|
|
103
|
+
step, and gives no "this is the final turn" flag. Before 2.5.4, qgrid saw tools
|
|
104
|
+
and dropped the user schema, so `answer` stayed a free-form string:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{ "action": "answer", "answer": "string | null", "toolCalls": "…" }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`strict: true` was in force, but only over that outer envelope. A model could
|
|
111
|
+
therefore return valid JSON with trailing garbage and still satisfy the schema:
|
|
112
|
+
|
|
113
|
+
```jsonc
|
|
114
|
+
// Envelope-valid. answer is a string, so the trailing marker is legal.
|
|
115
|
+
{ "action": "answer", "answer": "{\"title\":\"...\"}<|proto_end|>", "toolCalls": null }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Measured on the Medpath `translateBatch()` path: 5 of 10 raw Terra responses
|
|
119
|
+
were corrupted this way, while the same workload with a schema and no tools
|
|
120
|
+
failed 0 of 385 times. The model was fine; nothing constrained the final answer.
|
|
121
|
+
|
|
122
|
+
### The fix, and why it needs rebasing
|
|
123
|
+
|
|
124
|
+
Embed the user schema in the `answer` branch so the provider's
|
|
125
|
+
constrained-output machinery enforces it — the same machinery that scored
|
|
126
|
+
385/385. The model still chooses `tool_call` to continue the loop or `answer` to
|
|
127
|
+
finish, and qgrid never adds an extra turn to "finalize".
|
|
128
|
+
|
|
129
|
+
Embedding cannot be a verbatim nest, because `$ref` is document-absolute. `#`
|
|
130
|
+
means the root of the *final* document, not the root of the fragment it was
|
|
131
|
+
written in. Nest a caller schema unchanged and its pointers silently retarget
|
|
132
|
+
the envelope:
|
|
133
|
+
|
|
134
|
+
```jsonc
|
|
135
|
+
// Caller sends this. "#/$defs/Person" resolves inside their own document.
|
|
136
|
+
{
|
|
137
|
+
"type": "object",
|
|
138
|
+
"properties": { "author": { "$ref": "#/$defs/Person" } },
|
|
139
|
+
"$defs": { "Person": { "type": "object", "properties": { "name": { "type": "string" } } } }
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Nested verbatim, `#/$defs/Person` now points at the envelope root, where no
|
|
144
|
+
`Person` exists — a broken reference, or worse, a coincidental match against an
|
|
145
|
+
unrelated definition. Validation would then check the wrong shape and say
|
|
146
|
+
nothing.
|
|
147
|
+
|
|
148
|
+
So composition does two things: park the caller schema under a reserved name,
|
|
149
|
+
and rewrite every local pointer to the new base.
|
|
150
|
+
|
|
151
|
+
```jsonc
|
|
152
|
+
{
|
|
153
|
+
"type": "object",
|
|
154
|
+
"properties": {
|
|
155
|
+
"action": { "enum": ["answer", "tool_call"] },
|
|
156
|
+
// string branch becomes a reference to the caller's schema
|
|
157
|
+
"answer": { "anyOf": [{ "$ref": "#/$defs/__qgrid_user_output" }, { "type": "null" }] },
|
|
158
|
+
"toolCalls": { "…": "unchanged" }
|
|
159
|
+
},
|
|
160
|
+
"$defs": {
|
|
161
|
+
"__qgrid_user_output": {
|
|
162
|
+
"type": "object",
|
|
163
|
+
"properties": { "author": { "$ref": "#/$defs/__qgrid_user_output/$defs/Person" } },
|
|
164
|
+
"$defs": { "Person": { "type": "object", "properties": { "name": { "type": "string" } } } }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Pointer rewriting is mechanical — `#` gains the namespace prefix:
|
|
171
|
+
|
|
172
|
+
| Caller writes | Becomes |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `#` | `#/$defs/__qgrid_user_output` |
|
|
175
|
+
| `#/$defs/Person` | `#/$defs/__qgrid_user_output/$defs/Person` |
|
|
176
|
+
|
|
177
|
+
### Why the traversal is exhaustive
|
|
178
|
+
|
|
179
|
+
A missed position means an un-rebased `$ref` shipping to the provider, so the
|
|
180
|
+
rewriter must visit every place a subschema can hide. JSON Schema puts them in
|
|
181
|
+
three shapes — name→schema maps (`properties`, `$defs`), schema arrays
|
|
182
|
+
(`anyOf`, `prefixItems`), and single slots (`items`, `not`, `if`) — plus Draft-7
|
|
183
|
+
`dependencies`, whose value is either a schema or a list of property names.
|
|
184
|
+
|
|
185
|
+
That position list is shared: `utils/providers/common/json-schema-keywords.ts`
|
|
186
|
+
owns it, and the rebaser, the normalization scan, and the strict rewrite all
|
|
187
|
+
read from it. They previously kept private copies and had already drifted
|
|
188
|
+
(`additionalItems` was rebased but not scanned). Add a new keyword there, not in
|
|
189
|
+
a consumer.
|
|
190
|
+
|
|
191
|
+
Consumers still differ in what they *do* per position, and that stays local: the
|
|
192
|
+
strictifier marks positions like `not` and `if` non-normalizable because
|
|
193
|
+
tightening them would change caller semantics.
|
|
194
|
+
|
|
195
|
+
### What is rejected, and why silence is not an option
|
|
196
|
+
|
|
197
|
+
Composition fails loudly on `$id`, `id`, `$anchor`, `$dynamicAnchor`,
|
|
198
|
+
`$recursiveAnchor`, `$dynamicRef`, and `$recursiveRef`. Each one breaks the
|
|
199
|
+
premise that a pointer's meaning survives relocation: `$id` declares a new base
|
|
200
|
+
URI so descendant refs stop resolving against the document root, and dynamic
|
|
201
|
+
refs pick their target from runtime scope. There is no correct rewrite, and a
|
|
202
|
+
wrong one would validate the wrong shape without complaint — so callers get an
|
|
203
|
+
HTTP 400 naming the offending path instead.
|
|
204
|
+
|
|
205
|
+
Non-object top-level schemas are also rejected. Only `type: "object"` roots can
|
|
206
|
+
occupy the `answer` branch.
|
|
207
|
+
|
|
208
|
+
### Compatibility
|
|
209
|
+
|
|
210
|
+
`answer` falls back to `{ "type": "string" }` whenever no user schema is present.
|
|
211
|
+
That single branch is why tools-only calls and older SDKs keep working against a
|
|
212
|
+
2.5.4 server unchanged.
|
|
101
213
|
|
|
102
214
|
## Provider-Specific Path
|
|
103
215
|
|