@cartanova/qgrid-cli 2.5.3 → 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/README.ko.md +7 -2
- package/README.md +7 -2
- package/bundle/dist/application/monit/monit.frame.js +3 -3
- package/bundle/dist/application/qgrid/oauth.js +1 -1
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +36 -14
- package/bundle/dist/application/qgrid/qgrid.frame.js +19 -5
- package/bundle/dist/application/qgrid/tool-emulation-schema.js +93 -0
- package/bundle/dist/application/qgrid/tool-emulation.js +116 -96
- package/bundle/dist/application/request-log/request-log.model.js +3 -3
- package/bundle/dist/application/request-log-step/request-log-step.model.js +2 -2
- package/bundle/dist/application/token/token.model.js +2 -2
- package/bundle/dist/server-error-handler.js +15 -0
- package/bundle/dist/sonamu.config.js +4 -7
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +4 -3
- package/bundle/dist/utils/providers/anthropic/claude-session.js +7 -5
- package/bundle/dist/utils/providers/common/json-schema-keywords.js +77 -0
- package/bundle/dist/utils/providers/common/schema-validation.js +178 -0
- package/bundle/dist/utils/providers/common/strictifier.js +332 -42
- package/bundle/dist/utils/providers/openai/codex-worker.js +16 -8
- package/bundle/dist/utils/providers/openai/openai-dispatcher.js +5 -37
- package/bundle/dist/utils/providers/openai/openai-worker-pool-config.js +37 -0
- package/bundle/src/application/monit/monit.frame.ts +1 -1
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +103 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +337 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +45 -25
- package/bundle/src/application/qgrid/qgrid.frame.test.ts +580 -8
- package/bundle/src/application/qgrid/qgrid.frame.ts +28 -1
- package/bundle/src/application/qgrid/tool-emulation-schema.test.ts +188 -0
- package/bundle/src/application/qgrid/tool-emulation-schema.ts +177 -0
- package/bundle/src/application/qgrid/tool-emulation.test.ts +285 -67
- package/bundle/src/application/qgrid/tool-emulation.ts +199 -102
- package/bundle/src/server-error-handler.ts +22 -0
- package/bundle/src/sonamu.config.test.ts +37 -0
- package/bundle/src/sonamu.config.ts +2 -5
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +44 -0
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +2 -2
- package/bundle/src/utils/providers/anthropic/claude-session.test.ts +10 -0
- package/bundle/src/utils/providers/anthropic/claude-session.ts +6 -1
- package/bundle/src/utils/providers/common/json-schema-keywords.ts +72 -0
- package/bundle/src/utils/providers/common/schema-validation.test.ts +284 -0
- package/bundle/src/utils/providers/common/schema-validation.ts +308 -0
- package/bundle/src/utils/providers/common/strictifier.test.ts +746 -0
- package/bundle/src/utils/providers/common/strictifier.ts +520 -52
- package/bundle/src/utils/providers/openai/codex-worker.test.ts +31 -0
- package/bundle/src/utils/providers/openai/codex-worker.ts +12 -4
- package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +6 -4
- package/bundle/src/utils/providers/openai/openai-dispatcher.ts +6 -79
- package/bundle/src/utils/providers/openai/openai-worker-pool-config.ts +80 -0
- package/bundle/web-dist/client/assets/{index-DXdGBfB8.js → index-CN_kMswh.js} +3 -3
- package/bundle/web-dist/client/assets/{logs-D_j1yxC4.js → logs-6nYj9OKL.js} +1 -1
- package/bundle/web-dist/client/assets/{monit-CIToyoxW.js → monit-CaPH9Uwc.js} +1 -1
- package/bundle/web-dist/client/assets/{routes-Dd21A-wi.js → routes-Dh5HPi-b.js} +1 -1
- package/bundle/web-dist/client/assets/{show-DfePWRbc.js → show-Be_LVd9d.js} +1 -1
- package/bundle/web-dist/client/assets/{tokens-B4MV2fdk.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/ai-sdk-provider-contract.md +34 -2
- package/skills/qgrid/references/anthropic-claude-code-runtime.md +3 -0
- package/skills/qgrid/references/cli-env-and-server-boot.md +1 -1
- package/skills/qgrid/references/decision-rationale.md +10 -1
- package/skills/qgrid/references/docs-workflow.md +9 -0
- package/skills/qgrid/references/repo-map.md +2 -1
- package/skills/qgrid/references/tool-calling-and-multiturn.md +145 -6
- package/skills/qgrid/references/verification-and-debugging.md +16 -1
|
@@ -49,7 +49,16 @@ Sources:
|
|
|
49
49
|
Key decisions:
|
|
50
50
|
|
|
51
51
|
- qgrid tool calling is intentionally implemented through structured-output emulation, not native provider tool use. The provider call returns a structured action with tool calls; the AI SDK loop executes tools and sends follow-up turns.
|
|
52
|
-
- Tools and `jsonSchema`
|
|
52
|
+
- Tools and `jsonSchema` share one provider structured-output slot by composing
|
|
53
|
+
an action envelope on every turn. Its final `answer` branch embeds the user's
|
|
54
|
+
schema; its `tool_call` branch carries emulated AI SDK calls. This keeps final
|
|
55
|
+
output constrained even when tools are available but never called.
|
|
56
|
+
- Final output is identified by `action: "answer"`, not by tool presence or turn
|
|
57
|
+
count. Structured answers are serialized as JSON text for AI SDK
|
|
58
|
+
`Output.object` parsing, while tools-only callers retain their legacy string
|
|
59
|
+
answer and tolerant parse fallback.
|
|
60
|
+
- AI SDK `toolChoice` remains outside qgrid's wire contract. Do not imply that
|
|
61
|
+
qgrid transports or enforces it.
|
|
53
62
|
- Tool-call request logs are a multi-step run: create run, append generate/tool steps, then finish run. This makes AI SDK multi-step behavior visible in the dashboard instead of logging only one opaque completion.
|
|
54
63
|
- Tool results update the existing `tool_call` step by `request_log_id + tool_call_id`. They are not logged as a second unrelated completion row.
|
|
55
64
|
- The server infers single-turn completion versus an open tool run from context and finish reason. The removed caller-selected `logMode` contract must not be recreated.
|
|
@@ -40,3 +40,12 @@ When a repo doc establishes or changes durable rationale for a qgrid feature, re
|
|
|
40
40
|
- `docs/qgrid-doc-map.json` maps each durable topic to code, canonical skill references, and the matching Notion page.
|
|
41
41
|
- Code and tests remain the source of truth. Update the skill and Notion together when a code change alters a documented contract.
|
|
42
42
|
- Keep dated operational snapshots, internal server addresses, and other environment-specific facts in Notion. Keep reusable runtime contracts in the skill.
|
|
43
|
+
|
|
44
|
+
## Retract pre-release caveats after shipping
|
|
45
|
+
|
|
46
|
+
Documenting a contract before it ships is fine, but the caveat must be retracted once it does. Wording like "not deployed yet", "based on the working tree", "migration is still untracked", or "package version is still X" becomes a false statement the moment the release lands, and `pnpm qgrid-docs:check` will not catch it — that script validates the skill mirror and `docs/qgrid-doc-map.json` paths only, never Notion body text.
|
|
47
|
+
|
|
48
|
+
- When you add a pre-release caveat, record the retraction in the release page at the same time. Do not rely on memory.
|
|
49
|
+
- After a release, search the wiki for `미배포`, `배포되지 않았`, `working tree`, `untracked`, `아직`, and the previous version number.
|
|
50
|
+
- Update each page's baseline commit to the release commit. A stale baseline undermines every claim below it.
|
|
51
|
+
- This happened once already: caveats added 2026-07-23 survived the 2.4.9 and 2.5.0 releases and left six pages asserting the opposite of reality for several days.
|
|
@@ -28,7 +28,8 @@ Use this map to orient qgrid work quickly.
|
|
|
28
28
|
- Cost/usage accounting: `packages/api/src/utils/providers/common/model-cost.ts`.
|
|
29
29
|
- Weighted token selection: `packages/api/src/utils/providers/common/smooth-weighted-round-robin.ts`.
|
|
30
30
|
- Boot order and startup migrations: `packages/api/src/server-bootstrap.ts`, `packages/api/src/startup-migrations.ts`.
|
|
31
|
-
- Tool-call
|
|
31
|
+
- Tool-call schema composition: `packages/api/src/application/qgrid/tool-emulation-schema.ts`.
|
|
32
|
+
- Tool-call response decoding: `packages/api/src/application/qgrid/tool-emulation.ts`.
|
|
32
33
|
|
|
33
34
|
## Web/Sonamu hot spots
|
|
34
35
|
|
|
@@ -24,6 +24,11 @@ qgrid supports the AI SDK `tools` interface, but it does not use native provider
|
|
|
24
24
|
- qgrid maps that structured object back into AI SDK `tool-call` content.
|
|
25
25
|
- The AI SDK executes the actual tool functions and calls qgrid again with tool results.
|
|
26
26
|
|
|
27
|
+
`generateText` and `streamText` default to `stepCountIs(1)`. When an executable
|
|
28
|
+
tool can run before the final answer, callers must set a bounded `stopWhen`
|
|
29
|
+
(for example `stepCountIs(3)`) or the AI SDK stops after the first tool-call
|
|
30
|
+
step and cannot produce the final text or `Output.object` value.
|
|
31
|
+
|
|
27
32
|
Do not describe this as OpenAI native function calling, Claude native tool use, or Codex/Claude executing client tools. The tools are client-side AI SDK tools requested through structured-output emulation.
|
|
28
33
|
|
|
29
34
|
## Client Flow
|
|
@@ -36,7 +41,9 @@ When `options.tools` contains function tools:
|
|
|
36
41
|
2. Convert them with `toQgridTool`.
|
|
37
42
|
3. Send `tools` in `/api/qgrid/query` or `/api/qgrid/prepareStream`.
|
|
38
43
|
4. Forward the per-call `logger` switch when request logging is disabled.
|
|
39
|
-
5.
|
|
44
|
+
5. When a top-level object response format is present, send `jsonSchema` alongside
|
|
45
|
+
`tools`. The server composes both schemas and infers whether the logged request
|
|
46
|
+
becomes a multi-step run.
|
|
40
47
|
|
|
41
48
|
When qgrid responds with `finishReason: "tool-calls"`:
|
|
42
49
|
|
|
@@ -57,9 +64,10 @@ The registry is keyed per pending run so concurrent `generateText(...tools...)`
|
|
|
57
64
|
|
|
58
65
|
## Server Schema Emulation
|
|
59
66
|
|
|
60
|
-
`packages/api/src/application/qgrid/tool-emulation.ts` builds the tool-call schema.
|
|
67
|
+
`packages/api/src/application/qgrid/tool-emulation-schema.ts` builds the tool-call schema.
|
|
68
|
+
`tool-emulation.ts` decodes the model's action envelope into qgrid answer/tool-call content.
|
|
61
69
|
|
|
62
|
-
The schema shape is:
|
|
70
|
+
The tools-only schema shape is:
|
|
63
71
|
|
|
64
72
|
```json
|
|
65
73
|
{
|
|
@@ -83,7 +91,125 @@ The schema explicitly tells the model:
|
|
|
83
91
|
|
|
84
92
|
`qgrid.dispatcher.ts` strictifies this schema through `buildStrictOutputSchema` before it reaches provider dispatchers.
|
|
85
93
|
|
|
86
|
-
|
|
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.
|
|
87
213
|
|
|
88
214
|
## Provider-Specific Path
|
|
89
215
|
|
|
@@ -111,9 +237,14 @@ In both providers, native provider tools are not the public abstraction. The pub
|
|
|
111
237
|
`applyToolCallEmulation` handles provider text:
|
|
112
238
|
|
|
113
239
|
- No tools: return normal text content.
|
|
114
|
-
- Tools
|
|
240
|
+
- Tools-only mode and JSON parse failure: preserve the legacy warning and text
|
|
241
|
+
fallback with `finishReason: "stop"`.
|
|
242
|
+
- Tools plus a user schema and malformed or incoherent envelope: return an
|
|
243
|
+
explicit structured-output error; do not rescue it as successful text.
|
|
115
244
|
- `action: "tool_call"`: validate tool names, generate qgrid `toolCallId`, return `finishReason: "tool-calls"`.
|
|
116
|
-
- `action: "answer"
|
|
245
|
+
- `action: "answer"` in tools-only mode: return the string answer as final text.
|
|
246
|
+
- `action: "answer"` with a user schema: JSON-serialize the structured `answer`
|
|
247
|
+
as final text so AI SDK can parse and validate `Output.object`.
|
|
117
248
|
|
|
118
249
|
Unknown tool names throw.
|
|
119
250
|
|
|
@@ -128,6 +259,14 @@ AI SDK multi-step behavior is client-orchestrated:
|
|
|
128
259
|
|
|
129
260
|
This is different from a provider-native tool loop. Neither Codex nor Claude Code executes the AI SDK tool functions.
|
|
130
261
|
|
|
262
|
+
The composed schema is reapplied to tool-result follow-up turns. Final output is
|
|
263
|
+
identified by `action: "answer"` rather than by whether a tool happened to be
|
|
264
|
+
available or called. AI SDK `toolChoice` is not transported or enforced by
|
|
265
|
+
qgrid; the model selects between the envelope's answer and tool-call actions.
|
|
266
|
+
|
|
267
|
+
The combined contract requires qgrid server 2.5.4 and
|
|
268
|
+
`@cartanova/qgrid-ai-sdk` 2.5.4.
|
|
269
|
+
|
|
131
270
|
OpenAI/Codex follow-ups can reuse the same Codex thread when `runContext.threadCoord` remains valid. For tool-result follow-up turns, qgrid sends delta input containing tool result text to the existing thread; cold fallback injects full history.
|
|
132
271
|
|
|
133
272
|
Anthropic follow-ups do not reuse Claude sessions. qgrid starts a fresh Claude process, replays flattened full history, and sends the tool-result continuation input.
|
|
@@ -30,6 +30,7 @@ AI SDK package `@cartanova/qgrid-ai-sdk`:
|
|
|
30
30
|
- `pnpm --filter @cartanova/qgrid-ai-sdk test`.
|
|
31
31
|
- `pnpm --filter @cartanova/qgrid-ai-sdk build`.
|
|
32
32
|
- `pnpm --filter @cartanova/qgrid-ai-sdk e2e`.
|
|
33
|
+
- `pnpm --filter @cartanova/qgrid-ai-sdk e2e:tools-output`.
|
|
33
34
|
- `pnpm --filter @cartanova/qgrid-ai-sdk e2e:logger`.
|
|
34
35
|
|
|
35
36
|
CLI package `@cartanova/qgrid-cli`:
|
|
@@ -54,7 +55,7 @@ Choose by affected area:
|
|
|
54
55
|
| AI SDK logger integration | `packages/ai-sdk/src/logger.test.ts` |
|
|
55
56
|
| qgrid provider routing and strict schema | `packages/api/src/application/qgrid/qgrid.dispatcher.test.ts` |
|
|
56
57
|
| conversation/thread reuse routing | `packages/api/src/application/qgrid/conv-routing.test.ts` |
|
|
57
|
-
| tool-call emulation | `packages/api/src/application/qgrid/tool-emulation.test.ts` |
|
|
58
|
+
| tool-call emulation | `packages/api/src/application/qgrid/tool-emulation.test.ts`, `tool-emulation-schema.test.ts` |
|
|
58
59
|
| request-log run lifecycle | `packages/api/src/application/qgrid/qgrid-run-lifecycle.test.ts` |
|
|
59
60
|
| qgrid frame API behavior | `packages/api/src/application/qgrid/qgrid.frame.test.ts` |
|
|
60
61
|
| token subscriber and LISTEN/NOTIFY handling | `packages/api/src/application/qgrid/token-subscriber.test.ts` |
|
|
@@ -101,6 +102,20 @@ AI SDK structured output:
|
|
|
101
102
|
- Env: `QGRID_URL`, `SMOKE_MODEL`, `SMOKE_CASES`, `SMOKE_REPEAT`.
|
|
102
103
|
- Uses AI SDK 6 `streamText` with `Output.object`; useful for structured-output regressions.
|
|
103
104
|
|
|
105
|
+
AI SDK tools plus structured output:
|
|
106
|
+
|
|
107
|
+
- `packages/ai-sdk/e2e/tools-structured-output.ts`
|
|
108
|
+
- This is the public-path release acceptance matrix: `qgrid()` HTTP/SSE,
|
|
109
|
+
`generateText`/`streamText`, OpenAI/Anthropic, direct final answers, and one
|
|
110
|
+
client-tool result followed by `Output.object`.
|
|
111
|
+
- It consumes real provider quota and requires the explicit
|
|
112
|
+
`QGRID_REAL_PROVIDER_ACCEPTANCE=1` opt-in.
|
|
113
|
+
- Uses `QGRID_URL`, `QGRID_ACCEPTANCE_OPENAI_MODEL`, and
|
|
114
|
+
`QGRID_ACCEPTANCE_ANTHROPIC_MODEL`. OpenAI direct-final coverage defaults to
|
|
115
|
+
10 generate plus 10 stream attempts and can be changed with
|
|
116
|
+
`QGRID_ACCEPTANCE_OPENAI_DIRECT_REPEATS_PER_MODE`. Request logs use the stable
|
|
117
|
+
project name `qgrid-ai-sdk-tools-output-acceptance`.
|
|
118
|
+
|
|
104
119
|
Image generation:
|
|
105
120
|
|
|
106
121
|
- `packages/api/scripts/smoke-test-image-generation.ts`
|