-
Notifications
You must be signed in to change notification settings - Fork 3.1k
feat(providers/pi): best-effort structured output via prompt engineering #1297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,6 +64,30 @@ function lookupPiModel(provider: string, modelId: string): Model<Api> | undefine | |
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Append a "respond with JSON matching this schema" instruction to the user | ||
| * prompt so Pi-backed models produce parseable structured output. Pi's SDK | ||
| * has no JSON-mode equivalent to Claude's outputFormat or Codex's | ||
| * outputSchema, so this is a best-effort fallback: the event bridge parses | ||
| * the assistant transcript on agent_end. Models that reliably follow | ||
| * instruction (GPT-5, Claude, Gemini 2.x, recent Qwen Coder, DeepSeek V3) | ||
| * return clean JSON; models that don't produce a parse failure, which the | ||
| * executor surfaces via the existing dag.structured_output_missing warning. | ||
| */ | ||
| export function augmentPromptForJsonSchema( | ||
| prompt: string, | ||
| schema: Record<string, unknown> | ||
| ): string { | ||
| return `${prompt} | ||
|
|
||
| --- | ||
|
|
||
| CRITICAL: Respond with ONLY a JSON object matching the schema below. No prose before or after the JSON. No markdown code fences. Just the raw JSON object as your final message. | ||
|
|
||
| Schema: | ||
| ${JSON.stringify(schema, null, 2)}`; | ||
|
Comment on lines
+77
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid forcing object-shaped output for every schema. Line 85 asks for a JSON object, but JSON Schema can describe arrays/scalars and Suggested prompt wording-CRITICAL: Respond with ONLY a JSON object matching the schema below. No prose before or after the JSON. No markdown code fences. Just the raw JSON object as your final message.
+CRITICAL: Respond with ONLY valid JSON matching the schema below. No prose before or after the JSON. No markdown code fences. Just the raw JSON as your final message.🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| /** | ||
| * Pi community provider — wraps `@mariozechner/pi-coding-agent`'s full | ||
| * coding-agent harness. Each `sendQuery()` call creates a fresh session | ||
|
|
@@ -257,10 +281,26 @@ export class PiProvider implements IAgentProvider { | |
| yield { type: 'system', content: `⚠️ ${modelFallbackMessage}` }; | ||
| } | ||
|
|
||
| // 5. Bridge callback-based events to the async generator contract. | ||
| // 5. Structured output (best-effort). Pi has no SDK-level JSON schema | ||
| // mode the way Claude and Codex do, so we implement it via prompt | ||
| // engineering: append the schema + "JSON only, no fences" instruction, | ||
| // and have the bridge parse the accumulated assistant text on | ||
| // agent_end. Parse failures degrade gracefully — the executor's | ||
| // existing dag.structured_output_missing warning path handles them. | ||
| const outputFormat = requestOptions?.outputFormat; | ||
| const effectivePrompt = outputFormat | ||
| ? augmentPromptForJsonSchema(prompt, outputFormat.schema) | ||
| : prompt; | ||
|
|
||
| // 6. Bridge callback-based events to the async generator contract. | ||
| // bridgeSession owns dispose() and abort wiring. | ||
| try { | ||
| yield* bridgeSession(session, prompt, requestOptions?.abortSignal); | ||
| yield* bridgeSession( | ||
| session, | ||
| effectivePrompt, | ||
| requestOptions?.abortSignal, | ||
| outputFormat?.schema | ||
| ); | ||
| getLog().info({ piProvider: parsed.provider }, 'pi.prompt_completed'); | ||
| } catch (err) { | ||
| getLog().error({ err, piProvider: parsed.provider }, 'pi.prompt_failed'); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate parsed JSON against
jsonSchemabefore attaching it.Line 341 only parses JSON, and Line 343 treats any valid JSON as structured output. If the schema requires
{ area: string }but the model returns{"ok":true}, the executor will suppressdag.structured_output_missingand downstream field refs can silently degrade. Treat schema mismatches like parse failures.Also applies to: 340-343
🤖 Prompt for AI Agents