Edit Artifacts with LLM-Powered JSON Patching
LlamaIndex tool letting an agent create and iteratively edit a structured, Pydantic-modeled artifact in-memory.
Why it matters
Empower LLMs and agents to programmatically create, modify, and iterate on complex artifacts like reports and code using Pydantic models and JSON patch operations.
Outcomes
What it gets done
Define artifact structure using Pydantic models.
Apply JSON patch operations for in-memory artifact editing.
Integrate with LLM agents for iterative content generation.
Store and inject artifacts into agent memory.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/li-tool-tools-artifact-editor | bash Overview
Artifact Editor Tool Spec
ArtifactEditorToolSpec lets an agent create and iteratively edit a Pydantic-modeled artifact (a report, code, or similar structured content) in-memory using JSON patch operations, with ArtifactMemoryBlock keeping the artifact in the agent's memory across turns. Use it when an agent needs to build and revise a structured document over a multi-turn conversation. It requires modeling the artifact's shape as a Pydantic model.
What it does
ArtifactEditorToolSpec is a stateful tool spec that lets an agent edit an artifact in-memory using JSON patch operations. An LLM/Agent can be prompted to create, modify, and iterate on an artifact -- a report, code, or anything representable as a Pydantic model -- and the current state is retrievable via the tool spec's get_current_artifact() method. The package also includes ArtifactMemoryBlock, which stores the artifact and injects it into the LLM/Agent's memory so it stays available across turns.
When to use - and when NOT to
Use it when you want an agent to build and iteratively edit a structured document over a multi-turn conversation -- for example generating a report made of text, table, and image blocks, then asking the agent to rearrange or revise specific parts of it. It is built around Pydantic models representing the artifact's structure, so it is not a fit for freeform or unstructured content that does not map cleanly to a schema.
Inputs and outputs
Install with:
pip install llama-index-tools-artifact-editor
Define the artifact's shape as a Pydantic model -- the example uses a Report made of TextBlock (a content string), TableBlock (headers and rows), and ImageBlock (an image_url) variants -- then wire it into an agent:
tool_spec = ArtifactEditorToolSpec(Report)
tools = tool_spec.to_tool_list()
memory = Memory.from_defaults(
session_id="artifact_editor_01",
memory_blocks=[ArtifactMemoryBlock(artifact_spec=tool_spec)],
token_limit=60000,
chat_history_token_ratio=0.7,
)
agent = FunctionAgent(
tools=tools,
llm=OpenAI(model="o3-mini"),
system_prompt="You are an expert in writing reports. When you write a report, I will be able to see it (and also any changes you make to it!), so no need to repeat it back to me once its written.",
)
The example configures Memory with a 60,000-token limit and a 0.7 chat-history-to-artifact token ratio, controlling how much of that budget goes to conversation history versus the artifact itself. From there, a chat loop (typing "exit" or "quit" to stop) lets you prompt the agent -- "Create a ficticous report about the history of the internet," then "Move the image to the top of the report" -- while streaming the response as it's generated and printing which tool is called with what arguments as the artifact is edited. The artifact itself updates in-memory each turn, accessible via tool_spec.get_current_artifact().
Who it's for
Developers building agents that need to produce and iteratively revise a structured document -- reports, code, or any Pydantic-modeled content -- over a conversation, rather than regenerating the whole thing from scratch on every edit.
Source README
Artifact Editor Tool Spec
pip install llama-index-tools-artifact-editor
The ArtifactEditorToolSpec is a stateful tool spec that allows you to edit an artifact in-memory.
Using JSON patch operations, an LLM/Agent can be prompted to create, modify, and iterate on an artifact like a report, code, or anything that can be represented as a Pydantic model.
The tool package also includes an ArtifactMemoryBlock that can be used to store the artifact and inject it into the LLM/Agent's memory.
Usage
Below is an example of how to use the ArtifactEditorToolSpec and ArtifactMemoryBlock to create and iterate on a report.
import asyncio
from pydantic import BaseModel, Field
from typing import List, Literal, Optional, Any
from llama_index.core.agent.workflow import (
FunctionAgent,
AgentStream,
ToolCallResult,
)
from llama_index.core.memory import Memory
from llama_index.tools.artifact_editor import (
ArtifactEditorToolSpec,
ArtifactMemoryBlock,
)
from llama_index.llms.openai import OpenAI
### Define the Artifact Pydantic Model
class TextBlock(BaseModel):
type: Literal["text"] = "text"
content: str = Field(description="The content of the text block")
class TableBlock(BaseModel):
type: Literal["table"] = "table"
headers: List[str] = Field(description="The headers of the table")
rows: List[List[str]] = Field(description="The rows of the table")
class ImageBlock(BaseModel):
type: Literal["image"] = "image"
image_url: str = Field(description="The URL of the image")
class Report(BaseModel):
"""Creates an instance of a report, which is a collection of text, tables, and images."""
title: str = Field(description="The title of the report")
content: List[TextBlock | TableBlock | ImageBlock] = Field(
description="The content of the report"
)
### Initialize the tool spec and tools
tool_spec = ArtifactEditorToolSpec(Report)
tools = tool_spec.to_tool_list()
### Initialize the memory
memory = Memory.from_defaults(
session_id="artifact_editor_01",
memory_blocks=[ArtifactMemoryBlock(artifact_spec=tool_spec)],
token_limit=60000,
chat_history_token_ratio=0.7,
)
### Create the agent
agent = FunctionAgent(
tools=tools,
llm=OpenAI(model="o3-mini"),
system_prompt="You are an expert in writing reports. When you write a report, I will be able to see it (and also any changes you make to it!), so no need to repeat it back to me once its written.",
)
### Run the agent in a basic chat loop
### As it runs, the artifact will be updated in-memory and
### can be accessed via the `get_current_artifact` method.
async def main():
while True:
user_msg = input("User: ").strip()
if user_msg.lower() in ["exit", "quit"]:
break
handler = agent.run(user_msg, memory=memory)
async for ev in handler.stream_events():
if isinstance(ev, AgentStream):
print(ev.delta, end="", flush=True)
elif isinstance(ev, ToolCallResult):
print(
f"\n\nCalling tool: {ev.tool_name} with kwargs: {ev.tool_kwargs}"
)
response = await handler
print(str(response))
print("Current artifact: ", tool_spec.get_current_artifact())
if __name__ == "__main__":
asyncio.run(main())
When running this, you might initially ask the agent:
User: Create a ficticous report about the history of the internet
And you will get a report with a list of blocks. Try asking it to modify the report!
User: Move the image to the top of the report
And you will get a report with the image moved to the top.
Check out the documentation for more example on agents, memory, and tools.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.