Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Recognize indented file headers like Codex, and reject any other line between file sections instead of silently dropping that section while reporting success (#45).
- Preserve each line's ending on update: CRLF and mixed-ending files are no longer rewritten to LF, inserted lines take the file's line ending, and context lines keep their exact text (#47).
- Declare the Codex Lark grammar through pi's `constrainedSampling` hook, so GPT models on providers with OpenAI grammar tools get `apply_patch` as a native grammar tool instead of a plain JSON function (#43, thanks @zidou-kiyn).

### Changed

Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,10 @@ export function createApplyPatchTool(): ApplyPatchToolDefinition {
label: "ApplyPatch",
description: APPLY_PATCH_FREEFORM_DESCRIPTION,
parameters: APPLY_PATCH_PARAMS,
// Pi's public grammar hook: models whose provider sets `compat.supportsOpenAIGrammarTools`
// receive apply_patch as a native OpenAI custom tool constrained by the Codex Lark grammar.
// Other providers fall back to the plain function tool.
constrainedSampling: { type: "grammar", variants: { openai_lark: APPLY_PATCH_LARK_GRAMMAR } },
prepareArguments: normalizeApplyPatchArguments,
promptSnippet: "Apply Codex-format file patches with apply_patch",
promptGuidelines: [
Expand Down
29 changes: 29 additions & 0 deletions test/grammar-tool.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { resolveGrammarConstrainedSampling } from "@earendil-works/pi-ai/api/constrained-sampling";
import { describe, expect, it } from "vitest";
import { APPLY_PATCH_LARK_GRAMMAR, createApplyPatchTool } from "../src/index.js";

describe("apply_patch grammar tool", () => {
it("#given a provider with OpenAI grammar tools #when pi prepares apply_patch #then it is sent as the Codex Lark grammar tool", () => {
// given
const tool = createApplyPatchTool();

// when
const grammar = resolveGrammarConstrainedSampling(tool, true);

// then
expect(grammar?.format).toBe("lark");
expect(grammar?.definition).toBe(APPLY_PATCH_LARK_GRAMMAR);
expect(grammar?.inputProperty).toBe("input");
});

it("#given a provider without grammar tools #when pi prepares apply_patch #then it stays a plain function tool", () => {
// given
const tool = createApplyPatchTool();

// when
const grammar = resolveGrammarConstrainedSampling(tool, false);

// then
expect(grammar).toBeUndefined();
});
});
Loading