diff --git a/CHANGELOG.md b/CHANGELOG.md index 1debc8a..3641ee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/index.ts b/src/index.ts index 3e98659..c38a056 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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: [ diff --git a/test/grammar-tool.test.ts b/test/grammar-tool.test.ts new file mode 100644 index 0000000..1f3b843 --- /dev/null +++ b/test/grammar-tool.test.ts @@ -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(); + }); +});