arcade-google-docs 4.0.0__py3-none-any.whl → 4.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,7 @@ from arcade_google_docs.tools.search import (
12
12
  search_and_retrieve_documents,
13
13
  search_documents,
14
14
  )
15
+ from arcade_google_docs.tools.system_context import who_am_i
15
16
  from arcade_google_docs.tools.update import insert_text_at_end_of_document
16
17
 
17
18
  __all__ = [
@@ -24,4 +25,5 @@ __all__ = [
24
25
  "search_and_retrieve_documents",
25
26
  "search_documents",
26
27
  "generate_google_file_picker_url",
28
+ "who_am_i",
27
29
  ]
@@ -0,0 +1,56 @@
1
+ from typing import Annotated
2
+
3
+ from arcade_tdk import ToolContext, tool
4
+ from arcade_tdk.auth import Google
5
+ from openai import OpenAI
6
+
7
+ from arcade_google_docs.tools.edit_agent.executor import execute_plan
8
+ from arcade_google_docs.tools.edit_agent.models.planning import ReasoningEffort
9
+ from arcade_google_docs.tools.edit_agent.planner import plan_edits
10
+ from arcade_google_docs.tools.edit_agent.progress_tracker import ExecutionProgressTracker
11
+ from arcade_google_docs.tools.edit_agent.utils import get_docmd
12
+ from arcade_google_docs.utils import build_docs_service
13
+
14
+
15
+ @tool(
16
+ requires_auth=Google(
17
+ scopes=[
18
+ "https://www.googleapis.com/auth/drive.file",
19
+ ],
20
+ ),
21
+ requires_secrets=["OPENAI_API_KEY"],
22
+ )
23
+ async def edit_document(
24
+ context: ToolContext,
25
+ document_id: Annotated[str, "The ID of the document to edit"],
26
+ edit_requests: Annotated[
27
+ list[str],
28
+ "A list of natural language descriptions of the desired change(s) to the document. "
29
+ "Each entry should be a single, self-contained edit request that can be fully understood "
30
+ "independently. Note: Each request may result in zero, one, or multiple actual edits "
31
+ "depending on what changes are needed (e.g., a request might be ignored if the change "
32
+ "already exists in the document).",
33
+ ],
34
+ reasoning_effort: Annotated[
35
+ ReasoningEffort, "The effort to put into reasoning about the edit(s). Defaults to medium"
36
+ ] = ReasoningEffort.MEDIUM,
37
+ ) -> Annotated[dict, "The edited document's title, documentId, and documentUrl in a dictionary"]:
38
+ """
39
+ Edit a Google Docs document with the specified edit request.
40
+
41
+ This tool does not have context about previous edits because it is stateless. If your edit
42
+ request depends on knowledge about previous edits, then you should provide that context in
43
+ the edit requests.
44
+ """
45
+ progress = ExecutionProgressTracker()
46
+ progress.add(f"Starting to create a plan with '{reasoning_effort.value}' reasoning effort")
47
+
48
+ openai_client = OpenAI(api_key=context.get_secret("OPENAI_API_KEY"))
49
+ google_service = build_docs_service(context.get_auth_token_or_empty())
50
+ docmd = get_docmd(google_service, document_id)
51
+
52
+ plan = await plan_edits(openai_client, docmd, edit_requests, reasoning_effort, progress)
53
+
54
+ result = await execute_plan(openai_client, google_service, docmd, document_id, plan, progress)
55
+
56
+ return result
@@ -0,0 +1,103 @@
1
+ """Execution service for the edit agent."""
2
+
3
+ from typing import Any
4
+
5
+ from arcade_tdk.errors import ToolExecutionError
6
+ from openai import OpenAI
7
+
8
+ from arcade_google_docs.docmd import DocMD
9
+ from arcade_google_docs.tools.edit_agent.models.planning import Plan
10
+ from arcade_google_docs.tools.edit_agent.progress_tracker import ExecutionProgressTracker
11
+ from arcade_google_docs.tools.edit_agent.request_generator import generate_api_request_for_step
12
+ from arcade_google_docs.tools.edit_agent.utils import get_docmd
13
+
14
+
15
+ async def execute_plan(
16
+ openai_client: OpenAI,
17
+ google_service: Any,
18
+ docmd: DocMD,
19
+ document_id: str,
20
+ plan: Plan,
21
+ progress: ExecutionProgressTracker,
22
+ ) -> dict:
23
+ """
24
+ Execute a plan step by step.
25
+
26
+ Returns a dictionary with responses, edit_requests, and execution_logs.
27
+ """
28
+ if plan.number_of_steps == 0:
29
+ progress.add("I was unable to come up with a plan to edit the document")
30
+ return {
31
+ "responses": [],
32
+ "edit_requests": [],
33
+ "execution_logs": progress.get_messages(),
34
+ }
35
+
36
+ progress.add(
37
+ f"I have a {plan.number_of_steps} step plan where I will perform "
38
+ f"{plan.number_of_edit_requests} edit(s)"
39
+ )
40
+ progress.add(f"I need to execute the following plan:\n{plan.to_log_string()}")
41
+
42
+ progress.add("Starting to execute on the plan...")
43
+ all_google_edit_responses = []
44
+ all_requests = []
45
+
46
+ # Execute the plan step by step. Within each step, execute all items in parallel
47
+ for step_num, step in enumerate(plan.steps):
48
+ progress.add_step_start(step_num, len(plan.steps))
49
+
50
+ # Refresh document state before each step (except the first one)
51
+ if step_num > 0:
52
+ docmd = get_docmd(google_service, document_id)
53
+
54
+ step_requests = await generate_api_request_for_step(
55
+ openai_client,
56
+ docmd,
57
+ step,
58
+ )
59
+
60
+ error: str | None = None
61
+ failed_requests = step_requests
62
+
63
+ for attempt in range(2):
64
+ try:
65
+ if attempt == 1:
66
+ # Regenerate on retry with error context
67
+ step_requests = await generate_api_request_for_step(
68
+ openai_client,
69
+ docmd,
70
+ step,
71
+ previous_error=error,
72
+ failed_requests=failed_requests,
73
+ )
74
+
75
+ # All requests in the step are sent in a single batchUpdate call
76
+ if step_requests:
77
+ google_edit_response = (
78
+ google_service.documents()
79
+ .batchUpdate(documentId=document_id, body={"requests": step_requests})
80
+ .execute()
81
+ )
82
+ all_google_edit_responses.append(google_edit_response)
83
+ all_requests.extend(step_requests)
84
+
85
+ progress.add_step_success(step_num, len(plan.steps))
86
+ break
87
+ except Exception as e:
88
+ progress.add_step_error(step_num, len(plan.steps), e)
89
+ if attempt == 0:
90
+ error = str(e)
91
+ failed_requests = step_requests
92
+ progress.add_step_retry(step_num, len(plan.steps))
93
+ continue
94
+ raise ToolExecutionError(
95
+ message=f"Failed to execute step {step_num + 1} after 2 attempts",
96
+ developer_message="\n".join(progress.get_messages()),
97
+ )
98
+
99
+ return {
100
+ "responses": all_google_edit_responses,
101
+ "edit_requests": all_requests,
102
+ "execution_logs": progress.get_messages(),
103
+ }
@@ -0,0 +1,89 @@
1
+ from enum import Enum
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+ from arcade_google_docs.models.requests import EditRequestType
6
+
7
+
8
+ class ReasoningEffort(str, Enum):
9
+ MINIMAL = "minimal"
10
+ LOW = "low"
11
+ MEDIUM = "medium"
12
+ HIGH = "high"
13
+
14
+ def minimum_number_of_thoughts(self) -> int:
15
+ return {
16
+ ReasoningEffort.MINIMAL: 0,
17
+ ReasoningEffort.LOW: 1,
18
+ ReasoningEffort.MEDIUM: 2,
19
+ ReasoningEffort.HIGH: 3,
20
+ }[self]
21
+
22
+
23
+ class EditItem(BaseModel):
24
+ """A single edit request with its type and associated thoughts"""
25
+
26
+ edit_request_type: EditRequestType = Field(
27
+ ..., title="The type of edit request to be made to the document"
28
+ )
29
+ edit_instruction: str = Field(
30
+ ..., title="Natural language description of the desired change to the document"
31
+ )
32
+ thoughts: list[str] = Field(
33
+ ..., title="Thoughts that led to identifying this specific edit request"
34
+ )
35
+
36
+
37
+ class Step(BaseModel):
38
+ """A step containing one or more edit requests to be executed together"""
39
+
40
+ model_config = ConfigDict(
41
+ title="Step for Document Editing",
42
+ json_schema_extra={
43
+ "description": "A step containing edit requests that can be executed together",
44
+ "examples": [
45
+ {
46
+ "edit_items": [
47
+ {
48
+ "edit_request_type": "updateTextStyle",
49
+ "edit_instruction": "Make 'Performance' bold in the objectives section",
50
+ "thoughts": [
51
+ "(thought) I am asked to make 'Performance' bold in the "
52
+ "objectives section",
53
+ "(thought) I need to find where 'Performance' appears",
54
+ "(thought) 'Performance' is at index 150-161 in the document",
55
+ "(thought) I'll use updateTextStyle to make it bold",
56
+ ],
57
+ },
58
+ ],
59
+ }
60
+ ],
61
+ },
62
+ )
63
+ edit_items: list[EditItem] = Field(
64
+ ..., title="List of edit requests to be executed in this step"
65
+ )
66
+
67
+
68
+ class Plan(BaseModel):
69
+ """The final ordered plan with proper dependencies"""
70
+
71
+ steps: list[Step] = Field(
72
+ ..., title="Ordered list of steps, each containing one or more edit requests"
73
+ )
74
+
75
+ @property
76
+ def number_of_steps(self) -> int:
77
+ return len(self.steps)
78
+
79
+ @property
80
+ def number_of_edit_requests(self) -> int:
81
+ return sum(len(step.edit_items) for step in self.steps)
82
+
83
+ def to_log_string(self) -> str:
84
+ log_string = ""
85
+ for step_num, step in enumerate(self.steps):
86
+ log_string += f"Step {step_num + 1}:\n"
87
+ for edit_item in step.edit_items:
88
+ log_string += f" - {edit_item.edit_instruction}\n"
89
+ return log_string
@@ -0,0 +1,130 @@
1
+ import asyncio
2
+
3
+ from arcade_tdk.errors import ToolExecutionError
4
+ from openai import OpenAI
5
+
6
+ from arcade_google_docs.docmd import DocMD
7
+ from arcade_google_docs.tools.edit_agent.models.planning import (
8
+ Plan,
9
+ ReasoningEffort,
10
+ Step,
11
+ )
12
+ from arcade_google_docs.tools.edit_agent.progress_tracker import ExecutionProgressTracker
13
+ from arcade_google_docs.tools.edit_agent.prompts import PLAN_EDIT_DOCUMENT_SYSTEM_PROMPT
14
+
15
+
16
+ async def _generate_step_for_user_edit_request(
17
+ openai_client: OpenAI,
18
+ docmd: DocMD,
19
+ user_edit_request: str,
20
+ query_index: int,
21
+ reasoning_effort: ReasoningEffort,
22
+ progress: ExecutionProgressTracker,
23
+ ) -> Step:
24
+ """Generate a step for a single user edit request."""
25
+ messages = [
26
+ {"role": "developer", "content": PLAN_EDIT_DOCUMENT_SYSTEM_PROMPT},
27
+ {
28
+ "role": "user",
29
+ "content": f"{docmd}\n\nEDIT REQUEST:\n{user_edit_request}",
30
+ },
31
+ ]
32
+
33
+ try:
34
+ completion = await asyncio.get_event_loop().run_in_executor(
35
+ None,
36
+ lambda: openai_client.beta.chat.completions.parse(
37
+ model="gpt-5-mini",
38
+ messages=messages, # type: ignore[arg-type]
39
+ response_format=Step,
40
+ reasoning_effort=reasoning_effort.value, # type: ignore[arg-type]
41
+ ),
42
+ )
43
+ step = completion.choices[0].message.parsed
44
+ except Exception as e:
45
+ progress.add(
46
+ f"Failed to generate step for edit request {query_index + 1} '{user_edit_request}': {e}"
47
+ )
48
+ raise ToolExecutionError(
49
+ message=(
50
+ f"Failed to generate step for edit request {query_index + 1} '{user_edit_request}'"
51
+ ),
52
+ developer_message="\n".join(progress.get_messages()),
53
+ )
54
+ else:
55
+ return step or Step(edit_items=[])
56
+
57
+
58
+ def _merge_steps(steps: list[Step]) -> Step:
59
+ """Merge multiple steps into a single step."""
60
+ merged_edit_items = []
61
+
62
+ for step in steps:
63
+ merged_edit_items.extend(step.edit_items)
64
+
65
+ return Step(edit_items=merged_edit_items)
66
+
67
+
68
+ def _build_ordered_plan(merged_step: Step) -> Plan:
69
+ """
70
+ Convert a merged step into an ordered plan with content edits first,
71
+ then all formatting/styling edits grouped into a single step at the end.
72
+ """
73
+ if not merged_step.edit_items:
74
+ return Plan(steps=[])
75
+
76
+ content_edits = []
77
+ formatting_edits = []
78
+
79
+ for i, edit_item in enumerate(merged_step.edit_items):
80
+ if edit_item.edit_request_type.is_style_or_formatting_edit():
81
+ formatting_edits.append((i, edit_item))
82
+ else:
83
+ content_edits.append((i, edit_item))
84
+
85
+ # Sort content edits by precedence, maintaining original order for same precedence
86
+ content_edits.sort(key=lambda x: (x[1].edit_request_type.get_precedence(), x[0]))
87
+
88
+ steps = []
89
+
90
+ for _, edit_item in content_edits:
91
+ step = Step(edit_items=[edit_item])
92
+ steps.append(step)
93
+
94
+ if formatting_edits:
95
+ formatting_items = [edit_item for _, edit_item in formatting_edits]
96
+ formatting_step = Step(edit_items=formatting_items)
97
+ steps.append(formatting_step)
98
+
99
+ return Plan(steps=steps)
100
+
101
+
102
+ async def plan_edits(
103
+ openai_client: OpenAI,
104
+ docmd: DocMD,
105
+ user_edit_requests: list[str],
106
+ reasoning_effort: ReasoningEffort,
107
+ progress: ExecutionProgressTracker,
108
+ ) -> Plan:
109
+ """
110
+ Plan edits to a Google Docs document before actually executing them.
111
+ Returns a plan with ordered steps for executing the edits.
112
+ """
113
+ # Generate a step for each user edit request in parallel
114
+ step_tasks = [
115
+ _generate_step_for_user_edit_request(
116
+ openai_client,
117
+ docmd,
118
+ user_edit_request,
119
+ i,
120
+ reasoning_effort,
121
+ progress,
122
+ )
123
+ for i, user_edit_request in enumerate(user_edit_requests)
124
+ ]
125
+ steps = await asyncio.gather(*step_tasks)
126
+
127
+ merged_step = _merge_steps(steps)
128
+ plan = _build_ordered_plan(merged_step)
129
+
130
+ return plan
@@ -0,0 +1,32 @@
1
+ class ExecutionProgressTracker:
2
+ """Helper class to track execution progress throughout the edit process."""
3
+
4
+ def __init__(self) -> None:
5
+ self.messages: list[str] = []
6
+
7
+ def add(self, message: str) -> None:
8
+ """Add a progress message."""
9
+ self.messages.append(message)
10
+
11
+ def add_step_start(self, step_num: int, total_steps: int) -> None:
12
+ """Add a step start message."""
13
+ self.add(f"Executing step {step_num + 1} of {total_steps}")
14
+
15
+ def add_step_success(self, step_num: int, total_steps: int) -> None:
16
+ """Add a step success message."""
17
+ self.add(f"Successfully performed the edits for step {step_num + 1} of {total_steps}")
18
+
19
+ def add_step_error(self, step_num: int, total_steps: int, error: Exception) -> None:
20
+ """Add a step error message."""
21
+ message = (
22
+ f"I encountered an error while executing step {step_num + 1} of {total_steps}: {error}"
23
+ )
24
+ self.add(message)
25
+
26
+ def add_step_retry(self, step_num: int, total_steps: int) -> None:
27
+ """Add a step retry message."""
28
+ self.add(f"I will retry step {step_num + 1} of {total_steps}")
29
+
30
+ def get_messages(self) -> list[str]:
31
+ """Get all progress messages."""
32
+ return self.messages.copy()
@@ -0,0 +1,204 @@
1
+ PLAN_EDIT_DOCUMENT_SYSTEM_PROMPT = """
2
+ Your purpose is to understand the provided document and THE SINGLE QUERY to create a minimal list of edits.
3
+
4
+ UNDERSTANDING STYLE NOTATION IN DOCMD:
5
+ Blocks may have a 'styles' attribute showing text formatting (e.g., styles=bold:0-11,italic:15-20).
6
+ The format is: style_name:start-end (positions relative to block start).
7
+ Common styles: bold, italic, underline, strikethrough, color=rgb(r,g,b), bgColor=rgb(r,g,b), fontSize=Xpt, font=Name.
8
+
9
+ CRITICAL RULES:
10
+ 1. ONLY address the ONE query provided - do not generate edits for anything else
11
+ 2. Check if the requested change already exists in the document before creating an edit
12
+ 3. Return an EMPTY list if the change already exists or is not needed
13
+ 4. Be MINIMAL - only create the exact edits required for the single query
14
+ 5. STYLE INHERITANCE WARNING: When using replaceAllText, the replacement text inherits the style of the text being replaced.
15
+ - If the original text is bold, the replacement will be bold
16
+ - To avoid unwanted style inheritance, prefer insertText at specific positions when adding new content
17
+ - Only use replaceAllText when you want to preserve existing styles or when replacing all instances
18
+ 6. Do NOT duplicate edits or create variations of the same edit
19
+
20
+ You should generate a flat list of edit requests needed to satisfy ONLY THE SINGLE QUERY provided.
21
+ The system will handle ordering and dependencies automatically.
22
+
23
+ Each edit request should be atomic and focused on a single change.
24
+ Break down complex operations into their individual components.
25
+
26
+ IMPORTANT: A 'query' is a natural language description of a change.
27
+ - A single query can result in MULTIPLE edit requests (e.g., "make the title bold and centered" needs 2 edits)
28
+ - A query can also result in ZERO edit requests if the requested change already exists in the document
29
+ - Only include edit requests that are actually needed based on the current document state
30
+ - NEVER generate edits that weren't explicitly requested in the query
31
+
32
+ IMPORTANT CONSTRAINTS:
33
+ - Location Constraints: Some operations can't be used in certain locations (e.g., insertText can't be used at table start indices, must use preceding paragraph)
34
+ - Segment Constraints: Many operations are restricted to body-only (segment ID must be empty) like insertSectionBreak and insertPageBreak
35
+ - Cascading Effects: deleteContentRange across paragraph boundaries will merge paragraphs, potentially affecting subsequent paragraph-level operations
36
+
37
+
38
+ FOR EACH EDIT REQUEST: Include specific thoughts that explain why this particular edit is needed and how it addresses the query.
39
+ These thoughts should be specific to each individual edit, not general thoughts about the overall plan.
40
+
41
+ - replaceAllText: Use when you need to find and replace all occurrences of specific text or regex patterns throughout the entire document (or specified tabs) with support for case-sensitive/insensitive matching. Do not use when you need to replace text at a specific location/range only, or when you need to selectively replace some instances while preserving others. Replacing more text than necessary will result in unintentional styling being applied to the replacement text.
42
+
43
+ - insertText: Use this to add new text at a specific index within an existing paragraph or append text to the end of a document/header/footer/footnote segment, with automatic style inheritance from neighboring text. Do not use this at table start indices (insert in the preceding paragraph instead) or for replacing existing text (use replaceAllText or replaceNamedRangeContent instead).
44
+
45
+ - updateTextStyle: Use to modify text formatting properties (bold, italic, underline, colors, fonts, links, etc.) for existing text within a specified range, including updating bullet formatting when the range contains list paragraphs. Do not use for paragraph-level formatting (alignment, spacing, borders) which requires updateParagraphStyle, or when inserting new text where insertText should be used instead.
46
+
47
+ - createParagraphBullets: Use this to apply bullet or numbered list formatting to existing paragraphs within a specified range, with nesting levels automatically determined by leading tabs. Do not use this if the paragraphs already have bullets (must delete first with deleteParagraphBullets) or if you need custom bullet styles beyond the available presets.
48
+
49
+ - deleteParagraphBullets: Use this to remove bullet points or numbering from paragraphs within a specified range while preserving their visual indentation/nesting level. Don't use this if you want to remove both bullets AND indentation, or if you want to change bullet styles rather than remove them entirely (use createParagraphBullets instead).
50
+
51
+ - updateParagraphStyle: Use to modify paragraph-level formatting properties (alignment, line spacing, indentation, borders, shading, page breaks) for all paragraphs overlapping a specified range; do not use for character-level text styling (bold, italic, fonts) which requires updateTextStyle, or for bullet/list creation which requires createParagraphBullets.
52
+
53
+ - deleteContentRange: Use when removing any range of text/content from the document body (including across paragraph boundaries which will merge paragraphs), but not for deleting specific structural elements like tables, headers, footers, or positioned objects which have their own dedicated delete operations.
54
+
55
+ - insertTable: Use when creating a new empty table with specified rows and columns at a document location; don't use when modifying existing tables or populating table content (use updateTable* or insertTableRow/Column requests instead).
56
+
57
+ - insertTableRow: Use when adding empty rows to an existing table at a specific position (above or below a reference cell); do not use for creating new tables (use insertTable) or when you need rows with pre-populated content (insert empty row first, then add content separately).
58
+
59
+ - insertTableColumn: Use when adding a new empty column to an existing table at a specific position (left or right of a reference cell); do not use when creating a new table initially (use insertTable instead) or when modifying properties of existing columns (use updateTableColumnProperties).
60
+
61
+ - deleteTableRow: Use when you need to remove an entire row from an existing table by providing a reference to any cell within that row (via tableCellLocation with tableStartLocation, rowIndex, and columnIndex); do not use when you want to delete only the content within cells while preserving the row structure, delete individual cells, or remove the entire table.
62
+
63
+ - deleteTableColumn: Use when you need to remove an entire column from an existing table by specifying any cell location within that column (via tableCellLocation with tableStartLocation, rowIndex, and columnIndex); do not use when you only want to clear cell contents without removing the column structure, need to delete rows instead of columns, or want to delete the entire table.
64
+
65
+ - insertPageBreak: Use to force content to start on a new page within the document body at a specific paragraph index or at the end of the body; do not use when the target location is inside a table, equation, footnote, header, or footer (segment ID must be empty/body only).
66
+
67
+ - updateTableColumnProperties: Use this to modify the width properties of existing table columns (setting fixed widths or evenly distributed widths), but not for adding/removing columns, changing cell content, or modifying cell styles/borders which require different request types.
68
+
69
+ - updateTableCellStyle: Use this to modify visual styling properties of table cells (backgroundColor, borders, padding, contentAlignment) for a specific range of cells or an entire table; do NOT use this for changing table structure (adding/removing rows/columns), modifying cell content, or updating table/row-level properties (use updateTableRowStyle or updateTableColumnProperties instead).
70
+
71
+ - updateTableRowStyle: Use when you need to modify row-level properties of an existing table (minimum height, header designation, or page-break prevention for specific rows or all rows); don't use when you need to modify cell content, cell styling, column properties, or create/delete rows.
72
+
73
+ - updateDocumentStyle: Use this to modify document-wide properties like page margins, page size, background color, page numbering, and header/footer settings that apply to the entire document or tab; don't use this for styling specific text, paragraphs, or individual sections within the document (use updateTextStyle, updateParagraphStyle, or updateSectionStyle instead).
74
+
75
+ - mergeTableCells: Use to combine multiple adjacent table cells into a single cell (with text concatenated into the upper-left/head cell), don't use when cells are already merged or when you need to unmerge existing merged cells (use unmergeTableCells instead).
76
+
77
+ - unmergeTableCells: Use this to separate previously merged table cells back into individual cells (with text preserved in the head cell), don't use this if the specified cells aren't already merged or if you want to keep the cells merged together.
78
+
79
+ - updateSectionStyle: Use to modify section-level formatting properties like margins, columns, page orientation, and header/footer behavior for specific document sections (body only); don't use for paragraph/text-level styling, document-wide styles, or content within headers/footers/footnotes.
80
+
81
+ - insertSectionBreak: Use when creating document sections with different formatting properties (headers/footers, margins, page orientation, or column layouts) by inserting CONTINUOUS or NEXT_PAGE section breaks in the document body; do NOT use inside tables, equations, footnotes, headers, or footers, or when a simple page break suffices (use insertPageBreak instead).
82
+
83
+ - pinTableHeaderRows: Use when you need table header rows to repeat at the top of each page in multi-page tables (setting pinnedHeaderRowsCount > 0) or to remove existing pinned headers (setting to 0); don't use for tables that fit on a single page or when you want different headers on different pages.
84
+
85
+ ---
86
+
87
+ All messages sent to you will be in the following format:
88
+ {a document in DocMD format}
89
+ EDIT REQUEST:
90
+ {a single natural language edit request that describes a desired change to the document}
91
+ """ # noqa: E501
92
+
93
+ DETERMINE_BLOCK_ID_SYSTEM_PROMPT = """
94
+ Your purpose is to understand the provided document and user edit request to
95
+ determine the block id that the edit request is targeting. The block id is a
96
+ unique identifier for a block in the document. A block id is a string that is
97
+ constructed from a block type and a counter. For example, the first paragraph
98
+ block would have the id "P1", the second paragraph block would have the id "P2",
99
+ and so on. In docMD, the block id is the first element in the block's metadata.
100
+
101
+ All messages sent you will be in the following format:
102
+ {a document in DocMD format}
103
+
104
+ EDIT REQUEST:
105
+ {a natural language edit request}
106
+ """
107
+
108
+ GENERATE_EDIT_REQUEST_SYSTEM_PROMPT = """
109
+ Your purpose is to understand the provided document and edit instructions to
110
+ create an EditRequest object that satisfies the edit instructions.
111
+ The EditRequest that you return will be used to
112
+ construct a batchUpdate request to the Google Docs API. The EditRequest object
113
+ that you are constructing is a part of a larger plan.
114
+
115
+ UNDERSTANDING STYLE NOTATION IN DOCMD:
116
+ Blocks may have a 'styles' attribute showing text formatting (e.g., styles=bold:0-11,italic:15-20).
117
+ The format is: style_name:start-end (positions relative to block start).
118
+ This helps you understand what text has special formatting.
119
+
120
+ CRITICAL STYLE INHERITANCE RULES:
121
+ - replaceAllText: Replacement text inherits the style of the matched text
122
+ - insertText: New text inherits style from neighboring text at the insertion point
123
+ - If you need to add text WITHOUT inheriting existing styles, insert at positions without styling
124
+ - If styles show bold:0-11 and you replace text at positions 0-11, the replacement will be bold
125
+
126
+ For completeness, you may have access to a stream of thoughts that were
127
+ generated when the plan was previously created.
128
+ You should treat these thoughts as supplemental information to aide in your
129
+ understanding of why you are being asked to construct the single and narrow
130
+ EditRequest.
131
+
132
+ All messages sent you will be in the following format:
133
+
134
+ DOCUMENT:
135
+ {a document in DocMD format}
136
+
137
+ THOUGHTS THAT OCCURRED WHEN CONSTRUCTING YOUR INSTRUCTIONS:
138
+ {a list of thoughts that were generated when the following instructions were created}
139
+
140
+
141
+ YOUR JOB AND SOLE PURPOSE IS TO CONSTRUCT A SINGLE EDIT REQUEST OBJECT THAT SATISFIES
142
+ THE FOLLOWING INSTRUCTIONS:
143
+ {edit instructions}
144
+ """
145
+
146
+ GENERATE_EDIT_REQUEST_SYSTEM_PROMPT_WITH_LOCATION_TAGS = """
147
+ Your purpose is to understand the provided document and edit instructions to
148
+ create an EditRequest object that satisfies the edit instructions.
149
+ The EditRequest that you return will be used to
150
+ construct a batchUpdate request to the Google Docs API. The EditRequest object
151
+ that you are constructing is a part of a larger plan. You will also be provided
152
+ with a location tag on each word in the block that that the edit might be targeting.
153
+ It is possible that the edit request is targeting a block that is not the one that
154
+ the location tags are on, but instead the block before or after.
155
+ The location tag is a string in the format <@start_index>word</@end_index>.
156
+
157
+ UNDERSTANDING STYLE NOTATION IN DOCMD:
158
+ Blocks may have a 'styles' attribute showing text formatting (e.g., styles=bold:0-11,italic:15-20).
159
+ The format is: style_name:start-end (positions relative to block start).
160
+ This helps you understand what text has special formatting.
161
+
162
+ CRITICAL STYLE INHERITANCE RULES:
163
+ - replaceAllText: Replacement text inherits the style of the matched text
164
+ - insertText: New text inherits style from neighboring text at the insertion point
165
+ - If you need to add text WITHOUT inheriting existing styles, insert at positions without styling
166
+ - If styles show bold:0-11 and you replace text at positions 0-11, the replacement will be bold
167
+
168
+ For completeness, you may have access to a stream of thoughts that were
169
+ generated when the plan was previously created.
170
+ You should treat these thoughts as supplemental information to aide in your
171
+ understanding of why you are being asked to construct the single and narrow
172
+ EditRequest.
173
+
174
+ All messages sent you will be in the following format:
175
+
176
+ DOCUMENT:
177
+ {a document in DocMD format}
178
+
179
+ THOUGHTS THAT OCCURRED WHEN CONSTRUCTING YOUR INSTRUCTIONS:
180
+ {a list of thoughts that were generated when the following instructions were created}
181
+
182
+
183
+ YOUR JOB AND SOLE PURPOSE IS TO CONSTRUCT A SINGLE EDIT REQUEST OBJECT THAT SATISFIES
184
+ THE FOLLOWING INSTRUCTIONS:
185
+ {edit instructions}
186
+ """
187
+
188
+ ERROR_FEEDBACK_PROMPT = """
189
+ The previous attempt to execute these requests failed with the following error:
190
+
191
+ Error details:
192
+ {error}
193
+
194
+ Previous generated requests that failed:
195
+ {failed_requests}
196
+
197
+ Please regenerate the request, taking into account the error above. The error may indicate:
198
+ - Invalid field values or formatting
199
+ - Incorrect indices or ranges
200
+ - Missing required fields
201
+ - Structural issues with the request
202
+
203
+ Adjust your response to fix the issue indicated by the error.
204
+ """