Skip to content

Add opt-in JsonPatchWithAppend for string appends - #171

Open
jeonsworld wants to merge 5 commits into
stefankoegl:masterfrom
jeonsworld:feature/stream-chat
Open

jeonsworld wants to merge 5 commits into
stefankoegl:masterfrom
jeonsworld:feature/stream-chat

Conversation

@jeonsworld

@jeonsworld jeonsworld commented Sep 1, 2025 •

Copy link
Copy Markdown

Adds JsonPatchWithAppend, a drop-in subclass for opt-in string appends, while preserving the standard JsonPatch, module-level helpers, and CLI behavior.

  • Generates suffix-only append operations for growing strings in object members.
  • Uses full op, path, and value fields with existing parsing, serialization, and atomic rollback behavior.
  • Includes regression tests, property tests, documentation, and public type stubs.

Local validation: 311 unit/doctest tests and 26 property tests passed, along with Flake8, Sphinx, distribution checks, and installed-wheel type checks.

- Add new `append` operation as an optional extension to RFC 6902
- Introduce `use_append_ops` parameter to enable/disable append operations
- Support automatic detection of string append scenarios in make_patch()
- Maintain full backward compatibility with RFC 6902 by default
- Enable optimized notation for consecutive append operations in streaming

The append operation is disabled by default to maintain RFC 6902 compliance.
Users can opt-in by setting use_append_ops=True in apply_patch(), make_patch(), and JsonPatch methods.

This feature is particularly useful for chat applications and streaming scenarios where text is appended incrementally, reducing payload size by ~2.8x compared to replace operations.
@jeonsworld

Copy link
Copy Markdown
Author

@stefankoegl This PR is resubmitted with the append operation made optional for RFC 6902 compliance.

@stefankoegl
stefankoegl requested a review from Copilot September 2, 2025 20:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a new append operation to python-json-patch specifically optimized for text streaming in chat applications. When streaming text character-by-character or word-by-word, this reduces payload size by only sending incremental text instead of the entire string with each update.

Key changes include:

  • New AppendOperation class for string concatenation
  • Optional RFC 6902 extension controlled via use_append_ops parameter
  • Smart patch generation that automatically detects append scenarios when enabled

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread jsonpatch.py Outdated
Comment on lines +923 to +1032
else:
self._item_replaced(path, key, dst)
# Check if this is a string append operation (only if append ops are enabled)
if self.use_append_ops and isinstance(src, basestring) and isinstance(dst, basestring) and dst.startswith(
src):
appended_text = dst[len(src):]
if appended_text: # Only create append op if there's actual text to append
self.insert(AppendOperation({
'op': 'append',
'path': _path_join(path, key),
'value': appended_text,
}, pointer_cls=self.pointer_cls))
else:
self._item_replaced(path, key, dst)
else:
self._item_replaced(path, key, dst)

Copilot AI Sep 2, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic flow is incorrect - there's an unnecessary else: block at line 1018 that creates unreachable code. The else: clause at line 1031 will never execute because it's nested inside the outer else: block, making the string append detection logic unreachable in most cases.

Copilot uses AI. Check for mistakes.
Comment thread jsonpatch.py Outdated
raise JsonPatchConflict("Cannot append to root document")

try:
if isinstance(subobj[part], basestring):

Copilot AI Sep 2, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The append operation should validate that the 'value' parameter is a string before attempting concatenation. Currently, if 'value' is not a string, the += operation could succeed but produce unexpected results (e.g., concatenating a list to a string).

Suggested change
if isinstance(subobj[part], basestring):
if isinstance(subobj[part], basestring):
if not isinstance(self.operation['value'], basestring):
raise JsonPatchConflict("Cannot append non-string value to string")

Copilot uses AI. Check for mistakes.
@stefankoegl

Copy link
Copy Markdown
Owner

I don't like to idea of mixing in features that do not appear in RFC 6902 in the main interfaces. Instead I would prefer a solution that would keep them separate, eg a JsonPatchWithAppend class that would be a drop-in replacement for JsonPatch but with added support for append.

What do you think?

@jeonsworld jeonsworld changed the title Add append operation for efficient text streaming Add opt-in JsonPatchWithAppend for string appends Nov 6, 2026
@jeonsworld

Copy link
Copy Markdown
Author

@stefankoegl Updated the PR to introduce JsonPatchWithAppend as suggested, keeping the standard APIs unchanged.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants