Remove JSON indentation from prompts to reduce token usage (#985)
Changes to `to_prompt_json()` helper to default to minified JSON (no indentation) instead of 2-space indentation. This reduces token consumption in LLM prompts while maintaining all necessary information. - Changed default `indent` parameter from `2` to `None` in `prompt_helpers.py` - Updated all prompt modules to remove explicit `indent=2` arguments - Minor code formatting fixes in LLM clients 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
24deb4d58d
commit
196eb2f077
12 changed files with 63 additions and 53 deletions
|
|
@ -349,7 +349,9 @@ class AnthropicClient(LLMClient):
|
||||||
# Common retry logic
|
# Common retry logic
|
||||||
retry_count += 1
|
retry_count += 1
|
||||||
messages.append(Message(role='user', content=error_context))
|
messages.append(Message(role='user', content=error_context))
|
||||||
logger.warning(f'Retrying after error (attempt {retry_count}/{max_retries}): {e}')
|
logger.warning(
|
||||||
|
f'Retrying after error (attempt {retry_count}/{max_retries}): {e}'
|
||||||
|
)
|
||||||
|
|
||||||
# If we somehow get here, raise the last error
|
# If we somehow get here, raise the last error
|
||||||
span.set_status('error', str(last_error))
|
span.set_status('error', str(last_error))
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,11 @@ class BaseOpenAIClient(LLMClient):
|
||||||
# These errors should not trigger retries
|
# These errors should not trigger retries
|
||||||
span.set_status('error', str(last_error))
|
span.set_status('error', str(last_error))
|
||||||
raise
|
raise
|
||||||
except (openai.APITimeoutError, openai.APIConnectionError, openai.InternalServerError):
|
except (
|
||||||
|
openai.APITimeoutError,
|
||||||
|
openai.APIConnectionError,
|
||||||
|
openai.InternalServerError,
|
||||||
|
):
|
||||||
# Let OpenAI's client handle these retries
|
# Let OpenAI's client handle these retries
|
||||||
span.set_status('error', str(last_error))
|
span.set_status('error', str(last_error))
|
||||||
raise
|
raise
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,11 @@ class OpenAIGenericClient(LLMClient):
|
||||||
# These errors should not trigger retries
|
# These errors should not trigger retries
|
||||||
span.set_status('error', str(last_error))
|
span.set_status('error', str(last_error))
|
||||||
raise
|
raise
|
||||||
except (openai.APITimeoutError, openai.APIConnectionError, openai.InternalServerError):
|
except (
|
||||||
|
openai.APITimeoutError,
|
||||||
|
openai.APIConnectionError,
|
||||||
|
openai.InternalServerError,
|
||||||
|
):
|
||||||
# Let OpenAI's client handle these retries
|
# Let OpenAI's client handle these retries
|
||||||
span.set_status('error', str(last_error))
|
span.set_status('error', str(last_error))
|
||||||
raise
|
raise
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,11 @@ def edge(context: dict[str, Any]) -> list[Message]:
|
||||||
Given the following context, determine whether the New Edge represents any of the edges in the list of Existing Edges.
|
Given the following context, determine whether the New Edge represents any of the edges in the list of Existing Edges.
|
||||||
|
|
||||||
<EXISTING EDGES>
|
<EXISTING EDGES>
|
||||||
{to_prompt_json(context['related_edges'], indent=2)}
|
{to_prompt_json(context['related_edges'])}
|
||||||
</EXISTING EDGES>
|
</EXISTING EDGES>
|
||||||
|
|
||||||
<NEW EDGE>
|
<NEW EDGE>
|
||||||
{to_prompt_json(context['extracted_edges'], indent=2)}
|
{to_prompt_json(context['extracted_edges'])}
|
||||||
</NEW EDGE>
|
</NEW EDGE>
|
||||||
|
|
||||||
Task:
|
Task:
|
||||||
|
|
@ -98,7 +98,7 @@ def edge_list(context: dict[str, Any]) -> list[Message]:
|
||||||
Given the following context, find all of the duplicates in a list of facts:
|
Given the following context, find all of the duplicates in a list of facts:
|
||||||
|
|
||||||
Facts:
|
Facts:
|
||||||
{to_prompt_json(context['edges'], indent=2)}
|
{to_prompt_json(context['edges'])}
|
||||||
|
|
||||||
Task:
|
Task:
|
||||||
If any facts in Facts is a duplicate of another fact, return a new fact with one of their uuid's.
|
If any facts in Facts is a duplicate of another fact, return a new fact with one of their uuid's.
|
||||||
|
|
|
||||||
|
|
@ -64,20 +64,20 @@ def node(context: dict[str, Any]) -> list[Message]:
|
||||||
role='user',
|
role='user',
|
||||||
content=f"""
|
content=f"""
|
||||||
<PREVIOUS MESSAGES>
|
<PREVIOUS MESSAGES>
|
||||||
{to_prompt_json([ep for ep in context['previous_episodes']], indent=2)}
|
{to_prompt_json([ep for ep in context['previous_episodes']])}
|
||||||
</PREVIOUS MESSAGES>
|
</PREVIOUS MESSAGES>
|
||||||
<CURRENT MESSAGE>
|
<CURRENT MESSAGE>
|
||||||
{context['episode_content']}
|
{context['episode_content']}
|
||||||
</CURRENT MESSAGE>
|
</CURRENT MESSAGE>
|
||||||
<NEW ENTITY>
|
<NEW ENTITY>
|
||||||
{to_prompt_json(context['extracted_node'], indent=2)}
|
{to_prompt_json(context['extracted_node'])}
|
||||||
</NEW ENTITY>
|
</NEW ENTITY>
|
||||||
<ENTITY TYPE DESCRIPTION>
|
<ENTITY TYPE DESCRIPTION>
|
||||||
{to_prompt_json(context['entity_type_description'], indent=2)}
|
{to_prompt_json(context['entity_type_description'])}
|
||||||
</ENTITY TYPE DESCRIPTION>
|
</ENTITY TYPE DESCRIPTION>
|
||||||
|
|
||||||
<EXISTING ENTITIES>
|
<EXISTING ENTITIES>
|
||||||
{to_prompt_json(context['existing_nodes'], indent=2)}
|
{to_prompt_json(context['existing_nodes'])}
|
||||||
</EXISTING ENTITIES>
|
</EXISTING ENTITIES>
|
||||||
|
|
||||||
Given the above EXISTING ENTITIES and their attributes, MESSAGE, and PREVIOUS MESSAGES; Determine if the NEW ENTITY extracted from the conversation
|
Given the above EXISTING ENTITIES and their attributes, MESSAGE, and PREVIOUS MESSAGES; Determine if the NEW ENTITY extracted from the conversation
|
||||||
|
|
@ -125,7 +125,7 @@ def nodes(context: dict[str, Any]) -> list[Message]:
|
||||||
role='user',
|
role='user',
|
||||||
content=f"""
|
content=f"""
|
||||||
<PREVIOUS MESSAGES>
|
<PREVIOUS MESSAGES>
|
||||||
{to_prompt_json([ep for ep in context['previous_episodes']], indent=2)}
|
{to_prompt_json([ep for ep in context['previous_episodes']])}
|
||||||
</PREVIOUS MESSAGES>
|
</PREVIOUS MESSAGES>
|
||||||
<CURRENT MESSAGE>
|
<CURRENT MESSAGE>
|
||||||
{context['episode_content']}
|
{context['episode_content']}
|
||||||
|
|
@ -142,11 +142,11 @@ def nodes(context: dict[str, Any]) -> list[Message]:
|
||||||
}}
|
}}
|
||||||
|
|
||||||
<ENTITIES>
|
<ENTITIES>
|
||||||
{to_prompt_json(context['extracted_nodes'], indent=2)}
|
{to_prompt_json(context['extracted_nodes'])}
|
||||||
</ENTITIES>
|
</ENTITIES>
|
||||||
|
|
||||||
<EXISTING ENTITIES>
|
<EXISTING ENTITIES>
|
||||||
{to_prompt_json(context['existing_nodes'], indent=2)}
|
{to_prompt_json(context['existing_nodes'])}
|
||||||
</EXISTING ENTITIES>
|
</EXISTING ENTITIES>
|
||||||
|
|
||||||
Each entry in EXISTING ENTITIES is an object with the following structure:
|
Each entry in EXISTING ENTITIES is an object with the following structure:
|
||||||
|
|
@ -197,7 +197,7 @@ def node_list(context: dict[str, Any]) -> list[Message]:
|
||||||
Given the following context, deduplicate a list of nodes:
|
Given the following context, deduplicate a list of nodes:
|
||||||
|
|
||||||
Nodes:
|
Nodes:
|
||||||
{to_prompt_json(context['nodes'], indent=2)}
|
{to_prompt_json(context['nodes'])}
|
||||||
|
|
||||||
Task:
|
Task:
|
||||||
1. Group nodes together such that all duplicate nodes are in the same list of uuids
|
1. Group nodes together such that all duplicate nodes are in the same list of uuids
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ def edge(context: dict[str, Any]) -> list[Message]:
|
||||||
</FACT TYPES>
|
</FACT TYPES>
|
||||||
|
|
||||||
<PREVIOUS_MESSAGES>
|
<PREVIOUS_MESSAGES>
|
||||||
{to_prompt_json([ep for ep in context['previous_episodes']], indent=2)}
|
{to_prompt_json([ep for ep in context['previous_episodes']])}
|
||||||
</PREVIOUS_MESSAGES>
|
</PREVIOUS_MESSAGES>
|
||||||
|
|
||||||
<CURRENT_MESSAGE>
|
<CURRENT_MESSAGE>
|
||||||
|
|
@ -88,7 +88,7 @@ def edge(context: dict[str, Any]) -> list[Message]:
|
||||||
</CURRENT_MESSAGE>
|
</CURRENT_MESSAGE>
|
||||||
|
|
||||||
<ENTITIES>
|
<ENTITIES>
|
||||||
{to_prompt_json(context['nodes'], indent=2)}
|
{to_prompt_json(context['nodes'])}
|
||||||
</ENTITIES>
|
</ENTITIES>
|
||||||
|
|
||||||
<REFERENCE_TIME>
|
<REFERENCE_TIME>
|
||||||
|
|
@ -141,7 +141,7 @@ def reflexion(context: dict[str, Any]) -> list[Message]:
|
||||||
|
|
||||||
user_prompt = f"""
|
user_prompt = f"""
|
||||||
<PREVIOUS MESSAGES>
|
<PREVIOUS MESSAGES>
|
||||||
{to_prompt_json([ep for ep in context['previous_episodes']], indent=2)}
|
{to_prompt_json([ep for ep in context['previous_episodes']])}
|
||||||
</PREVIOUS MESSAGES>
|
</PREVIOUS MESSAGES>
|
||||||
<CURRENT MESSAGE>
|
<CURRENT MESSAGE>
|
||||||
{context['episode_content']}
|
{context['episode_content']}
|
||||||
|
|
@ -175,7 +175,7 @@ def extract_attributes(context: dict[str, Any]) -> list[Message]:
|
||||||
content=f"""
|
content=f"""
|
||||||
|
|
||||||
<MESSAGE>
|
<MESSAGE>
|
||||||
{to_prompt_json(context['episode_content'], indent=2)}
|
{to_prompt_json(context['episode_content'])}
|
||||||
</MESSAGE>
|
</MESSAGE>
|
||||||
<REFERENCE TIME>
|
<REFERENCE TIME>
|
||||||
{context['reference_time']}
|
{context['reference_time']}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ def extract_message(context: dict[str, Any]) -> list[Message]:
|
||||||
</ENTITY TYPES>
|
</ENTITY TYPES>
|
||||||
|
|
||||||
<PREVIOUS MESSAGES>
|
<PREVIOUS MESSAGES>
|
||||||
{to_prompt_json([ep for ep in context['previous_episodes']], indent=2)}
|
{to_prompt_json([ep for ep in context['previous_episodes']])}
|
||||||
</PREVIOUS MESSAGES>
|
</PREVIOUS MESSAGES>
|
||||||
|
|
||||||
<CURRENT MESSAGE>
|
<CURRENT MESSAGE>
|
||||||
|
|
@ -201,7 +201,7 @@ def reflexion(context: dict[str, Any]) -> list[Message]:
|
||||||
|
|
||||||
user_prompt = f"""
|
user_prompt = f"""
|
||||||
<PREVIOUS MESSAGES>
|
<PREVIOUS MESSAGES>
|
||||||
{to_prompt_json([ep for ep in context['previous_episodes']], indent=2)}
|
{to_prompt_json([ep for ep in context['previous_episodes']])}
|
||||||
</PREVIOUS MESSAGES>
|
</PREVIOUS MESSAGES>
|
||||||
<CURRENT MESSAGE>
|
<CURRENT MESSAGE>
|
||||||
{context['episode_content']}
|
{context['episode_content']}
|
||||||
|
|
@ -225,7 +225,7 @@ def classify_nodes(context: dict[str, Any]) -> list[Message]:
|
||||||
|
|
||||||
user_prompt = f"""
|
user_prompt = f"""
|
||||||
<PREVIOUS MESSAGES>
|
<PREVIOUS MESSAGES>
|
||||||
{to_prompt_json([ep for ep in context['previous_episodes']], indent=2)}
|
{to_prompt_json([ep for ep in context['previous_episodes']])}
|
||||||
</PREVIOUS MESSAGES>
|
</PREVIOUS MESSAGES>
|
||||||
<CURRENT MESSAGE>
|
<CURRENT MESSAGE>
|
||||||
{context['episode_content']}
|
{context['episode_content']}
|
||||||
|
|
@ -269,8 +269,8 @@ def extract_attributes(context: dict[str, Any]) -> list[Message]:
|
||||||
2. Only use the provided MESSAGES and ENTITY to set attribute values.
|
2. Only use the provided MESSAGES and ENTITY to set attribute values.
|
||||||
|
|
||||||
<MESSAGES>
|
<MESSAGES>
|
||||||
{to_prompt_json(context['previous_episodes'], indent=2)}
|
{to_prompt_json(context['previous_episodes'])}
|
||||||
{to_prompt_json(context['episode_content'], indent=2)}
|
{to_prompt_json(context['episode_content'])}
|
||||||
</MESSAGES>
|
</MESSAGES>
|
||||||
|
|
||||||
<ENTITY>
|
<ENTITY>
|
||||||
|
|
@ -296,8 +296,8 @@ def extract_summary(context: dict[str, Any]) -> list[Message]:
|
||||||
{summary_instructions}
|
{summary_instructions}
|
||||||
|
|
||||||
<MESSAGES>
|
<MESSAGES>
|
||||||
{to_prompt_json(context['previous_episodes'], indent=2)}
|
{to_prompt_json(context['previous_episodes'])}
|
||||||
{to_prompt_json(context['episode_content'], indent=2)}
|
{to_prompt_json(context['episode_content'])}
|
||||||
</MESSAGES>
|
</MESSAGES>
|
||||||
|
|
||||||
<ENTITY>
|
<ENTITY>
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,14 @@ from typing import Any
|
||||||
DO_NOT_ESCAPE_UNICODE = '\nDo not escape unicode characters.\n'
|
DO_NOT_ESCAPE_UNICODE = '\nDo not escape unicode characters.\n'
|
||||||
|
|
||||||
|
|
||||||
def to_prompt_json(data: Any, ensure_ascii: bool = False, indent: int = 2) -> str:
|
def to_prompt_json(data: Any, ensure_ascii: bool = False, indent: int | None = None) -> str:
|
||||||
"""
|
"""
|
||||||
Serialize data to JSON for use in prompts.
|
Serialize data to JSON for use in prompts.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data: The data to serialize
|
data: The data to serialize
|
||||||
ensure_ascii: If True, escape non-ASCII characters. If False (default), preserve them.
|
ensure_ascii: If True, escape non-ASCII characters. If False (default), preserve them.
|
||||||
indent: Number of spaces for indentation
|
indent: Number of spaces for indentation. Defaults to None (minified).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
JSON string representation of the data
|
JSON string representation of the data
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ def summarize_pair(context: dict[str, Any]) -> list[Message]:
|
||||||
IMPORTANT: Keep the summary concise and to the point. SUMMARIES MUST BE LESS THAN 250 CHARACTERS.
|
IMPORTANT: Keep the summary concise and to the point. SUMMARIES MUST BE LESS THAN 250 CHARACTERS.
|
||||||
|
|
||||||
Summaries:
|
Summaries:
|
||||||
{to_prompt_json(context['node_summaries'], indent=2)}
|
{to_prompt_json(context['node_summaries'])}
|
||||||
""",
|
""",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
@ -85,8 +85,8 @@ def summarize_context(context: dict[str, Any]) -> list[Message]:
|
||||||
{summary_instructions}
|
{summary_instructions}
|
||||||
|
|
||||||
<MESSAGES>
|
<MESSAGES>
|
||||||
{to_prompt_json(context['previous_episodes'], indent=2)}
|
{to_prompt_json(context['previous_episodes'])}
|
||||||
{to_prompt_json(context['episode_content'], indent=2)}
|
{to_prompt_json(context['episode_content'])}
|
||||||
</MESSAGES>
|
</MESSAGES>
|
||||||
|
|
||||||
<ENTITY>
|
<ENTITY>
|
||||||
|
|
@ -98,7 +98,7 @@ def summarize_context(context: dict[str, Any]) -> list[Message]:
|
||||||
</ENTITY CONTEXT>
|
</ENTITY CONTEXT>
|
||||||
|
|
||||||
<ATTRIBUTES>
|
<ATTRIBUTES>
|
||||||
{to_prompt_json(context['attributes'], indent=2)}
|
{to_prompt_json(context['attributes'])}
|
||||||
</ATTRIBUTES>
|
</ATTRIBUTES>
|
||||||
""",
|
""",
|
||||||
),
|
),
|
||||||
|
|
@ -118,7 +118,7 @@ def summary_description(context: dict[str, Any]) -> list[Message]:
|
||||||
Summaries must be under 250 characters.
|
Summaries must be under 250 characters.
|
||||||
|
|
||||||
Summary:
|
Summary:
|
||||||
{to_prompt_json(context['summary'], indent=2)}
|
{to_prompt_json(context['summary'])}
|
||||||
""",
|
""",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -56,16 +56,16 @@ def search_results_to_context_string(search_results: SearchResults) -> str:
|
||||||
These are the most relevant facts and their valid and invalid dates. Facts are considered valid
|
These are the most relevant facts and their valid and invalid dates. Facts are considered valid
|
||||||
between their valid_at and invalid_at dates. Facts with an invalid_at date of "Present" are considered valid.
|
between their valid_at and invalid_at dates. Facts with an invalid_at date of "Present" are considered valid.
|
||||||
<FACTS>
|
<FACTS>
|
||||||
{to_prompt_json(fact_json, indent=12)}
|
{to_prompt_json(fact_json)}
|
||||||
</FACTS>
|
</FACTS>
|
||||||
<ENTITIES>
|
<ENTITIES>
|
||||||
{to_prompt_json(entity_json, indent=12)}
|
{to_prompt_json(entity_json)}
|
||||||
</ENTITIES>
|
</ENTITIES>
|
||||||
<EPISODES>
|
<EPISODES>
|
||||||
{to_prompt_json(episode_json, indent=12)}
|
{to_prompt_json(episode_json)}
|
||||||
</EPISODES>
|
</EPISODES>
|
||||||
<COMMUNITIES>
|
<COMMUNITIES>
|
||||||
{to_prompt_json(community_json, indent=12)}
|
{to_prompt_json(community_json)}
|
||||||
</COMMUNITIES>
|
</COMMUNITIES>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -783,7 +783,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphiti-core"
|
name = "graphiti-core"
|
||||||
version = "0.22.0rc3"
|
version = "0.22.0rc4"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "diskcache" },
|
{ name = "diskcache" },
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue