SPB Git

Repository Public

tools-apply-patch.md 18.3 KB 583 lines markdown

1# Apply Patch23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45The `apply_patch` tool lets GPT-5.1 create, update, and delete files in your codebase using structured diffs. Instead of just suggesting edits, the model emits patch operations that your application applies and then reports back on, enabling iterative, multi-step code editing workflows.67## When to use89Some common scenarios where you would use apply_patch:1011- **Multi-file refactors** – Rename symbols, extract helpers, or reorganize modules across many files at once.12- **Bug fixes** – Have the model both diagnose issues and emit precise patches.13- **Tests & docs generation** – Create new test files, fixtures, and documentation alongside code changes.14- **Migrations & mechanical edits** – Apply repetitive, structured updates (API migrations, type annotations, formatting fixes, etc.).1516If you can describe your repo and desired change in text, apply_patch can usually generate the corresponding diffs.1718## Use apply patch tool with Responses API1920At a high level, using `apply_patch` with the Responses API looks like this:21221. **Call the Responses API with the `apply_patch` tool**23   - Provide the model with context about available files (or a summary) in your `input`, or give the model tools for exploring your file system.24   - Enable the tool with `tools=[{"type": "apply_patch"}]`.252. **Let the model return one or more patch operations**26   - The Response output includes one or more `apply_patch_call` objects.27   - Each call describes a single file operation: create, update, or delete.283. **Apply patches in your environment**29   - Run a patch harness or script that:30     - Interprets the `operation` diff for each `apply_patch_call`.31     - Applies the patch to your working directory or repo.32     - Records whether each patch succeeded and any logs or error messages.334. **Report patch results back to the model**34   - Call the Responses API again, either with `previous_response_id` or by passing back your conversation items into `input`.35   - Include an `apply_patch_call_output` event for each `call_id`, with a `status` and optional `output` string.36   - Keep `tools=[{"type": "apply_patch"}]` so the model can continue editing if needed.375. **Let the model continue or explain changes**38   - The model may issue more `apply_patch_call` operations, or39   - Provide a human-facing explanation of what it changed and why.4041## Example: Renaming a function with Apply Patch Tool4243**Step 1: Ask the model to plan and emit patches**4445Ask the model to plan and emit patches4647```javascript48const response = await client.responses.create({49  model: "gpt-6-astra",50  input: fileContext,51  tools: [{ type: "apply_patch" }],52});5354const patchCalls = response.output.filter(55  (item) => item.type === "apply_patch_call"56);57```5859```python60from openai import OpenAI6162client = OpenAI()6364# For brevity, we are including file context in the example input.65# Most agentic use cases should instead equip the model with tools66# for exploring file system state.67RESPONSE_INPUT = """68The user has the following files:69<BEGIN_FILES>70===== lib/fib.py71def fib(n):72    if n <= 1:73        return n74    return fib(n-1) + fib(n-2)7576===== run.py77from lib.fib import fib7879def main():80  print(fib(42))81<END_FILES>8283You are a helpful coding assistant that should assist the user with whatever they84ask.8586User query:87Help me rename the fib() function to fibonacci()88"""8990response = client.responses.create(91    model="gpt-6-astra",92    input=RESPONSE_INPUT,93    tools=[{"type": "apply_patch"}],94)9596# response.output may contain multiple apply_patch_call entries, e.g.:97# - update lib/fib.py98# - update run.py99patch_calls = [100    item.model_dump() for item in response.output if item.type == "apply_patch_call"101]102```103104```go105response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{106	Model: "gpt-6-astra",107	Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(responseInput)},108	Tools: []responses.ToolUnionParam{{OfApplyPatch: &responses.ApplyPatchToolParam{}}},109})110if err != nil {111	panic(err)112}113patchCalls := make([]responses.ResponseOutputItemUnion, 0)114for _, item := range response.Output {115	if item.Type == "apply_patch_call" {116		patchCalls = append(patchCalls, item)117	}118}119```120121```java122import com.openai.client.OpenAIClient;123import com.openai.client.okhttp.OpenAIOkHttpClient;124import com.openai.models.responses.ApplyPatchTool;125import com.openai.models.responses.ResponseCreateParams;126127ResponseCreateParams params =128    ResponseCreateParams.builder()129        .model("gpt-6-astra")130        .input(131            "Rename fib() to fibonacci() in lib/fib.py and update run.py to use the new name.")132        .addTool(ApplyPatchTool.builder().build())133        .build();134135client.responses().create(params).output().stream()136    .flatMap(item -> item.applyPatchCall().stream())137    .forEach(System.out::println);138```139140```ruby141require "openai"142143client = OpenAI::Client.new144response = client.responses.create(145  model: "gpt-6-astra",146  input: "Rename fib() to fibonacci() in lib/fib.py and update run.py to use the new name.",147  tools: [{ type: :apply_patch }]148)149150patch_calls = response.output.select { |item| item.type == :apply_patch_call }151puts(patch_calls)152```153154155**Example `apply_patch_call` object**156157Example apply_patch_call object158159```json160{161    "id": "apc_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe",162    "type": "apply_patch_call",163    "status": "completed",164    "call_id": "call_Rjsqzz96C5xzPb0jUWJFRTNW",165    "operation": {166        "type": "update_file",167        "diff": "168@@169-def fib(n):170+def fibonacci(n):171    if n <= 1:172        return n173-    return fib(n-1) + fib(n-2)                                                  +    return fibonacci(n-1) + fibonacci(n-2),174",175        "path": "lib/fib.py"176    }177}178```179180181**Step 2: Apply the patch and send results back**182183Apply the patch and return results184185```javascript186const results = patchCalls.map((call) => {187  const { success, output } = applyOperation(call.operation);188189  return {190    type: "apply_patch_call_output",191    call_id: call.call_id,192    status: success ? "completed" : "failed",193    output,194  };195});196197const followup = await client.responses.create({198  model: "gpt-6-astra",199  previous_response_id: response.id,200  input: results,201  tools: [{ type: "apply_patch" }],202});203204console.log(followup.output_text);205```206207```python208from apply_patch_harness import apply_operation  # your implementation209210results = []211for call in patch_calls:212    op = call["operation"]213    success, maybe_log_output = apply_operation(op)214215    results.append(216        {217            "type": "apply_patch_call_output",218            "call_id": call["call_id"],219            "status": "completed" if success else "failed",220            "output": maybe_log_output,221        }222    )223224followup = client.responses.create(225    model="gpt-6-astra",226    previous_response_id=response.id,227    input=results,228    tools=[{"type": "apply_patch"}],229)230```231232```go233results := make(responses.ResponseInputParam, 0, len(patchCalls))234for _, call := range patchCalls {235	success, logOutput := applyOperation(call.Operation)236	status := "completed"237	if !success {238		status = "failed"239	}240	result := responses.ResponseInputItemParamOfApplyPatchCallOutput(call.CallID, status)241	result.OfApplyPatchCallOutput.Output = openai.String(logOutput)242	results = append(results, result)243}244_, err = client.Responses.New(context.Background(), responses.ResponseNewParams{245	Model:              "gpt-6-astra",246	PreviousResponseID: openai.String(response.ID),247	Input:              responses.ResponseNewParamsInputUnion{OfInputItemList: results},248	Tools:              []responses.ToolUnionParam{{OfApplyPatch: &responses.ApplyPatchToolParam{}}},249})250if err != nil {251	panic(err)252}253```254255```java256import com.openai.client.OpenAIClient;257import com.openai.client.okhttp.OpenAIOkHttpClient;258import com.openai.models.responses.ApplyPatchTool;259import com.openai.models.responses.ResponseCreateParams;260import com.openai.models.responses.ResponseInputItem;261import java.util.List;262263ResponseCreateParams params =264    ResponseCreateParams.builder()265        .model("gpt-6-astra")266        .inputOfResponse(267            List.of(268                ResponseInputItem.ofApplyPatchCallOutput(269                    ResponseInputItem.ApplyPatchCallOutput.builder()270                        .callId(System.getenv("OPENAI_EXAMPLE_APPLY_PATCH_CALL_ID"))271                        .status(ResponseInputItem.ApplyPatchCallOutput.Status.COMPLETED)272                        .output("Patch applied successfully.")273                        .build())))274        .previousResponseId(System.getenv("OPENAI_EXAMPLE_PREVIOUS_RESPONSE_ID"))275        .addTool(ApplyPatchTool.builder().build())276        .build();277278client.responses().create(params).output().stream()279    .flatMap(item -> item.message().stream())280    .flatMap(message -> message.content().stream())281    .flatMap(content -> content.outputText().stream())282    .forEach(text -> System.out.println(text.text()));283```284285```ruby286require "openai"287288client = OpenAI::Client.new289response_id = ENV.fetch("OPENAI_RESPONSE_ID")290patch_call_id = ENV.fetch("OPENAI_APPLY_PATCH_CALL_ID")291response = client.responses.create(292  model: "gpt-6-astra",293  previous_response_id: response_id,294  input: [295    {296      type: :apply_patch_call_output,297      call_id: patch_call_id,298      status: :completed,299      output: "Patch applied successfully."300    }301  ],302  tools: [{ type: :apply_patch }]303)304305puts(response.output_text)306```307308309If a patch fails (for example, file not found), set `status: "failed"` and include a helpful `output` string so the model can recover:310311Report a failed apply_patch call312313```json314{315  "type": "apply_patch_call_output",316  "call_id": "call_cNWm41dB3RyQcLNOVTIPBWZU",317  "status": "failed",318  "output": "Could not apply patch to lib/foo.py — file not found on disk"319}320```321322323## Apply patch operations324325| Operation Type | Purpose                            | Payload                                                          |326| -------------- | ---------------------------------- | ---------------------------------------------------------------- |327| `create_file`  | Create a new file at `path`.       | `diff` is a V4A diff representing the full file contents.        |328| `update_file`  | Modify an existing file at `path`. | `diff` is a V4A diff with additions, deletions, or replacements. |329| `delete_file`  | Remove a file at `path`.           | No `diff`; delete the file entirely.                             |330331Your patch harness is responsible for interpreting the V4A diff format and applying changes. For reference implementations, see the [Python Agents SDK](https://github.com/openai/openai-agents-python/blob/main/src/agents/apply_diff.py) or [TypeScript Agents SDK](https://github.com/openai/openai-agents-js/blob/main/packages/agents-core/src/utils/applyDiff.ts) code.332333## Implementing the patch harness334335When using the `apply_patch` tool, you don’t provide an input schema; the model knows how to construct `operation` objects. Your job is to:3363371. **Parse operations from the Response**338   - Scan the Response for items with `type: "apply_patch_call"`.339   - For each call, inspect `operation.type`, `operation.path`, and any potential `diff`.3402. **Apply file operations**341   - For `create_file` and `update_file`, apply the V4A diff to the file system or in-memory workspace.342   - For `delete_file`, remove the file at `path`.343   - Record whether each operation succeeded and any logs or error messages.3443. **Return `apply_patch_call_output` events**345   - For each `call_id`, emit exactly one `apply_patch_call_output` event with:346     - `status: "completed"` if the operation was applied successfully.347     - `status: "failed"` if you encountered an error (include a short human-readable `output` string).348349### Safety and robustness350351- **Path validation**: Prevent directory traversal and restrict edits to allowed directories.352- **Backups**: Consider backing up files (or working in a scratch copy) before applying patches.353- **Error handling**: Always return a `failed` status with an informative `output` string when patches cannot be applied.354- **Atomicity**: Decide whether you want “all-or-nothing” semantics (rollback if any patch fails) or per-file success/failure.355356## Use the apply patch tool with the Agents SDK357358Alternatively, you can use the [Agents SDK](https://developers.openai.com/api/docs/guides/tools#usage-in-the-agents-sdk) to use the apply patch tool. You'll still have to implement the harness that handles the actual file operations but you can use the `applyDiff` function to handle the diff processing.359360Use the apply patch tool with the Agents SDK361362```javascript363import { applyDiff, Agent, run, applyPatchTool } from "@openai/agents";364365class WorkspaceEditor {366  async createFile(operation) {367    // convert the diff to the file content368    const content = applyDiff("", operation.diff, "create");369    // write the file content to the file system370    return { status: "completed", output: `Created ${operation.path}` };371  }372373  async updateFile(operation) {374    // read the file content from the file system375    const current = "";376    // convert the diff to the new file content377    const newContent = applyDiff(current, operation.diff);378    // write the updated file content to the file system379    return { status: "completed", output: `Updated ${operation.path}` };380  }381382  async deleteFile(operation) {383    // delete the file from the file system384    return { status: "completed", output: `Deleted ${operation.path}` };385  }386}387388const editor = new WorkspaceEditor();389390const agent = new Agent({391  name: "Patch Assistant",392  model: "gpt-6-astra",393  instructions:394    "You can edit files inside the /tmp directory using the apply_patch tool.",395  tools: [396    applyPatchTool({397      editor,398      // could also be a function for you to determine if approval is needed399      needsApproval: true,400      onApproval: async (_ctx, _approvalItem) => {401        // create your own approval logic402        return { approve: true };403      },404    }),405  ],406});407408const result = await run(409  agent,410  "Create tasks.md with a shopping checklist of 5 entries."411);412413console.log(`\nFinal response:\n${result.finalOutput}`);414```415416```python417from agents import Agent, ApplyPatchTool, Runner, apply_diff418419420class WorkspaceEditor:421    async def create_file(self, operation):422        # convert the diff to the file content423        content = apply_diff("", operation.diff, mode="create")424        # write the file content to the file system425        return {"status": "completed", "output": f"Created {operation.path}"}426427    async def update_file(self, operation):428        # read the file content from the file system429        current = ""430        # convert the diff to the new file content431        new_content = apply_diff(current, operation.diff)432        # write the updated file content to the file system433        return {"status": "completed", "output": f"Updated {operation.path}"}434435    async def delete_file(self, operation):436        # delete the file from the file system437        return {"status": "completed", "output": f"Deleted {operation.path}"}438439440editor = WorkspaceEditor()441442agent = Agent(443    name="Patch Assistant",444    model="gpt-6-astra",445    instructions="You can edit files inside the /tmp directory using the apply_patch tool.",446    tools=[447        ApplyPatchTool(448            editor=editor,449            # could also be a function for you to determine if approval is needed450            needs_approval=True,451            # Implement your own approval logic452            on_approval=lambda _ctx, _approval_item: {"approve": True},453        ),454    ],455)456457458async def main():459    result = await Runner.run(460        agent,461        input="Create tasks.md with a shopping checklist of 5 entries.",462    )463464    print(f"\nFinal response:\n{result.final_output}")465466467if __name__ == "__main__":468    import asyncio469470    asyncio.run(main())471```472473474You can find full working examples on GitHub.475476[Apply patch tool example - TypeScript477478479480      Example of how to use the apply patch tool with the Agents SDK in TypeScript](https://github.com/openai/openai-agents-js/blob/main/examples/tools/applyPatch.ts)481482[Apply patch tool example - Python483484485486      Example of how to use the apply patch tool with the Agents SDK in Python](https://github.com/openai/openai-agents-python/blob/main/examples/tools/apply_patch.py)487488## Handling common errors489490Use `status: "failed"` plus a clear `output` message to help the model recover.491492493494File not found495496    File not found error497498```json499{500  "type": "apply_patch_call_output",501  "call_id": "call_abc",502  "status": "failed",503  "output": "Error: File not found at path 'lib/baz.py'"504}505```506507  508509  510511    512Patch conflict513514    Patch conflict error515516```json517{518  "type": "apply_patch_call_output",519  "call_id": "call_abc",520  "status": "failed",521  "output": "Error: Invalid Context:\n@@ def fib(n):"522}523```524525526527The model can then adjust future diffs (for example, by re-reading a file in your prompt or simplifying a change) based on these error messages.528529## Best practices530531- **Give clear file context**532  - When you call the Responses API, include either an inline snapshot of your files (as in the example), or give the model tools for exploring your filesystem (like the `shell` tool).533- **Consider using with the `shell` tool**534  - When used in conjunction with the `shell` tool, the model can explore file system directories, read files, and grep for keywords, enabling agentic file discovery and editing.535- **Encourage small, focused diffs**536  - In your system instructions, nudge the model toward minimal, targeted edits rather than huge rewrites.537- **Make sure changes apply cleanly**538  - After a series of patches, run your tests or linters and share failures back in the next `input` so the model can fix them.539540## Usage notes541542<table>543<tbody>544545<tr>546  <th>API Availability</th>547  <th>Supported models</th>548</tr>549550<tr>551  <td>552    553554      [Responses](https://developers.openai.com/api/reference/resources/responses)555    556557    558559      [Chat Completions](https://developers.openai.com/api/reference/resources/chat)560    561562    563564      [Assistants](https://developers.openai.com/api/reference/resources/beta/subresources/assistants)565    566567  </td>568  <td style={{ maxWidth: "150px" }}>569    [GPT-5.5](https://developers.openai.com/api/docs/models/gpt-5.5)570    571572    [GPT-5.4](https://developers.openai.com/api/docs/models/gpt-5.4)573    574575    [GPT-5.2](https://developers.openai.com/api/docs/models/gpt-5.2)576    577578    [GPT-5.1](https://developers.openai.com/api/docs/models/gpt-5.1)579  </td>580</tr>581582</tbody>583</table>
Files main
Menu

Theme